查询 M:N 包含

Posted

技术标签:

【中文标题】查询 M:N 包含【英文标题】:Query M:N contains 【发布时间】:2018-03-07 00:58:38 【问题描述】:

我正在尝试过滤一组表,其中包括 android Room (SQLite) 中的 M:N 联结表。

一张图片可以有很多主题。我想允许按主题过滤,以便获得包含完整图像信息(包括所有主题)的行。因此,如果一张图片有(国家公园,优胜美地)对其中任何一个进行过滤,则会导致一行包含两个关键字。除非我搞砸了,否则典型的连接会产生多行,这样匹配的Yosemite 会得到正确的图像,但你会缺少National Park。我想出了这个:

SELECT *,  
  (SELECT GROUP_CONCAT(name) 
    FROM meta_subject_junction
    JOIN subject 
      ON subject.id = meta_subject_junction.subjectId
      WHERE meta_subject_junction.metaId = meta.id) AS keywords, 
  (SELECT documentUri
    FROM image_parent
    WHERE meta.parentId = image_parent.id ) AS parentUri
FROM meta

现在这让我得到了完整的行,但我认为此时我需要:

WHERE keywords LIKE(%YOSEMITE%)

我认为LIKE 不太理想,更不用说不精确的匹配了。有没有更好的方法来实现这一点?谢谢,这让我的新手 SQL 大脑受不了了。

更多详情

meta
+----+----------+--+
| id |   name   |  |
+----+----------+--+
|  1 | yosemite |  |
|  2 | bryce    |  |
|  3 | flowers  |  |
+----+----------+--+
subject
+----+---------------+--+
| id |     name      |  |
+----+---------------+--+
|  1 | National Park |  |
|  2 | Yosemite      |  |
|  3 | Tulip         |  |
+----+---------------+--+
junction
+--------+-----------+
| metaId | subjectId |
+--------+-----------+
|      1 |         1 |
|      1 |         2 |
|      2 |         1 |
|      3 |         3 |
+--------+-----------+

虽然我可能做错了什么,但据我所知 Android Room 不喜欢:

+----+-----------+---------------+
| id |   name    |    subject    |
+----+-----------+---------------+
|  1 | yosemite  | National Park |
|  1 | yosemite  | Yosemite      |
+----+-----------+---------------+

所以我正在尝试减少行数:

+----+-----------+-------------------------+
| id |   name    |         subject         |
+----+-----------+-------------------------+
|  1 | yosemite  | National Park, Yosemite |
+----+-----------+-------------------------+

上面的查询所做的。但是,我也想查询一个主题。这样National Park 过滤器就会产生:

+----+-----------+-------------------------+
| id |   name    |         subject         |
+----+-----------+-------------------------+
|  1 | yosemite  | National Park, Yosemite |
|  2 | bryce     | National Park           |
+----+-----------+-------------------------+

我想比 LIKE 更精确/更高效,因为已经是“concat”主题。我的大多数尝试都以在 Room(多行)中没有结果或将主题减少到仅过滤器关键字而告终。

更新

这是我一直在使用的一个测试,用于将查询的实际 SQL 结果与 Android Room 的最终结果进行比较:

http://sqlfiddle.com/#!7/0ac11/10/0

该连接查询被解释为 Android Room 中的四个对象,因此我试图减少行数,但在过滤包含主题关键字的任何图像时保留完整的主题结果。

【问题讨论】:

请提供一些示例数据和您想要的输出。 (有关如何添加一些内容,请参阅How to format SQL tables in a Stack Overflow post?。) 【参考方案1】:

如果要多个关键字,则可以使用wheregroup byhaving

select image_id
from image_subject
where subject_id in ('a', 'b', 'c')  -- whatever
group by image-id
having count(distinct subject_id) = 3;  -- same count as in `where`

【讨论】:

对不起,我不清楚。我认为这是在寻找匹配的多个(全部或没有)主题。如果我误读了那对不起。我正在匹配具有我要过滤的任何主题的任何图像(不需要拥有所有主题)。我遇到的问题(我认为)是 Android Room (ORM) 似乎需要由单行表示的对象,所以我需要匹配 Yosemite 以产生一行与“国家公园、优胜美地等."。或者也许 group by 正在那里做那个魔术并且某种具有 > 1 的东西会起作用? 所以稍微玩一下那个查询,你是否暗示我会使用那个查询在第二个查询中创建一个where meta.id IN ([your query result]),因为这给了我一个 id 结果集?换句话说,这将是一个两个交易过程吗?【参考方案2】:

这得到了我需要的结果,但如果效率特别低,我很想听到更好的选择。

SELECT meta.*, 
  (SELECT GROUP_CONCAT(name)
    FROM junction
    JOIN subject 
      ON subject.id = junction.subjectId
      WHERE junction.metaId = meta.id) AS keywords,
  junction.subjectId
FROM meta
LEFT JOIN junction ON junction.metaId = meta.id
WHERE subjectId IN (1,2)
GROUP BY meta.id

+----+----------+------------------------+-----------+
| id |   name   |        keywords        | subjectId |
+----+----------+------------------------+-----------+
|  1 | yosemite | National Park,Yosemite |         2 |
|  2 | bryce    | National Park          |         1 |
+----+----------+------------------------+-----------+

http://sqlfiddle.com/#!7/86a76/13

【讨论】:

以上是关于查询 M:N 包含的主要内容,如果未能解决你的问题,请参考以下文章

mysql-多表联查(实例)

从有序矩阵M x N中找出是否包含某一个数,要求时间复杂度为O(M+N)

多表查询

python使用matplotlib可视化subplots绘制子图自定义几行几列子图,如果M行N列,那么最终包含M*N个子图在指定的子图中添加可视化结果

数列分段

考虑页面置换算法,系统有m个物理块供调度,初始时全空,页面引用串长度为p,包含了n个不同的页号,无论用什么算法,缺页次数不会少于( )