检查是不是存在一组相关行

Posted

技术标签:

【中文标题】检查是不是存在一组相关行【英文标题】:Check if a set of related rows exist检查是否存在一组相关行 【发布时间】:2017-11-23 19:58:13 【问题描述】:

我想管理数据库中的一组事物。假设有以下两张表:

CREATE TABLE Sets (id BIGINT PRIMARY KEY, name VARCHAR(64));
CREATE TABLE SetItems (fkSet BIGINT, item BIGINT, FOREIGN KEY (fkSet) REFERENCES Sets(id));

我可以通过在表Sets 中插入一行来创建集合,然后在SetItems 中添加一行或多行以及对应的fkSet

获取特定套装的物品很容易,基本上是SELECT * FROM SetItems WHERE fkSet = :id

问题:现在我想找出一个集合是否存在,给定一组 SetItems。

示例:我想查找是否存在包含项目25 的集合。

我尝试了什么:

(1) 我可以尝试以下方法:

SELECT s.fkSet FROM Sets s, SetItems i1, SetItems i2
              WHERE s.id = i1.fkSet AND i1.item = 2
                AND s.id = i2.fkSet AND i2.item = 5;

但是这种方法有几个缺点:

如果我需要检查更多的 SetItem,我猜它的扩展性很差。 我需要将 sql-query 与我不喜欢的字符串连接放在一起,从而增加了注入攻击的机会。 它还可以找到除了 2 和 5 之外还有其他项目的集合,这是我不喜欢的。

为了更好地防止 SQL 注入,我更喜欢一种可以使用准备好的语句的方式。从技术上讲,我可以使用字符串连接为准备好的语句组装查询字符串,然后设置查询参数,但是这种方法在某种程度上感觉不对。

(2) 另一种解决方案:我可以首先获取第一个 SetItem 所属的所有集合,然后检查每个返回的 Set 是否还包含所有其他项目并且不包含其他项目。如果第一个 SetItem 包含在大量的 Set 中,这会导致大量的查询,看起来效率低下且不可扩展。

(3) 对于应该包含的每个 SetItem,我可以获取它所在的所有集合,然后在 SQL 之外的代码中进行交集。这最多需要与要检查的 SetItem 一样多的 sql 查询。

(4) 另一种方法是将 setItems 存储为 VARCHAR 形式的逗号分隔列表,按升序排序,直接作为表 Sets 中的附加列。那时不需要表 SetItems。要检查是否存在集合,我可以查询是否存在具有相同逗号分隔列表的行。但是,依靠 SQL 查询中的字符串匹配,像“xy 项包含在哪个集合中”这样的查询就不会那么容易了。关系不大……

问题:如果存在一组相关行,如何有效地查询 SQL 数据库? 我应该以不同的方式构建数据吗?我应该为这样的查询使用 NoSQL 数据库吗?

我目前正在使用 H2,并且希望使用不使用单个数据库供应商的某些特定 SQL 方言的解决方案。

【问题讨论】:

【参考方案1】:

您可以使用having 检查每组有多少不同的匹配项:

select   i.fkSet 
from     SetItems i
where    i.item in (2, 5)
group by s.fkSet
having   count(distinct i.item) = 2

当然,您需要确保最终数字(此处为 2)与您在 in 运算符中列出的值的数量相对应。

【讨论】:

感谢您的回答,它有效!我的实际需求有点复杂(我为这个问题简化了我的问题),但这个答案应该让我重新开始。

以上是关于检查是不是存在一组相关行的主要内容,如果未能解决你的问题,请参考以下文章

如何检查Oracle中的项目表中是不是存在一组值

使用 LEFT OUTER JOIN 检查相关行不存在的最佳方法是啥

检查行是不是已存在如果不存在则插入行否则显示消息

如何检查MySQL中是不是存在行? (即检查电子邮件是不是存在于 MySQL 中)

Room:如何检查行是不是存在

检查行是不是已经存在,如果存在则告诉引用的表 id