在 sql 中只有 select 个条件的唯一记录

Only select unique record for a condition in sql

我如何才能 select 只有不同的用户 ID 仅具有 ACCESS_COLUMN_ID 值 1,即使它们也可能具有 ACCESS_COLUMN_ID 值 2。

这是我的查询 returns 1 和 2:

SELECT DISTINCT(USER_ID) FROM USER_ACCESS WHERE ACCESS_COLUMN_ID = 1

返回的结果中也包含有ACCESS_COLUMN_ID = 2的userid。

Here is my table data

USERID ACCESS_COLUMN_ID 
1          1
1          2
2          1

我只希望 USERID 2 作为我的查询结果

你可以使用 HAVING:

SELECT    USER_ID
FROM      USER_ACCESS 
GROUP  BY USER_ID
HAVING    MIN(ACCESS_COLUMN_ID) = 1
AND       MAX(ACCESS_COLUMN_ID) = 1

此查询将获取所有 user_id,但由于 group by 子句,仅获取唯一的。然后它会为它们中的每一个取它找到的最小值和最大值access_column_id,如果这两个值都是1,那么user_id会保留在最终结果集中。

上面的代码会有很好的性能,因为它只引用了一次table。

为了您的兴趣,还有其他几种方法可以获得相同的结果。但是,它们都需要 table 被引用两次。您可能想自己比较它们的可读性和性能:

不存在

SELECT    DISTINCT USER_ID
FROM      USER_ACCESS UA1
WHERE     UA1.ACCESS_COLUMN_ID = 1
AND       NOT EXISTS (
              SELECT 1
              FROM   USER_ACCESS UA2
              WHERE  UA1.USER_ID = UA2.USER_ID
              AND    UA2.ACCESS_COLUMN_ID <> 1)

不在

这与上一个非常相似,但根据我的经验,性能不如上一个:

SELECT    DISTINCT USER_ID
FROM      USER_ACCESS
WHERE     ACCESS_COLUMN_ID = 1
AND       USER_ID NOT IN (
              SELECT USER_ID
              FROM   USER_ACCESS
              WHERE  ACCESS_COLUMN_ID <> 1)

外自连接

这通常比前两个解决方案具有更好的性能:

SELECT    DISTINCT USER_ID
FROM      USER_ACCESS UA1
LEFT JOIN USER_ACCESS UA2
       ON UA1.USER_ID = UA2.USER_ID
      AND UA2.ACCESS_COLUMN_ID <> 1
WHERE     UA1.ACCESS_COLUMN_ID = 1
AND       UA2.USER_ID IS NULL

最后一个 NULL 条件检查外连接没有产生任何匹配(与 ACCESS_COMUN_ID <> 1)。

除外

这是SQL服务器特有的语法,但是很容易理解(Oracle有类似的MINUS);

SELECT    DISTINCT USER_ID
FROM      USER_ACCESS
WHERE     ACCESS_COLUMN_ID = 1
EXCEPT
SELECT    USER_ID
FROM      USER_ACCESS
WHERE     ACCESS_COLUMN_ID <> 1

DISTINCT备注

DISTINCT 关键字很容易理解,但通常使用 GROUP BY 子句可能会获得更好的性能。这可以应用于上述所有解决方案。

如果确定USER_IDACCESS_COLUMN_ID不可能有两条记录具有相同的值,则可以在上述查询中省略DISTINCT关键字。

可以用NOT IN过滤掉ACCESS_COLUMN_ID = 2

SELECT DISTINCT(USER_ID) FROM USER_ACCESS WHERE ACCESS_COLUMN_ID = 1 and 
USER_ID NOT IN (
  SELECT DISCTINCT (USER_ID) FROM USER_ACCESS WHERE ACCESS_COLUMN_ID = 2
)

您可以通过多种方式完成此任务。这可能是最灵活的。

获取具有所需访问 ID 的所有用户的列表,并将其弹回具有不需要的访问 ID 的用户列表。这具有可扩展的好处。

SELECT * FROM USER_ACCESS u1
LEFT OUTER JOIN 
    (SELECT USER_ID FROM USER_ACCESS 
     WHERE ACCESS_COLUMN_ID NOT IN ($IDsGoHere$)) u2
ON u1.USER_ID = u2.USER_ID
WHERE u1.ACCESS_COLUMN_ID IN ($IDsGoHere$) AND
u2.USER_ID IS NULL;

几个要点:

  • 查询可能受益于子select table u2 可能是 DISTINCT。这取决于返回结果的数量。
  • 如果您想查看所有有权访问 A 但无权访问 B 的用户,您可以相应地替换子 select 的 WHERE NOT IN 子句。

我不知道 SQLServer 是如何处理优化的,但我看到系统发现用 = X 和 [=17 替换 IN (X)NOT IN (X) 可以获得性能提升=]分别。

编辑 1 - 关于 WHERE 子句的一般思考

根据一般经验,在计算响应时考虑是否需要参考不在生成行范围内的其他行总是好的。如果你这样做了(在这种情况下,该行必须存在,而其他行不存在相同的 USER_ID),这通常表明你需要引入某种连接来消除你的结果不想。