在多列上计算DISTINCT

Nov*_*zky 184 sql t-sql sql-server

有没有更好的方法来执行这样的查询:

SELECT COUNT(*) 
FROM (SELECT DISTINCT DocumentId, DocumentSessionId
      FROM DocumentOutputItems) AS internalQuery
Run Code Online (Sandbox Code Playgroud)

我需要计算此表中不同项目的数量,但不同的是超过两列.

我的查询工作正常,但我想知道我是否只使用一个查询得到最终结果(不使用子查询)

Jas*_*ner 65

如果您尝试提高性能,可以尝试在两列的散列值或连接值上创建持久计算列.

一旦它被持久化,如果列是确定性的并且您正在使用"理智的"数据库设置,则可以对其进行索引和/或在其上创建统计数据.

我相信计算列的不同计数将等同于您的查询.

  • 您能否添加一个示例或代码示例来详细说明这意味着什么以及如何做到这一点? (4认同)
  • 优秀的建议!我读的越多,我就越意识到SQL不仅仅是知道语法和函数,更多的是关于应用纯逻辑......我希望我有2个upvotes! (3认同)
  • 太好的建议了。它避免了我为此编写不必要的代码。 (2认同)
  • 这个技巧如何关心哈希冲突?我认为由于冲突,哈希值的不同计数将小于真实值。 (2认同)

Jay*_*Tee 50

编辑:从不太可靠的校验和查询改变 我发现了一种方法(在SQL Server 2005中),这对我来说非常好,我可以使用尽可能多的列(通过将它们添加到CHECKSUM()函数).REVERSE()函数将ints转换为varchars以使distinct更加可靠

SELECT COUNT(DISTINCT (CHECKSUM(DocumentId,DocumentSessionId)) + CHECKSUM(REVERSE(DocumentId),REVERSE(DocumentSessionId)) )
FROM DocumentOutPutItems
Run Code Online (Sandbox Code Playgroud)

  • 对于像Checksum()这样的散列,很少有机会为不同的输入返回相同的散列,因此计数可能会略微偏离.HashBytes()的机会更小,但仍然不是零.如果这两个Ids是int(32b),则"无损散列"可以将它们组合成bigint(64b),如Id1 << 32 + Id2. (7认同)
  • 我们可以避免CHECKSUM - 我们可以将这两个值连接在一起吗?我认为风险考虑同样的事情:('他','艺术')=='听','t').但我认为这可以通过@APC提出的分隔符​​来解决(某些值不会出现在任何一列中),所以'他| art'!='听到| t'简单的"连接"还有其他问题吗?进场? (4认同)
  • 机会甚至不那么小,尤其是当您开始组合列时(这应该是它的用途)。我对这种方法很好奇,在特定情况下,校验和的计数最终小了 10%。如果你再想一想,Checksum 只会返回一个 int,所以如果你对一个完整的 bigint 范围进行校验和,你最终会得到一个比实际小 20 亿倍的不同计数。-1 (3认同)
  • +1 不错,完美运行(当您拥有正确的列类型来执行 CheckSum 时...;) (2认同)

APC*_*APC 28

您不喜欢的现有查询是什么?如果您担心DISTINCT跨两列不返回唯一的排列,为什么不尝试呢?

它确实可以像您在Oracle中所期望的那样工作.

SQL> select distinct deptno, job from emp
  2  order by deptno, job
  3  /

    DEPTNO JOB
---------- ---------
        10 CLERK
        10 MANAGER
        10 PRESIDENT
        20 ANALYST
        20 CLERK
        20 MANAGER
        30 CLERK
        30 MANAGER
        30 SALESMAN

9 rows selected.


SQL> select count(*) from (
  2  select distinct deptno, job from emp
  3  )
  4  /

  COUNT(*)
----------
         9

SQL>
Run Code Online (Sandbox Code Playgroud)

编辑

我带着分析走了一条死胡同,但答案却显得令人沮丧......

SQL> select count(distinct concat(deptno,job)) from emp
  2  /

COUNT(DISTINCTCONCAT(DEPTNO,JOB))
---------------------------------
                                9

SQL>
Run Code Online (Sandbox Code Playgroud)

编辑2

鉴于以下数据,上面提供的连接解决方​​案将错误计算:

col1  col2
----  ----
A     AA
AA    A
Run Code Online (Sandbox Code Playgroud)

所以我们要包括一个分隔符......

select col1 + '*' + col2 from t23
/
Run Code Online (Sandbox Code Playgroud)

显然,所选的分隔符必须是一个字符或一组字符,它们永远不会出现在任一列中.


Tre*_*ins 14

怎么样的:

select count(*)
from
  (select count(*) cnt
   from DocumentOutputItems
   group by DocumentId, DocumentSessionId) t1

可能只是和你一样,但它避免了DISTINCT.

  • 根据原始查询的复杂性,使用“GROUP BY”解决此问题可能会给查询转换带来一些额外的挑战,以实现所需的输出(例如,当原始查询已经具有“GROUP BY”或“HAVING”子句时。 ..) (2认同)

spe*_*nk1 13

要作为单个查询运行,请连接列,然后获取连接字符串的不同实例计数.

SELECT count(DISTINCT concat(DocumentId, DocumentSessionId)) FROM DocumentOutputItems;
Run Code Online (Sandbox Code Playgroud)

在MySQL中,您可以执行相同的操作,而无需连接步骤,如下所示:

SELECT count(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems;
Run Code Online (Sandbox Code Playgroud)

MySQL文档中提到了此功能:

http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count-distinct

  • FWIW,这几乎适用于 PostgreSQL;只需要额外的括号:`SELECT COUNT(DISTINCT (DocumentId, DocumentSessionId)) FROM DocumentOutputItems;` (3认同)
  • 使用此方法时要非常小心,因为它可能会导致错误的计数。以下示例将返回计数 1。文档会话ID“A”| “AB”“AA”| “乙” (3认同)
  • 正如 @Bort 指出的,第一个选项可能会导致不正确的结果,并且最好使用 CONCAT_WS 编写。如果任何列可以为空,第二种方法也不能保证产生与原始查询相同的结果。 (2认同)

kar*_*aze 13

许多(大多数?)SQL 数据库可以使用像值这样的元组,因此您可以这样做: SELECT COUNT(DISTINCT (DocumentId, DocumentSessionId)) FROM DocumentOutputItems; 如果您的数据库不支持此功能,则可以根据 @oncel-umut-turer 的 CHECKSUM 或其他标量函数的建议进行模拟,以提供良好的唯一性例如 COUNT(DISTINCT CONCAT(DocumentId, ':', DocumentSessionId))

元组的一个相关用途是执行IN查询,例如: SELECT * FROM DocumentOutputItems WHERE (DocumentId, DocumentSessionId) in (('a', '1'), ('b', '2'));

  • 哪些数据库支持`select count(distinct(a, b))`?:D (3认同)
  • @VytenisBivainis 我知道 PostgreSQL 确实如此——不确定从哪个版本开始。 (2认同)

小智 8

我已经使用了这种方法并且它对我有用。

SELECT COUNT(DISTINCT DocumentID || DocumentSessionId) 
FROM  DocumentOutputItems
Run Code Online (Sandbox Code Playgroud)

就我而言,它提供了正确的结果。

  • 这个问题被标记为 SQL Server,这不是 SQL Server 语法 (4认同)
  • 警告!这可能会得到不正确的结果。考虑 DocumentID=123、DocumentSessionId=21 和 DocumentID=12、DocumentSessionId=321 (2认同)

Ale*_*äll 7

这是一个没有子选择的较短版本:

SELECT COUNT(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems
Run Code Online (Sandbox Code Playgroud)

它在MySQL中运行良好,我认为优化器更容易理解这个.

编辑:显然我误读了MSSQL和MySQL - 抱歉,但也许它有帮助.

  • 在SQL Server中你得到:__ Msg 102,Level 15,State 1,Line 1','.__附近的语法不正确 (6认同)
  • 请看看@JayTee 的答案。它就像一个魅力。`计数(不同的校验和([Field1],[Field2])` (3认同)

小智 5

如果您正在使用固定长度的数据类型,则可以通过强制转换来binary非常轻松且快速地完成此操作。假设DocumentIdDocumentSessionId都是ints,因此长度为 4 个字节......

SELECT COUNT(DISTINCT CAST(DocumentId as binary(4)) + CAST(DocumentSessionId as binary(4)))
FROM DocumentOutputItems
Run Code Online (Sandbox Code Playgroud)

我的具体问题要求我将 a 除以SUM各种COUNT外键和日期字段的不同组合,按另一个外键分组,偶尔按某些值或键进行过滤。表非常大,使用子查询会大大增加查询时间。由于其复杂性,统计根本不是一个可行的选择。该CHECKSUM解决方案的转换速度也太慢,特别是由于数据类型不同,我不能冒它不可靠性的风险。

然而,使用上述解决方案几乎没有增加查询时间(与仅使用 相比SUM),并且应该是完全可靠的!它应该能够帮助处于类似情况的其他人,所以我将其发布在这里。