How to assign virtual auto-increment rank with limit of 4?
-
27-10-2019 - |
Question
Simply put, I would like rid to order my pid.
Instead of creating an extra table called 'rank' with 4 rows I would like to be able to just attach a virtual rank that gives the same effect. My rank is just there to give a position to my tags. Since I have 4 tags per id I would have a value of 1-4 enumerating those tags.
This way I won't have to manually put in the same numbers over and over again.
My tables:
people tags_rel tags rank id | etc id | pid | tid | rid id | tag id -----+----- ---+------+-----+---- ---+-------- -- 2345 | 1 | 2345 | 2 | 1 1 | bread 1 2346 | 2 | 2345 | 3 | 2 2 | water 2 2347 | 3 | 2345 | 1 | 3 3 | bear 3 4 | 2345 | 6 | 4 4 | milk 4 ---+------+-----+---- 5 | hotdogs 5 | 2346 | 3 | 1 6 | 2346 | 4 | 2 7 | 2346 | 2 | 3 8 | 2346 | 5 | 4 ---+------+-----+---- 9 | 2347 | 6 | 1 10 | 2347 | 1 | 2 11 | 2347 | 4 | 3 12 | 2347 | 5 | 4 ---+------+-----+----
My query:
SELECT p.id as pid, t.tag as tname, tr.tid as trid, r.id as rank
FROM people AS p
RIGHT JOIN tags_rel AS tr ON tr.pid = p.id
LEFT JOIN tags AS t ON tr.tid = t.id
LEFT JOIN rank AS r ON tr.rid = r.id
MySQL expected results:
> +--------+------------+--------+------+ > | pid | tname | trid | rank | > +--------+------------+--------+------+ > | 2345 | water | 2 | 1 | > | 2345 | bread | 1 | 2 | > | 2345 | cereal | 3 | 3 | > | 2345 | milk | 4 | 4 | > | 2346 | cereal | 3 | 1 | > | 2346 | milk | 4 | 2 | > | 2346 | water | 2 | 3 | > | 2346 | hotdogs | 5 | 4 | > | 2347 | chocolate | 6 | 1 | > | 2347 | bread | 1 | 2 | > | 2347 | bread | 4 | 3 | > | 2347 | bread | 5 | 4 | > +--------+------------+--------+------+
I am using rank as position. So whatever tags are attached to pid I would like to show by first position. Since the tags are different for each pid they would have different positions. I may want to call tname=water by rank=1 and it would display all the 'water' tags in position 1 only.
Thanks!
Solution
If I understand it correctly, you want to remove the rid
column from your tag_rel
table, and have an automatically genarated position field in your query instead. The "position" of a tag in your case can be understood as the number of entries in the tag_rel
table that have been added for a specific pid
. In other words: all entries in the table with the same pid
and an id
that is equal or lower, assuming id
is an autoincrement field.
I think this can be done as follows. Mind you, it may not be the most efficient code in the world:
SELECT id, pid, tid, (
SELECT count(*) from tags_rel t2 where t2.pid = t1.pid and t2.id <= t1.id
) AS rid
FROM tags_rel t1;
I'm not entirely sure this will work, as I only have mysql 3 here, which doesn't do subqueries like this. I think it will though.
If you may have more than 4 tags linked to a person, but you want only the first 4 tags for each person, you can now use:
SELECT * FROM (
[the above query]
) WHERE rid <= 4;
This having been said, I think in almost all situations you might as well just order by pid, id
and then adding the rank numbers in your output medium. But then, I don't know your situation.
I think the most important thing is: is this what you meant? If so, hopefully some people on here will be able to provide you with the best answer to your question.
EDIT:
For your current query, this would amount to:
SELECT p.id as pid, t.tag as tname, tr.tid as trid, (
SELECT count(*)
FROM tags_rel AS tr2
WHERE tr2.pid = tr.pid and tr2.id <= tr.id
) AS rank
FROM tags_rel AS tr
LEFT JOIN people AS p ON tr.pid = p.id
LEFT JOIN tags AS t ON tr.tid = t.id
assuming you don't need the where rid <= 4
part. (By the way: the tr.tid as trid
kind of suggests you want to select tags_rel.id
instead of the tags_rel.tid
(== tags.id
). I'm not sure if that's what you mean; if not, it might be better to select tr.tid as tid
(or just tr.tid
).)
OTHER TIPS
make use of order by and limit
order by pid,rank
limit 4;
Here is the query you want.
SELECT p.id AS pid, t.tag AS tname,tr.tid AS trid,tr.rid AS rank
FROM
people AS p JOIN tags_rel AS tr ON tr.pid = p.id
LEFT JOIN tags AS t ON tr.tid = t.id
order by pid,rank;
If you just want the 'water' tags in position 1, you would add "WHERE tname = 'water' AND rank = '1'" before "ORDER BY".
Note that there is actually no need for your rank table. If you want the 'rid' field to be restricted to only 4 values, just make it an enum datatype.