找出列中每个组是否包含两个不同的值

时间:2018-09-03 09:54:28

标签: sql sql-server tsql relational-division

我想选择同时满足同一列的两个条件的行。下面是表架构。

安全表

Id  RoleId  CompId  SecurityToken       Accesstype
1   1           10           abc        2
2   1           10           xyz        2
3   12          10           abc        2
4   16          12           abc        2
5   16          12           xyz        2
6   30          13           abc        2
7   1           10           efg        2
8   1           10           lmn        0 

我想要“每种RoleID / CompID组合的所有行,其中accesstype = 2,并且对于该角色/ compID组合,既包含包含安全令牌“ abc”的行,又包含包含SecurityToken“ xyz”的行”

输出应为

Id  RoleId  CompId  SecurityToken       Accesstype
1   1           10           abc        2
2   1           10           xyz        2
4   16          12           abc        2
5   16          12           xyz        2

3 个答案:

答案 0 :(得分:6)

我相信以下查询将产生所需的输出:

SELECT *
FROM testdata
WHERE Accesstype = 2
AND SecurityToken IN ('abc', 'xyz')
AND EXISTS (
    SELECT 1
    FROM testdata AS tmp
    WHERE RoleId = testdata.RoleId 
    AND CompId = testdata.CompId
    AND Accesstype = testdata.AccessType
    AND SecurityToken IN ('abc', 'xyz')
    HAVING COUNT(DISTINCT SecurityToken) = 2
)

SQL Fiddle

为了消除包含额外安全性令牌(例如efglmn)的集合,请将WHERE和HAVING子句更改为:

    WHERE RoleId = testdata.RoleId 
    AND CompId = testdata.CompId
    AND Accesstype = testdata.AccessType
    HAVING COUNT(DISTINCT SecurityToken) = 2
    AND COUNT(DISTINCT SecurityToken) = COUNT(CASE WHEN SecurityToken IN ('abc', 'xyz') THEN 1 END)

答案 1 :(得分:0)

CREATE TABLE #Table1
    ([Id] int, [RoleId] int, [CompId] int, [SecurityToken] varchar(3), [Accesstype] int)
;

INSERT INTO #Table1
    ([Id], [RoleId], [CompId], [SecurityToken], [Accesstype])
VALUES
    (1, 1, 10, 'abc', 2),
    (2, 1, 10, 'xyz', 2),
    (3, 12, 10, 'abc', 2),
    (4, 16, 12, 'abc', 2),
    (5, 16, 12, 'xyz', 2),
    (6, 30, 13, 'abc', 2)
;
WITH cte AS (
SELECT *,ROW_NUMBER() OVER (PARTITION BY [ROLEID],[COMPID] ORDER BY ID) AS RN FROM #TABLE1
),
COUNTED AS (
  SELECT
    *,
    COUNT(*) OVER (PARTITION BY [ROLEID],[COMPID]) AS CNT
  FROM cte
)
SELECT
[ID], [ROLEID], [COMPID], [SECURITYTOKEN], [ACCESSTYPE]
FROM COUNTED
WHERE CNT >= 2

输出

ID  ROLEID  COMPID  SECURITYTOKEN   ACCESSTYPE
1   1        10            abc          2
2   1        10            xyz          2
4   16       12            abc          2
5   16       12            xyz          2

WITH CTE AS 
(
  SELECT
    *,
    COUNT(*) OVER (PARTITION BY [ROLEID],[COMPID]) AS CNT
  FROM #TABLE1)
  SELECT [ID], [ROLEID], [COMPID], [SECURITYTOKEN], [ACCESSTYPE] FROM CTE WHERE CNT>=2

答案 2 :(得分:0)

一种方法使用exists

select t.*
from t
where t.Accesstype = 2 and
      t.securityToken in ('abc', 'xyz') and
      exists (select 1
              from t t2
              where t2.RoleId = t.RoleId and
                    t2.CompId = t.CompId and
                    t2.Accesstype = t.AccessType and
                    t2.SecurityToken in ('abc', 'xyz') and
                    t2.SecrityToken <> t.SecurityToken
             );

也许更简单的方法使用窗口函数:

select t.*
from (select t.*,
             min(securitytoken) over (partition by roleid, compid) as min_st,
             min(securitytoken) over (partition by roleid, compid) as max_st
      from t
      where t.Accesstype = 2 and
            t.SecurityToken in ('abc', 'xyz')
     ) t
where minsecuritytoken = 'abc' and
      maxsecuritytoken = 'xyz;