SQL:过滤一对多查询

时间:2017-11-04 22:07:31

标签: sql sqlite

我的数据库中存在多对多关系(但我的问题也可能是一对多,因此标题),它将文件与标签链接起来。用户可以使用标签作为过滤器来搜索文件。

例如:

+ (a AND b) - c

表示我想要所有标记有标记a和标记b的文件,而不是标记为c的文件。

在伪代码中,这将是:

getFiles(filter) {
  let matching_files = [];
  for file in files {
    let tags = file.tags;
    if filter.match(tags) { // something non-trivial
       matching_files.push(file);
    }
  }
  matching_files
}

我刚刚意识到我想要做的是基于搜索而不是基于一些行,而是基于一组行,这不是SQL的意思。

我已经知道如何实现这一点,这将通过生成基于搜索查询的动态交叉/除外。所以先前的查询将是:

(SELECT file_id from files, tags WHERE tags.id = a) INTERSECT 
(SELECT file_id from files, tags WHERE tags.id = b) /* <- this handles + (a AND b) */ EXCEPT 
(SELECT file_id from files, tags WHERE tags.id = c) -- this one handles the - c

所以+群组是一堆联盟,AND个关键字会生成INTERSECT s,OR可能只是OR WHERE 1}}组中的子句,-对于之前已过滤的所有内容都是EXCEPT

所以我的问题是,有没有更简单的方法来做所有这些?因为首先这看起来非常h​​acky,然后当文件数量增长时,它看起来并不高效 ,因为我将查询每个组的所有文件。解决方案不一定是SQL(如果有其他类型的数据存储可以更好地处理这个问题),但请注意我正在为桌面应用程序执行此操作(所以使用我可以嵌入我的程序会很好)。谢谢!

2 个答案:

答案 0 :(得分:0)

如果您想使用intersect,可以执行以下操作:

select file_id
from file_tags
where file_tags.tag_id = a
intersect
select file_id
from file_tags
where file_tags.tag_id = b
intersect
select file_id
from file_tags
where file_tags.tag_id = c;

我通常会使用group byhaving执行此操作:

select file_id
from file_tags
where tag_id in (a, b, c)
having count(*) = 3;

答案 1 :(得分:0)

首先,您在JOIN等查询中错过tags.file_id = files.file_id条件。您可以使用EXISTSNOT EXISTS执行相同的工作。

SELECT file_id 
FROM files 
WHERE EXISTS (
   SELECT 1 FROM tags 
   WHERE tags.file_id = files.file_id and tags.id = a 
) and EXISTS (
   SELECT 1 FROM tags 
   WHERE tags.file_id = files.file_id and tags.id = b 
) and NOT EXISTS (
   SELECT 1 FROM tags 
   WHERE tags.file_id = files.file_id and tags.id = a 
)

如果你有一个类似+(a OR b)的过滤器 - c则可以使用以下sql表示

SELECT file_id 
FROM files 
WHERE EXISTS (
   SELECT 1 FROM tags 
   WHERE tags.file_id = files.file_id and tags.id IN (a, b) 
) and NOT EXISTS (
   SELECT 1 FROM tags 
   WHERE tags.file_id = files.file_id and tags.id = a 
)

效率主要与适当的指标有关。如果你创建了诸如tags(id, file_id)之类的索引,那么它应该没问题(因为你可能已经在file(file_id)上有了一个)。