简体   繁体   English

SQL 查询:在 JOINed 表中搜索多个字段

[英]SQL Query: Search Across Multiple Fields in JOINed Tables

SQL Version : MySQL 8.0 or SQL Server SQL 版本:MySQL 8.0 或 SQL 服务器

SQL Fiddle : https://www.db-fiddle.com/f/wcHeXkcynUiYP3qzryoYJ7/6 SQL 小提琴https://www.db-fiddle.com/f/wcHeXkcynUiYP3qzryoYJ7/6

I have a table of images and a table of tags that link to those images.我有一张图片表和一张链接到这些图片的标签表。

==================================  ===================================================
| 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           |
                                    ===================================================

I want to be able to pass (2) tags to the query and have it select only the images that match BOTH tags.我希望能够将(2)标签传递给查询,并让它 select 只匹配两个标签的图像。 For example, I want to pass November and 2021 and have it return only 1.jpg .例如,我想通过November2021并让它只返回1.jpg

If I do something like this:如果我做这样的事情:

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'

But that returns:但这会返回:

f_imageID   f_imagefilename
================================
1           1.jpg
1           1.jpg
2           2.jpg

How can I rewrite this query to only get images that match both tags?如何重写此查询以仅获取与两个标签匹配的图像?

One method uses aggregation:一种方法使用聚合:

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;

The idea is to aggregate by image and then assert that both November and 2021 appear as tag values, across some records within each image group.这个想法是按图像聚合,然后断言November2021都作为标签值出现在每个图像组内的一些记录中。

Here is your updated DB Fiddle .这是您更新的 DB Fiddle

You can us EXISTS for that您可以为此而存在

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)

The problem is your data is related across different rows .问题是您的数据跨不同行相关。 If all the data was in the same row then it'd be easy如果所有数据都在同一行中,那就很容易了

SELECT * FROM blah WHERE month = nov and year = 2021

When it's in different rows you want to get both rows like you're doing..当它在不同的行中时,您希望像您正在做的那样获得两行..

..but then you only want those images for which there are two rows. ..但是你只想要那些有两行的图像。 If there is only one row (eg only Nov or only 2021) you don't want that如果只有一行(例如只有 11 月或只有 2021 年),您不希望这样

There are various ways to do it.有多种方法可以做到这一点。 One is to join the tags table to itself, having filtered one of the sides to just months and the other to just years一种是将标签表连接到自身,将一侧过滤到几个月,另一侧过滤到几年

tb_imagetags tmonth 
JOIN tb_imagetags tyear 
ON 
  tmonth.f_imagetagname = 'November' AND
  tyear.f_imagetagname = '2021' AND
  tmonth.f_imagetagimage = tyear.f_imagetagimage

This would implicitly put month nov and year 2021 "on the same row" so only images with both those tags would appear in the join result..这将隐式地将 11 月和 2021 年“放在同一行”,因此只有带有这两个标签的图像才会出现在连接结果中。

..but probably the usual way we do such "across row" queries is to check the count after grouping them, or check that the min is x and the max is y, for example: ..但我们进行此类“跨行”查询的常用方法可能是在对它们进行分组后检查计数,或者检查最小值是否为 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

Or或者

HAVING MIN(f_imagetagname) = '2021' AND MAX( f_imagetagname) = 'November'

The count thing works if the tag names are distinct.如果标签名称不同,则计数内容有效。 If you can double up November by accident then it'll pick those up too.如果你能意外地把 11 月加倍,那么它也会把它们加倍。 The min max only works for two tags.. you can also use something like最小最大值仅适用于两个标签..您也可以使用类似的东西

HAVING SUM(CASE f_imagetagname WHEN 'November' THEN 1 WHEN '2021' THEN 2 END) = 3

And that's good for any number of criteria, you just go up in powers of two, so for 3 tags you case when 1,2,4 and demand the sum be 7, you could also go in powers of anything, like base 10.. go up in 1,10,100 and demand the sum be 111..这对任何数量的标准都有好处,您只需 go 以 2 的幂次方,因此对于 3 个标签,当 1、2、4 并要求总和为 7 时,您也可以 go 以任何方式的幂,例如基数 10。 . go 增加 1,10,100 并要求总和为 111..

You can also ask that there exists a related row multiple times:您还可以多次询问是否存在相关行:

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 true if there is a row that meets the criteria: his sql means "images where there is some tag row that is November and there is some(other) tag row that is 2021"如果存在符合条件的行,则 EXISTS 返回 true:他的 sql 的意思是“有一些标签行是 11 月并且有一些(其他)标签行是 2021 年的图像”


Whatever you do, you need to think of a way to group the data up across the N rows where it exists and then do something that means the rows as a group meet the criteria.无论您做什么,您都需要想办法将数据分组到它存在的 N 行中,然后执行一些操作,以使这些行作为一个符合标准。 That's a trick because we don't usually think in those set terms as humans, we tend to think more "row by row"这是一个技巧,因为我们通常不会以人类的方式思考这些设定的术语,我们倾向于更多地“逐行”思考

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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