2

我有两个这样的表:

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

4 回答 4

1

我认为处理集合成员的最佳方法是使用HAVINGSQL 中的子句。这个想法是将集合的元素组合在一起,在这种情况下,每个 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子句是最通用的,因为它可以容纳关于集合包含标准的广泛逻辑。

于 2012-10-04T18:18:53.337 回答
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 */

在 SQL Fiddle上还有一个演示方法可以使用。

于 2012-10-04T22:21:35.347 回答
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>)

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

于 2012-10-04T18:06:12.740 回答
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
于 2012-10-04T19:19:25.657 回答