2011-03-29 108 views
3

如果存在一组项目,我想查询关系数据库。查询关系数据库中的集合

我建模数据有以下形式:

key1 = [ item1, item3, item5 ] 
key2 = [ item2, item7 ] 
key3 = [ item2, item3, item4, item5 ] 
... 

我将它们存储在一个表下面的模式

CREATE TABLE sets (key INTEGER, item INTEGER); 

因此,举例来说,下面的INSERT语句会插入以上三套。

INSERT INTO sets VALUES (key1, item1); 
INSERT INTO sets VALUES (key1, item3); 
INSERT INTO sets VALUES (key1, item5); 
INSERT INTO sets VALUES (key2, item2); 
INSERT INTO sets VALUES (key2, item7); 
INSERT INTO sets VALUES (key3, item2); 
INSERT INTO sets VALUES (key3, item3); 
INSERT INTO sets VALUES (key3, item4); 
INSERT INTO sets VALUES (key3, item5); 

给定一组项目,我想用一组相关联的密钥,如果它被存储在表和NULL,如果事实并非如此。是否有可能用sql查询做到这一点?如果是这样,请提供详细信息。

详细信息,可能是相关的:

  • 我在数据库设计/查询策略主要有兴趣,但我最终会在MySQL实现这一点,并使用mysql-Python包在Python从瓶坯查询。
  • 如果不同的布局对于这种类型的查询更方便,我可以自由重构数据库模式。
  • 每个集合,如果它存在应该是唯一的。
  • 我对部分比赛不感兴趣。
  • 数据库规模大约为< 1000套,其中每套包含< 10个项目,因此此时的性能不是优先级。

在此先感谢。

+0

Iam -1:从现在开始,所有“预先感谢”。所以禁止我! – stefan 2011-03-29 23:08:42

+0

问一个真正的问题。这是值得您的SQL的教授应该教给你(虽然他可能会教你全错,所以使用互联网资源,而不是) – stefan 2011-03-29 23:09:28

+1

@stefan,何必那么认真? – epaps 2011-03-30 00:05:42

回答

2

我不会评论是否有更适合的模式来完成此操作(这很可能),但对于具有列nameitem的模式,以下查询应该可以工作。 (MySQL的句法)

SELECT k.name 
FROM (SELECT DISTINCT name FROM sets) AS k 
INNER JOIN sets i1 ON (k.name = i1.name AND i1.item = 1) 
INNER JOIN sets i2 ON (k.name = i2.name AND i2.item = 3) 
INNER JOIN sets i3 ON (k.name = i3.name AND i3.item = 5) 
LEFT JOIN sets ix ON (k.name = ix.name AND ix.item NOT IN (1, 3, 5)) 
WHERE ix.name IS NULL; 

的想法是,我们在k所有设置键,我们则在sets与设定项数据加入一次组中的每个组项目,我们在寻找,三这个案例。表格别名i1i2i3的三个内部联接中的每个都会过滤掉所有不包含使用该联接搜索的项目的名称。最后,我们还有一个sets的左连接,其表别名为ix,它带来了集合中的所有额外项目,也就是我们没有搜索的每个项目。 ix.nameNULL在没有找到额外项目的情况下,这正是我们想要的,因此WHERE条款。如果找到该集合,则查询返回包含set key的行,否则不返回行。


编辑: collapsars答案背后的想法似乎是比我好很多,所以这里有与解释有点较短的版本。

SELECT sets.name 
FROM sets 
LEFT JOIN (
    SELECT DISTINCT name 
    FROM sets 
    WHERE item NOT IN (1, 3, 5) 
) s1 
ON (sets.name = s1.name) 
WHERE s1.name IS NULL 
GROUP BY sets.name 
HAVING COUNT(sets.item) = 3; 

的这里的想法是,子查询s1选择包含我们正在寻找的那些其他项目组的所有的钥匙。因此,当我们离开加入setss1时,s1.nameNULL当集合只包含我们正在搜索的项目。然后,我们按设置键进行分组,并过滤​​出任何具有错误项目数的组。然后,我们只留下只包含我们正在搜索并且长度正确的项目的集合。由于集合只能包含一个项目,因此只能有一个满足该条件的集合,这就是我们正在寻找的集合。


编辑:这只是我恍然大悟如何做到这一点,而不排除。

SELECT totals.name 
FROM (
    SELECT name, COUNT(*) count 
    FROM sets 
    GROUP BY name 
) totals 
INNER JOIN (
    SELECT name, COUNT(*) count 
    FROM sets 
    WHERE item IN (1, 3, 5) 
    GROUP BY name 
) matches 
ON (totals.name = matches.name) 
WHERE totals.count = 3 AND matches.count = 3; 

第一个子查询查找每个集合中项目的总数,第二个查找每个集合中匹配项目的数量。当matches.count是3时,该集合包含我们正在寻找的所有项目,并且如果totals.count也是3,则该集合没有任何额外项目。

+0

我不认为有必要排除: – momeara 2011-03-31 13:10:33

+0

@momeara如果我们不排除具有我们正在寻找的项目以外的项目的集合,则查询将返回所有具有相同项目数量的集合甚至一个匹配项目。也就是说,搜索集合(1,3,5)也可以返回(1,4,7),因为它具有相同的长度,1是搜索到的项目之一,并且我们不排除包含非搜索项目。 – 2011-03-31 21:27:34

1

aleksis解决方案需要对每个可能的项目集进行特定的查询。以下建议提供了一种通用的解决方案,即将要查询的项目集可以作为另一个查询的结果集进行分解 - 只需使用适当的子查询替换集合包含操作符即可。

 SELECT CASE COUNT(ddd.key) WHEN 0 THEN NULL ELSE MIN(ddd.key) END 
     FROM (
       SELECT s4.key 
         , COUNT(*) icount 
        FROM sets s4 
        JOIN (
          SELECT DISTINCT d.key 
          FROM (
            SELECT s1.key 
            FROM sets s1 
            WHERE s1.item IN ('item1', 'item3', 'item5') 
            MINUS 
            SELECT s2.key 
            FROM sets s2 
            WHERE s2.item NOT IN ('item1', 'item3', 'item5') 
           ) d  
         ) dd ON (dd.key = s4.key) 
       GROUP BY s4.key 
      ) ddd 
     WHERE ddd.icount = (
          SELECT COUNT(*) 
           FROM (
             SELECT DISTINCT s3.item 
             FROM sets s3 
             WHERE s3.item IN ('item1', 'item3', 'item5') 
            ) 
         ) 
      ;     

结果集DD提供一组候选谁不与其他项目比那些从一组被测试asscociate键。唯一不明确的地方可能来自引用测试项目集的适当子集的键。因此我们计算与dd关键字相关的项目数量,并选择该数字与测试项目集合的基数相匹配的关键字。如果这样的密钥存在,它是唯一的(因为我们知道该项目集是唯一的)。 最外层select中的case表达式只是一种保证它们不会为空结果集的奇特方式,即如果项集不由关系表示,则返回null值。

也许这个解决方案将是对你有用,

问候

卡斯滕

+0

这很聪明。谢谢! – momeara 2011-03-30 13:12:19

0

为了简化黑洞的解决方案,这是已经被阿列克西Torhamo简化:

这是没有必要为了得到所有不匹配的密钥,可能很大,只要找到匹配的密钥并将它们称为部分匹配即可。

-- get all partial matches 
CREATE TEMPORARY VIEW partial_matches AS 
SELECT DISTINCT key FROM sets WHERE item IN (1,3,5); 

-- filter for full matches 
SELECT sets.key 
FROM sets, partial_matches 
WHERE sets.key = partial_matches.key 
GROUP BY sets.key HAVING COUNT(sets.key) = 3; 
+0

这不起作用。我想你的意思是'COUNT(sets.item)'。如果你改变它,并在数据库中设置(1,3,5)和(1,4,7),搜索(1,3,5)将返回两者,因为两者都包含部分匹配(单独1个就足够了对于另一组被认为是部分匹配)并且都具有正确数量的项目。 – 2011-03-31 21:18:49

+0

是的,我认为你是对的! – momeara 2011-04-01 18:27:41

1

此查询有一个众所周知的名称。谷歌“关系部门”,“一套遏制加入”,“一套平等参加”。

+0

我从来没有听说过这个名字。 – Marcin 2012-02-04 09:03:11