如何确定表中是否存在某些行组合

时间:2013-12-13 21:25:28

标签: sql sql-server oracle

我希望能够在子表中返回不包含特定行的唯一ID列表。

我的表类似于:

Id  Name
1   X
1   Y
1   Z
2   A
2   B
2   C
3   X
3   B
3   Z

我想写一个像

这样的SQL查询
SELECT Id
FROM table t
WHERE UPPER(t.Name) IN ('X', 'Y', 'Z') 
      OR UPPER(t.Name) IN ('A', 'B', 'C')  
GROUP BY t.Id
HAVING COUNT(DISTINCT UPPER(t.Name)) != 3

但这不起作用,因为我预计只有Id = 3才会被返回为无效。

这可以在单个SQL语句中使用吗?

此外,如果有任意数量的列表(X,Y,Z; A,B,C; P,Q,Z; ...)或混合列表长度(X),是否可以解决此问题,Y; A,B,C,D; L,M,N; ......)?

  

编辑:

为了澄清,每个id实际上都是指父表。所以这些是儿童记录。

在第一个示例中,父记录仅在包含至少3个子项时才有效。必须将3个孩子命名为(A,B和C)或(X,Y和Z)。父母即使包含所有6个孩子也有效。但是有4个名为A,B,X,Y的孩子无效(添加C或Z孩子会使其有效)。

到目前为止,戈登·林诺夫最接近。我需要再写一些测试。

当然这是一个人为的例子,在我的实现中,不同的规则集将要求我使用不同大小的不同列表(可能是混合的)。例如,我可能有一条规则,即只有父项有子(A和B)或(W,X,Y和Z)或(L,M,N和Z)的子项才有效。

谢谢,

3 个答案:

答案 0 :(得分:2)

您希望查找不包含X,Y,Z或A,B,C的所有行。您可以使用aggregation和having子句执行此操作:

select id
from t
group by name
having not ((sum(case when Name = 'X' then 1 else 0 end) > 0 and
             sum(case when Name = 'Y' then 1 else 0 end) > 0 and
             sum(case when Name = 'Z' then 1 else 0 end) > 0
            ) or
            (sum(case when Name = 'A' then 1 else 0 end) > 0 and
             sum(case when Name = 'B' then 1 else 0 end) > 0 and
             sum(case when Name = 'C' then 1 else 0 end) > 0
            )
       );

having子句中的每个条件都计算与特定名称匹配的行数。当存在至少一个时,一行通过过滤器。 andor的组合似乎符合您的要求。

请注意,具有A,B,C和D的id将匹配。您的问题没有说明这是正确还是不正确。

答案 1 :(得分:1)

SQL Fiddle

Oracle 11g R2架构设置

CREATE TABLE tbl ( Id NUMBER(1),  Name VARCHAR2(1) );

INSERT INTO tbl VALUES ( 1,   'X' );
INSERT INTO tbl VALUES ( 1,   'Y' );
INSERT INTO tbl VALUES ( 1,   'Z' );
INSERT INTO tbl VALUES ( 2,   'A' );
INSERT INTO tbl VALUES ( 2,   'B' );
INSERT INTO tbl VALUES ( 2,   'C' );
INSERT INTO tbl VALUES ( 3,   'X' );
INSERT INTO tbl VALUES ( 3,   'B' );
INSERT INTO tbl VALUES ( 3,   'Z' );
INSERT INTO tbl VALUES ( 4,   'F' );
INSERT INTO tbl VALUES ( 4,   'G' );
INSERT INTO tbl VALUES ( 4,   'H' );

CREATE TYPE VARCHAR2s_1_Table AS TABLE OF VARCHAR2(1);

查询1

WITH groups AS (
  SELECT id,
         CAST( COLLECT( Name ) AS VARCHAR2s_1_Table ) AS grp
  FROM   tbl
  GROUP BY
         id
)
SELECT id
FROM   groups
WHERE  ( grp MULTISET INTERSECT VARCHAR2s_1_Table( 'X', 'Y', 'Z') ) IS NOT EMPTY
AND    ( grp MULTISET INTERSECT VARCHAR2s_1_Table( 'A', 'B', 'C') ) IS NOT EMPTY

<强> Results

| ID |
|----|
|  3 |

答案 2 :(得分:0)

CREATE TYPE string_table
AS TABLE OF VARCHAR2(1);

WITH grouped_names AS (
   SELECT
      id,
      CAST(COLLECT(name) AS string_table) AS names_grp
   FROM tbl
   GROUP BY id
)
SELECT id
FROM grouped_names
WHERE string_table('A','B','C') NOT SUBMULTISET OF names_grp
  AND string_table('X','Y','Z') NOT SUBMULTISET OF names_grp