SQL查找完全包含给定集的集

时间:2012-10-04 18:00:10

标签: sql oracle

我有两张桌子:

create table doi (
    id number primary key not null,
    -- Other columns omitted
);

create table doi_membership (
    id number primary key not null,
    doi_id number not null, --foreign key to doi.id
    key_1 number not null,
    key_2 number not null,
    -- Other columns omitted
);

我有一组key_1,key_2对,我想知道是否有一个doi包含给定key_1,key_2对的完整集合,仅此而已。我似乎无法想到执行此操作所必需的SQL。如果有帮助,我正在使用oracle数据库。有什么想法吗?

更新

我认为我没有很好地解释这一点,所以我会举一个例子。

从概念上讲,单个DOI包含key_1,key_2对的列表。我有自己的key_1,key_2对列表,我想知道是否存在一个DOI,其中的对列表与我的对列表完全匹配。

因此,假设有一个DOI具有以下key_1,key_2对列表(这些行中的每一行都是与同一个doi相关的单独的doi_membership行):

1, 2
3, 4
5, 6

另一个DOI,其中包含以下列表:

1, 2
3, 4
5, 6
7, 8

我拥有的那对配对

1, 2
3, 4
5, 6

我想匹配给出的第一个DOI,因为它的对和我的一组对完全匹配。第二个DOI应匹配。

我希望能够解决问题。

4 个答案:

答案 0 :(得分:1)

我认为处理集合成员资格的最佳方法是在SQL中使用HAVING子句。我们的想法是将集合的元素组合在一起,在本例中为每个doi的成员资格记录,然后在个人层面进行测试。

例如,以下having子句将检查keyval1是否存在:

having sum(case when keyval_1 = <keyval1> then 1 else 0 end) > 0

它通过将记录数与keyval_1 = <keval1>相加来实现。如果大于0,则“doi_id”符合条件。

你的条件有点复杂,因为你正在寻找成对的价值观。解决这个问题的一种方法是将值连接在一起(不是必需的,但它有点简化了逻辑)。以下子句验证doi_id上仅存在您的值对:

having sum(case when concat(key_1, ',', key_2) in (<key value pairs here>)
                then 0 else 1 end) = 0

它的作用是计算密钥对不匹配的记录数。如果有,则比较失败。您需要为in子句连接键值对。类似于in ('1,1', '2,2', '3,14')

为了概括所有匹配的条件,我使用以下内容:

select doi_id
from doi_membership
group by doi_id
having sum(case when concat(key_1, ',', key_2) in (<key value pairs here>)
                then 0 else 1 end) = 0 and 
       sum(case when concat(key_1, ',', key_2) = <key pair 1>
                then 1 else 0 end) > 0 and
       sum(case when concat(key_1, ',', key_2) = <key pair 2>
                then 1 else 0 end) > 0 and
       . . .
       sum(case when concat(key_1, ',', key_2) = <key pair n>
                then 1 else 0 end)

HAVING子句首先测试所有对都存在。然后剩余的条款测试每对的存在。

还有其他方法。我发现HAVING子句是最通用的,因为它可以适应集合的包含标准的各种逻辑。

答案 1 :(得分:1)

假设您的密钥对列表存储在一个表中,这是您可以考虑的另一种方法:

SELECT m.doi_id
FROM doi_membership m
LEFT JOIN sample_key_set s
  ON m.key_1 = s.key_1 AND m.key_2 = s.key_2
GROUP BY m.doi_id
HAVING COUNT(*) = ALL(
  COUNT(s.key_1),
  (SELECT COUNT(*) FROM sample_key_set)
)
;

查询外部将doi_membership连接到对的样本列表,按doi_id对结果集进行分组,并将组中的总行数与匹配行的总数进行比较,以及与样本对的总数。如果所有计数相等,则返回相应的doi_id

如果你不知道ALL谓词,这个条件

COUNT(*) = ALL(
  COUNT(s.key_1),
  (SELECT COUNT(*) FROM sample_key_set)
)

只是

的捷径
    COUNT(*) = COUNT(s.key_1)
AND COUNT(*) = (SELECT COUNT(*) FROM sample_key_set)

为了表明该方法有效,下面分析了各种例子:

#   Rows in "m"  Rows in "s"  Count values          Outcome
--  -----------  -----------  --------------------  -------
1      1, 2         1, 2      COUNT(*)          =2  MATCH
       3, 4         3, 4      COUNT(s.key_1)    =2
                              SELECT COUNT(*)...=2
--  -----------  -----------  --------------------  -------
2      1, 2         1, 2      COUNT(*)          =1  NO
                    3, 4      COUNT(s.key_1)    =1  MATCH
                              SELECT COUNT(*)...=2
--  -----------  -----------  --------------------  -------
3      1, 2         1, 2      COUNT(*)          =2  NO
       5, 6         3, 4      COUNT(s.key_1)    =1  MATCH
                              SELECT COUNT(*)...=2
--  -----------  -----------  --------------------  -------
4      1, 2         1, 2      COUNT(*)          =3  NO
       3, 4         3, 4      COUNT(s.key_1)    =2  MATCH
       5, 6                   SELECT COUNT(*)...=2

如您所见,使用此方法,仅返回其键集完全匹配的DOI。

作为将示例密钥对列表存储在表中的替代方法,您可以使用如下的公用表表达式:

WITH sample_key_set AS (
  SELECT key1, key2 FROM DUAL UNION ALL
  SELECT key3, key4 FROM DUAL UNION ALL
  ...
)
SELECT m.doi_id
FROM ... /* the rest of the above query */

还有一个方法at SQL Fiddle的演示。

答案 2 :(得分:0)

SELECT <whatever you need>
FROM doi_membership
WHERE (Key_1 = <key value your looking for> AND Key_2 = <key value you're looking for>)

除非我误解了这个问题,否则我认为这很容易

答案 3 :(得分:0)

你不能只使用自我加入吗?

好的,所以你不需要内部比较,只需要参数/硬编码密钥对值。

内部检查将是:

SELECT 
    d.doi_ID, 
    c.CountOfID
FROM 
    doiMembership d INNER JOIN 
    (SELECT 
    doi_ID,Count(ID) CountOfID
    FROM doiMembership
    GROUP BY doi_ID) c ON 
    d.doi_ID = c.doi_ID INNER JOIN 
    (SELECT 
    doi_ID, Count(ID) CountOfID
    FROM doiMembership
    GROUP BY doi_ID) c2  on
    c2.CountOfID = c.CountOfID  inner join
    doiMembership d2 ON 
    c2.doi_ID = d2.doi_ID and
    c.CountOfID = d2.CountOfID AND 
    (d.key1 = d2.[key1]) AND 
    (d.key2 = d2.[key2])
WHERE 
    (d.ID <> d2.[id]) 
GROUP BY 
    d.doi_ID, 
    c.CountOfID

但是如果你想与已知的doi进行比较,你可以使用:

SELECT 
    d.doi_ID, 
    c.CountOfID
FROM 
    doiMembership d INNER JOIN 
    (SELECT 
    doi_ID,Count(ID) AS CountOfID
    FROM doiMembership
    GROUP BY doi_ID) c ON 
    d.doi_ID = c.doi_ID INNER JOIN 
    (SELECT 
    doi_ID, Count(ID) AS CountOfID
    FROM doiMembership
    GROUP BY doi_ID) c2  INNER JOIN 
    doiMembership AS d2 ON 
    c2.doi_ID = d2.doi_ID and
    c.CountOfID = d2.CountOfID
WHERE 
    (d.ID <> d2.[id]) AND 
    (d.key1 = d2.[key1]) AND 
    (d.key2 = d2.[key2]) and
    d.doi_id = 'value'
GROUP BY 
    d.doi_ID, 
    c.CountOfID