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!

Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top