在多个列上计数DISTINCT

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

SELECT COUNT(*)
FROM (SELECT DISTINCT DocumentId, DocumentSessionId
FROM DocumentOutputItems) AS internalQuery

我需要数一下这个表中不同项的数量,但不同项超过两列。

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

644592 次浏览

希望这能起作用,我正在prima vista上写

SELECT COUNT(*)
FROM DocumentOutputItems
GROUP BY DocumentId, DocumentSessionId

您不喜欢现有查询的哪些方面?如果你担心跨两列的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>

编辑

我进入了分析的死胡同,但答案很明显……

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


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


SQL>

编辑2

对于以下数据,上面提供的串联解决方案将会计数错误:

col1  col2
----  ----
A     AA
AA    A

所以我们要包含分隔符…

select col1 + '*' + col2 from t23
/

显然,所选择的分隔符必须是一个字符或一组字符,它不能出现在任何一列中。

比如:

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


可能只是做了和你已经做的一样的事情,但是它避免了DISTINCT。

下面是不带subselect的简短版本:

SELECT COUNT(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems

它在MySQL中工作得很好,我认为优化器更容易理解这一点。

编辑:显然我误解了MSSQL和MySQL -对不起,但也许它有帮助。

如果你只有一个字段可以“DISTINCT”,你可以使用:

SELECT COUNT(DISTINCT DocumentId)
FROM DocumentOutputItems

并且返回与原始的相同的查询计划,正如SET SHOWPLAN_ALL ON测试的那样。然而,你正在使用两个字段,所以你可以尝试一些疯狂的东西,如:

    SELECT COUNT(DISTINCT convert(varchar(15),DocumentId)+'|~|'+convert(varchar(15), DocumentSessionId))
FROM DocumentOutputItems

但如果涉及到null,就会出现问题。我还是用原来的问题吧。

你的查询没有问题,但你也可以这样做:

WITH internalQuery (Amount)
AS
(
SELECT (0)
FROM DocumentOutputItems
GROUP BY DocumentId, DocumentSessionId
)
SELECT COUNT(*) AS NumberOfDistinctRows
FROM internalQuery

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

一旦它被持久化,只要列是确定的,并且您使用的是“正常的”数据库设置,就可以对其建立索引和/或在其上创建统计信息。

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

编辑:从不可靠的仅校验和查询更改 我发现了一种方法来做到这一点(在SQL Server 2005中),这对我来说很好,我可以使用尽可能多的列,因为我需要(通过将它们添加到CHECKSUM()函数)。REVERSE()函数将int型转换为varchars型,以使distinct型更加可靠

SELECT COUNT(DISTINCT (CHECKSUM(DocumentId,DocumentSessionId)) + CHECKSUM(REVERSE(DocumentId),REVERSE(DocumentSessionId)) )
FROM DocumentOutPutItems

我希望MS SQL也能做一些类似COUNT(DISTINCT A, B)的事情,但它不能。

起初,JayTee的答案对我来说似乎是一个解决方案,但经过一些测试,CHECKSUM()未能创建唯一的值。一个简单的例子是,CHECKSUM(31,467,519)和CHECKSUM(69,1120,823)给出的答案都是55。

然后我做了一些研究,发现微软不建议使用CHECKSUM进行更改检测。在一些论坛上,有人建议使用

SELECT COUNT(DISTINCT CHECKSUM(value1, value2, ..., valueN) + CHECKSUM(valueN, value(N-1), ..., value1))

但这也不令人欣慰。

您可以按照TSQL校验和难题中的建议使用HASHBYTES()函数。然而,这也有一个小的机会不返回唯一的结果。

我建议使用

SELECT COUNT(DISTINCT CAST(DocumentId AS VARCHAR)+'-'+CAST(DocumentSessionId AS VARCHAR)) FROM DocumentOutputItems

当我在谷歌上搜索我自己的问题时,发现如果你计算DISTINCT对象,你会得到正确的返回数(我使用MySQL)

SELECT COUNT(DISTINCT DocumentID) AS Count1,
COUNT(DISTINCT DocumentSessionId) AS Count2
FROM DocumentOutputItems

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

SELECT count(DISTINCT concat(DocumentId, DocumentSessionId)) FROM DocumentOutputItems;

在MySQL中,你可以做同样的事情,而不需要下面的连接步骤:

SELECT count(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems;

MySQL文档中提到了这个特性:

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

这对我很管用。在oracle中:

SELECT SUM(DECODE(COUNT(*),1,1,1))
FROM DocumentOutputItems GROUP BY DocumentId, DocumentSessionId;

在jpql:

SELECT SUM(CASE WHEN COUNT(i)=1 THEN 1 ELSE 1 END)
FROM DocumentOutputItems i GROUP BY i.DocumentId, i.DocumentSessionId;

这个怎么样,

Select DocumentId, DocumentSessionId, count(*) as c
from DocumentOutputItems
group by DocumentId, DocumentSessionId;

这将得到documententid和DocumentSessionId的所有可能组合的计数

一些SQL数据库可以使用元组表达式,所以你可以这样做:

SELECT COUNT(DISTINCT (DocumentId, DocumentSessionId))
FROM DocumentOutputItems;
如果你的数据库不支持,可以根据@oncel-umut-的建议来模拟CHECKSUM或其他提供良好唯一性的标量函数。 COUNT(DISTINCT CONCAT(DocumentId, ':', DocumentSessionId)) . < / p >

MySQL特别支持COUNT(DISTINCT expr, expr, ...)是非sql标准语法。它还注意到In standard SQL, you would have to do a concatenation of all expressions inside COUNT(DISTINCT ...).

元组的一个相关用法是执行IN查询,例如:

SELECT * FROM DocumentOutputItems
WHERE (DocumentId, DocumentSessionId) in (('a', '1'), ('b', '2'));

你可以使用Count函数两次。

在这种情况下,它将是:

SELECT COUNT (DISTINCT DocumentId), COUNT (DISTINCT DocumentSessionId)
FROM DocumentOutputItems

这段代码使用distinct on 2参数,并提供特定于这些不同值的行数计数。它在MySQL中为我工作,就像一个魅力。

select DISTINCT DocumentId as i,  DocumentSessionId as s , count(*)
from DocumentOutputItems
group by i ,s;

我有一个类似的问题,但我的查询是一个子查询与比较数据在主查询。喜欢的东西:

Select code, id, title, name
(select count(distinct col1) from mytable where code = a.code and length(title) >0)
from mytable a
group by code, id, title, name
--needs distinct over col2 as well as col1

忽略这个问题的复杂性,我意识到我无法用原问题中描述的双子查询将a.code的值获取到子查询中

Select count(1) from (select distinct col1, col2 from mytable where code = a.code...)
--this doesn't work because the sub-query doesn't know what "a" is

所以最后我发现我可以作弊,把这些列合并起来:

Select count(distinct(col1 || col2)) from mytable where code = a.code...

这就是最终成功的方法

我用过这种方法,对我很有效。

SELECT COUNT(DISTINCT DocumentID || DocumentSessionId)
FROM  DocumentOutputItems

对于我的案例,它提供了正确的结果。

如果你正在处理固定长度的数据类型,你可以转换为binary来非常容易和快速地做到这一点。假设DocumentIdDocumentSessionId都是__abc3,因此长度为4字节…

SELECT COUNT(DISTINCT CAST(DocumentId as binary(4)) + CAST(DocumentSessionId as binary(4)))
FROM DocumentOutputItems

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

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