簡體   English   中英

在多列上計算 DISTINCT

[英]Counting DISTINCT over multiple columns

有沒有更好的方法來進行這樣的查詢:

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

我需要計算此表中不同項的數量,但不同項超過兩列。

我的查詢工作正常,但我想知道我是否可以僅使用一個查詢(不使用子查詢)獲得最終結果

如果您想提高性能,您可以嘗試在兩列的散列或串聯值上創建一個持久化計算列。

一旦它被持久化,如果該列是確定性的並且您使用的是“健全的”數據庫設置,則可以對其進行索引和/或可以在其上創建統計信息。

我相信計算列的不同計數相當於您的查詢。

編輯:從不太可靠的僅校驗和查詢改變我發現了一種方法來做到這一點(在 SQL Server 2005 中)對我來說效果很好,我可以根據需要使用盡可能多的列(通過將它們添加到CHECKSUM() 函數)。 REVERSE() 函數將整數轉換為 varchars 以使不同的更可靠

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

您不喜歡現有查詢的哪些方面? 如果您擔心跨兩列的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(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

怎么樣:

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

可能只是和你已經做的一樣,但它避免了 DISTINCT。

許多(大多數?)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 DocumentId, DocumentSessionId) FROM DocumentOutputItems

它在 MySQL 中運行良好,我認為優化器更容易理解這一點。

編輯:顯然我誤讀了 MSSQL 和 MySQL - 對此很抱歉,但也許無論如何它都有幫助。

我已經使用了這種方法並且它對我有用。

SELECT COUNT(DISTINCT DocumentID || DocumentSessionId) 
FROM  DocumentOutputItems

就我而言,它提供了正確的結果。

希望這行得通,我正在 prima vista 上寫作

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

您的查詢沒有任何問題,但您也可以這樣做:

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

如果您只有一個字段“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,您就會遇到問題。 我只是堅持原始查詢。

當我搜索自己的問題時,我發現了這一點,發現如果計算 DISTINCT 對象,則會返回正確的數字(我正在使用 MySQL)

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

我希望 MS SQL 也可以做類似 COUNT(DISTINCT A, B) 的事情。 但它不能。

起初,在一些測試 CHECKSUM() 未能創建唯一值之后,JayTee 的回答對我來說似乎是一個解決方案。 一個簡單的例子是,CHECKSUM(31,467,519) 和 CHECKSUM(69,1120,823) 給出了相同的答案,即 55。

然后我做了一些研究,發現 Microsoft 不建議將 CHECKSUM 用於更改檢測目的。 在某些論壇中,有些人建議使用

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

但這也不舒服。

您可以按照TSQL CHECKSUM conundrum 中的建議使用 HASHBYTES() 函數。 然而,這也有可能不返回唯一結果。

我建議使用

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

這個怎么樣,

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

這將使我們獲得 DocumentId 和 DocumentSessionId 的所有可能組合的計數

如果您正在使用固定長度的數據類型,您可以轉換為binary以非常輕松快速地完成此操作。 假設DocumentIdDocumentSessionId都是int ,因此長度為 4 個字節...

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

我的具體問題要求我將SUM除以各種外鍵和日期字段的不同組合的COUNT ,按另一個外鍵分組,偶爾按某些值或鍵過濾。 表非常大,使用子查詢大大增加了查詢時間。 由於復雜性,統計數據根本不是一個可行的選擇。 CHECKSUM解決方案的轉換速度也太慢,特別是由於數據類型多種多樣,我不能冒險它的不可靠性。

但是,使用上述解決方案幾乎沒有增加查詢時間(與僅使用SUM ),並且應該是完全可靠的! 它應該能夠幫助處於類似情況的其他人,所以我將其發布在這里。

這個對我有用。 在甲骨文中:

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 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...

這就是最終的工作

此代碼在 2 個參數上使用 distinct 並提供特定於這些不同值行計數的行數計數。 它在 MySQL 中對我很有用。

select DISTINCT DocumentId as i,  DocumentSessionId as s , count(*) 
from DocumentOutputItems   
group by i ,s;
select count(*) from (
    select p.weight, p.price  
    from products p 
    group by p.weight, p.price 
) as list

您可以只使用兩次計數功能。

在這種情況下,它將是:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM