简体   繁体   中英

How can I add a WHERE clause for each row in a subquery?

I have the following tables:

CREATE TABLE `content` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `content` varchar(255) NOT NULL,
  `tag_a_id` int unsigned DEFAULT NULL,
  `tag_b_id` int unsigned DEFAULT NULL,
  `tag_c_id` int unsigned DEFAULT NULL,
  `tag_d_id` int unsigned DEFAULT NULL,
  `tag_e_id` int unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
);
CREATE TABLE `tags` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `tag` varchar(32) NOT NULL UNIQUE,
  PRIMARY KEY (`id`)
);

The tags table has a one-to-many relationship with the content table, using the tag_?_id fields, but each tag ID will only appear ONCE per row.

I would like to do a query in which I select all rows from the content table that are associated with a given set of tags (and all tags associated). For example, "get me all content rows that have the tags "News" and "MedicalCare" associated.

This means the IDs for "News" and "MedicalCare" need to be looked up in the tags table, and then injected into a query on the content table, using a pair of queries like this (assuming those tags have the IDs 45 and 68 ):

SELECT id FROM tags WHERE tag IN ("News","MedicalCare");

... and then...

SELECT t1.id, t1.content, ts_a.tag, ts_b.tag, ts_c.tag, ts_d.tag, ts_e.tag
FROM (
    SELECT t.id, t.content, t.tag_a_id, t.tag_b_id, t.tag_c_id, t.tag_d_id, t.tag_e_id
    FROM content t
    WHERE 45 IN (t.tag_a_id, t.tag_b_id, t.tag_c_id, t.tag_d_id, t.tag_e_id)
    AND 68 IN (t.tag_a_id, t.tag_b_id, t.tag_c_id, t.tag_d_id, t.tag_e_id)
    ORDER BY t.id ASC LIMIT 200
) t1
LEFT JOIN tags ts_a ON t1.tag_a_id=ts_a.id
LEFT JOIN tags ts_b ON t1.tag_b_id=ts_b.id
LEFT JOIN tags ts_c ON t1.tag_c_id=ts_c.id
LEFT JOIN tags ts_d ON t1.tag_d_id=ts_d.id
LEFT JOIN tags ts_e ON t1.tag_e_id=ts_e.id;

Is there a way I can fetch the tag IDs I'm interested in within this query, and dynamically generate those AND x IN (a,b,c) clauses?

Another option might be something like:

WHERE EVERY ONE OF (
    SELECT id FROM tags WHERE tag IN ("News","MedicalCare")
) IN (t.tag_a_id, t.tag_b_id, t.tag_c_id, t.tag_d_id, t.tag_e_id)

PLEASE NOTE: The content table is very large, so it is unfeasible to join the content table to the tags table without first filtering out unwanted rows and applying a LIMIT .

If you know the tags are unique, you can do something like:

where ((ts_a.tag in ('News', 'MecialCare')) +
       (ts_b.tag in ('News', 'MecialCare')) +
       (ts_c.tag in ('News', 'MecialCare')) +
       (ts_d.tag in ('News', 'MecialCare')) +
       (ts_e.tag in ('News', 'MecialCare'))
      ) = 2

This uses the fact that in MySQL a comparison returns 0 or 1, which can then be added together.

By the way, this question is a good reason why data should be properly structured, with a table that would a separate row for each tag for each person, a persno_tag table.

I think this will do it:

select t1.id, t1.content, ts_a.tag, ts_b.tag, ts_c.tag, ts_d.tag, ts_e.tag
from content t1
LEFT JOIN tags ts_a ON t1.tag_a_id=ts_a.id
LEFT JOIN tags ts_b ON t1.tag_b_id=ts_b.id
LEFT JOIN tags ts_c ON t1.tag_c_id=ts_c.id
LEFT JOIN tags ts_d ON t1.tag_d_id=ts_d.id
LEFT JOIN tags ts_e ON t1.tag_e_id=ts_e.id
where "News" in (ts_a.tag, ts_b.tag, ts_c.tag, ts_d.tag, ts_e.tag)
and "MedicalCare" in (ts_a.tag, ts_b.tag, ts_c.tag, ts_d.tag, ts_e.tag)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM