SQL 查询:跨联接表中的多个字段搜索
SQL Query: Search Across Multiple Fields in JOINed Tables
SQL 版本:MySQL 8.0 或 SQL 服务器
SQL Fiddle: https://www.db-fiddle.com/f/wcHeXkcynUiYP3qzryoYJ7/6
我有 table 个图像和 table 个标签,这些标签 link 到这些图像。
================================== ===================================================
| tb_images | | tb_imagetags |
================================== ===================================================
| f_imageID | f_imagefilename | | f_imagetagID | f_imagetagimage | f_imagetagname |
---------------------------------- ---------------------------------------------------
| 1 | 1.jpg | | 10 | 1 | November |
| 2 | 2.jpg | | 11 | 1 | 2021 |
| 3 | 3.jpg | | 12 | 2 | November |
================================== | 13 | 2 | 2020 |
| 14 | 3 | December |
| 15 | 3 | 2020 |
===================================================
我希望能够将 (2) 个标签传递给查询并使其 select 仅匹配两个标签的图像。例如,我想传递 November
和 2021
并只传递 return 1.jpg
.
如果我这样做:
SELECT f_imageID, f_imagefilename
FROM tb_images
LEFT JOIN tb_imagetags
ON f_imagetagimage = f_imageID
WHERE f_imagetagname = 'November'
OR f_imagetagname = '2021'
但是 returns:
f_imageID f_imagefilename
================================
1 1.jpg
1 1.jpg
2 2.jpg
如何重写此查询以仅获取与两个标签都匹配的图像?
一种方法使用聚合:
SELECT i.f_imageID, i.f_imagefilename
FROM tb_images i
INNER JOIN tb_imagetags it
ON it.f_imagetagimage = i.f_imageID
GROUP BY i.f_imageID, i.f_imagefilename
HAVING SUM(f_imagetagname = 'November') > 0 AND
SUM(f_imagetagname = '2021') > 0;
想法是按图像聚合,然后断言 November
和 2021
都显示为标记值,跨越每个图像组中的一些记录。
这是你的updated DB Fiddle。
您可以为此使用 EXISTS
SELECT DISTINCT f_imageID, f_imagefilename
FROM tb_images
LEFT JOIN tb_imagetags fi2
ON f_imagetagimage = f_imageID
WHERE f_imagetagname = 'November'
AND EXISTS(SELECT 1 FROM tb_imagetags Fi WHERE f_imagetagname = '2021' AND fi.f_imageID = fi2.f_imageID)
问题是您的数据跨不同的行。如果所有的数据都在同一行,那就容易了
SELECT * FROM blah WHERE month = nov and year = 2021
当它在不同的行中时,您希望像现在一样获得两行..
..但是你只想要那些有两行的图像。如果只有一行(例如只有 11 月或只有 2021 年),则您不希望这样
有多种方法可以做到这一点。一种是将标签 table 连接到自身,将其中一侧过滤到几个月,另一侧过滤到几年
tb_imagetags tmonth
JOIN tb_imagetags tyear
ON
tmonth.f_imagetagname = 'November' AND
tyear.f_imagetagname = '2021' AND
tmonth.f_imagetagimage = tyear.f_imagetagimage
这会隐式地将 11 月和 2021 年“放在同一行”,因此只有同时具有这两个标签的图像才会出现在连接结果中..
..但我们做这种“跨行”查询的通常方法可能是在分组后检查计数,或者检查最小值是x,最大值是y,例如:
SELECT f_imageID, f_imagefilename
FROM tb_images
INNER JOIN tb_imagetags
ON f_imagetagimage = f_imageID
WHERE f_imagetagname = 'November'
OR f_imagetagname = '2021'
GROUP BY f_imageID
HAVING COUNT(*) = 2
或
HAVING MIN(f_imagetagname) = '2021' AND MAX( f_imagetagname) = 'November'
如果标签名称不同,则计数会起作用。如果你可以不小心将 11 月加倍,那么它也会把它们捡起来。 min max 只适用于两个标签..你也可以使用类似
的东西
HAVING SUM(CASE f_imagetagname WHEN 'November' THEN 1 WHEN '2021' THEN 2 END) = 3
这对任何数量的标准都有好处,你只需乘以 2 的幂,所以对于 3 个标签,当 1、2、4 要求总和为 7 时,你也可以使用任何的幂,像基数 10.. 上升到 1,10,100 并要求总和为 111..
也可以多次询问是否存在相关行:
SELECT f_imageID, f_imagefilename
FROM tb_images
WHERE
EXISTS(SELECT null FROM tb_imagetags WHERE f_imagetagimage = f_imageID AND f_imagetagname = 'November')
AND
EXISTS(SELECT null FROM tb_imagetags WHERE f_imagetagimage = f_imageID AND f_imagetagname = '2021')
EXISTS returns 如果有符合条件的行则为真:他的 sql 表示“图像中有一些标记行是 November 并且有一些(其他)标记行是 2021 年
无论你做什么,你都需要想办法将数据分组到它存在的 N 行中,然后做一些事情,这意味着这些行作为 组 相遇标准。这是一个技巧,因为我们通常不会按照人类的那些固定术语来思考,我们倾向于更多地“逐行”思考
SQL 版本:MySQL 8.0 或 SQL 服务器
SQL Fiddle: https://www.db-fiddle.com/f/wcHeXkcynUiYP3qzryoYJ7/6
我有 table 个图像和 table 个标签,这些标签 link 到这些图像。
================================== ===================================================
| tb_images | | tb_imagetags |
================================== ===================================================
| f_imageID | f_imagefilename | | f_imagetagID | f_imagetagimage | f_imagetagname |
---------------------------------- ---------------------------------------------------
| 1 | 1.jpg | | 10 | 1 | November |
| 2 | 2.jpg | | 11 | 1 | 2021 |
| 3 | 3.jpg | | 12 | 2 | November |
================================== | 13 | 2 | 2020 |
| 14 | 3 | December |
| 15 | 3 | 2020 |
===================================================
我希望能够将 (2) 个标签传递给查询并使其 select 仅匹配两个标签的图像。例如,我想传递 November
和 2021
并只传递 return 1.jpg
.
如果我这样做:
SELECT f_imageID, f_imagefilename
FROM tb_images
LEFT JOIN tb_imagetags
ON f_imagetagimage = f_imageID
WHERE f_imagetagname = 'November'
OR f_imagetagname = '2021'
但是 returns:
f_imageID f_imagefilename
================================
1 1.jpg
1 1.jpg
2 2.jpg
如何重写此查询以仅获取与两个标签都匹配的图像?
一种方法使用聚合:
SELECT i.f_imageID, i.f_imagefilename
FROM tb_images i
INNER JOIN tb_imagetags it
ON it.f_imagetagimage = i.f_imageID
GROUP BY i.f_imageID, i.f_imagefilename
HAVING SUM(f_imagetagname = 'November') > 0 AND
SUM(f_imagetagname = '2021') > 0;
想法是按图像聚合,然后断言 November
和 2021
都显示为标记值,跨越每个图像组中的一些记录。
这是你的updated DB Fiddle。
您可以为此使用 EXISTS
SELECT DISTINCT f_imageID, f_imagefilename
FROM tb_images
LEFT JOIN tb_imagetags fi2
ON f_imagetagimage = f_imageID
WHERE f_imagetagname = 'November'
AND EXISTS(SELECT 1 FROM tb_imagetags Fi WHERE f_imagetagname = '2021' AND fi.f_imageID = fi2.f_imageID)
问题是您的数据跨不同的行。如果所有的数据都在同一行,那就容易了
SELECT * FROM blah WHERE month = nov and year = 2021
当它在不同的行中时,您希望像现在一样获得两行..
..但是你只想要那些有两行的图像。如果只有一行(例如只有 11 月或只有 2021 年),则您不希望这样
有多种方法可以做到这一点。一种是将标签 table 连接到自身,将其中一侧过滤到几个月,另一侧过滤到几年
tb_imagetags tmonth
JOIN tb_imagetags tyear
ON
tmonth.f_imagetagname = 'November' AND
tyear.f_imagetagname = '2021' AND
tmonth.f_imagetagimage = tyear.f_imagetagimage
这会隐式地将 11 月和 2021 年“放在同一行”,因此只有同时具有这两个标签的图像才会出现在连接结果中..
..但我们做这种“跨行”查询的通常方法可能是在分组后检查计数,或者检查最小值是x,最大值是y,例如:
SELECT f_imageID, f_imagefilename
FROM tb_images
INNER JOIN tb_imagetags
ON f_imagetagimage = f_imageID
WHERE f_imagetagname = 'November'
OR f_imagetagname = '2021'
GROUP BY f_imageID
HAVING COUNT(*) = 2
或
HAVING MIN(f_imagetagname) = '2021' AND MAX( f_imagetagname) = 'November'
如果标签名称不同,则计数会起作用。如果你可以不小心将 11 月加倍,那么它也会把它们捡起来。 min max 只适用于两个标签..你也可以使用类似
的东西HAVING SUM(CASE f_imagetagname WHEN 'November' THEN 1 WHEN '2021' THEN 2 END) = 3
这对任何数量的标准都有好处,你只需乘以 2 的幂,所以对于 3 个标签,当 1、2、4 要求总和为 7 时,你也可以使用任何的幂,像基数 10.. 上升到 1,10,100 并要求总和为 111..
也可以多次询问是否存在相关行:
SELECT f_imageID, f_imagefilename
FROM tb_images
WHERE
EXISTS(SELECT null FROM tb_imagetags WHERE f_imagetagimage = f_imageID AND f_imagetagname = 'November')
AND
EXISTS(SELECT null FROM tb_imagetags WHERE f_imagetagimage = f_imageID AND f_imagetagname = '2021')
EXISTS returns 如果有符合条件的行则为真:他的 sql 表示“图像中有一些标记行是 November 并且有一些(其他)标记行是 2021 年
无论你做什么,你都需要想办法将数据分组到它存在的 N 行中,然后做一些事情,这意味着这些行作为 组 相遇标准。这是一个技巧,因为我们通常不会按照人类的那些固定术语来思考,我们倾向于更多地“逐行”思考