複数の列にわたるDISTINCTのカウント


212

このようなクエリを実行するより良い方法はありますか?

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

この表の個別のアイテムの数を数える必要がありますが、個別のアイテムは2列以上です。

私のクエリは正常に機能しますが、1つのクエリのみを使用して(サブクエリを使用せずに)最終結果を取得できるかどうか疑問に思いました


IordanTanev、RC、Mark Brackett-返信ありがとうございます。試してみましたが、SOに投稿する前に何をしているかを確認する必要があります。あなたが提供したクエリは私のクエリと同等ではありません。私は常にスカラーの結果を持っていますが、クエリは複数の行を返します。
Novitzky、2009

回答の1つからの明確なコメントを含むように質問を更新しました
Jeff


これは良い質問です。これを行う簡単な方法があるかどうかも疑問に思いました
Anupam

回答:


73

パフォーマンスを改善しようとしている場合は、2つの列のハッシュ値または連結値のいずれかに永続的な計算列を作成してみてください。

列が確定的で、「正常な」データベース設定を使用している場合、それが永続化されると、インデックスを作成したり、統計を作成したりできます。

計算された列の個別のカウントは、クエリに相当すると思います。


4
素晴らしい提案!読むほど、SQLは構文と関数を理解することよりも、純粋なロジックを適用することについてより多くのことを理解するようになります。
tumchaaditya 2013年

あまりにも良い提案。これに不要なコードを書くのを避けました。
Avrajit Roy 2016

1
これが何を意味し、どのようにそれを行うかについてより多くを示すために、例またはコードサンプルを追加していただけませんか?
ジェイキ

52

編集:信頼性が低いチェックサムのみのクエリ から変更しました(SQL Server 2005で)これを行う方法を発見しました。これは私にとって非常にうまく機能し、必要なだけ列を使用できます(追加することで) CHECKSUM()関数)。REVERSE()関数は、intをvarcharに変換して、区別をより信頼できるものにします

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

1
+1良いもの、完璧に動作(CheckSumを実行するための適切な列タイプがある場合;)
ベルヌーイIT

8
Checksum()のようなハッシュでは、異なる入力に対して同じハッシュが返される可能性が低いため、カウントがわずかにずれる場合があります。HashBytes()はさらに小さなチャンスですが、まだゼロではありません。これらの2つのIdがint(32b)の場合、「ロスレスハッシュ」はId1 << 32 + Id2のようなbigint(64b)にそれらを組み合わせることができます。
crokusek 2014年

1
特に列の結合を開始するときは、その可能性はそれほど小さくありません(それが想定されていたものです)。私はこのアプローチに興味があり、特定のケースではチェックサムのカウントが10%小さくなりました。少し長く考えると、チェックサムはintを返すだけなので、チェックサムを完全に大きな範囲にすると、実際の数よりも20億倍も小さい明確なカウントになります。-1
pvolders 2014

重複の可能性を排除するために "REVERSE"の使用を含むようにクエリを更新
JayTee

4
チェックサムを回避できますか?2つの値を連結するだけでいいですか?同じことを考えるとリスクがあると思います(( 'he'、 'art')== 'hear'、 't')。しかし、@ APCが提案するように区切り文字で解決できると思います(どちらの列にも表示されない値)ので、 'he | art'!= 'hear | t'単純な「連結」には他の問題がありますか?アプローチ?
Red Pea

31

既存のクエリについて、気に入らない点は何ですか?DISTINCT2つの列にまたがって一意の順列が返されないことが心配な場合は、試してみませんか?

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
/

選択されたセパレータは、文字または文字のセットでなければならず、どちらの列にも表示されることはありません。


私からの+1。ご回答有難うございます。私のクエリは正常に動作しますが、1つのクエリだけを使用して(サブクエリを使用せずに)最終結果を取得できるかどうか疑問に思いました
Novitzky

19

単一のクエリとして実行するには、列を連結してから、連結された文字列のインスタンスの個別の数を取得します。

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


これはSQL Serverの質問であり、投稿した両方のオプションは、この質問に対する次の回答で既に言及されています:stackoverflow.com/a/1471444/4955425およびstackoverflow.com/a/1471713/4955425
スタン、2016

1
FWIW、これはほとんどPostgreSQLで機能します。余分な括弧が必要です:SELECT COUNT(DISTINCT (DocumentId, DocumentSessionId)) FROM DocumentOutputItems;
ijoseph '25年

14

次のようなものはどうですか:

選択数(*)
から
  (select count(*)cnt
   DocumentOutputItemsから
   DocumentId、DocumentSessionIdによるグループ化)t1

おそらくあなたはすでにあなたと同じことをしますが、それはDISTINCTを避けます。


私のテストでは(SET SHOWPLAN_ALL ONを使用)、同じ実行プランとまったく同じTotalSubtreeCost
KMがありました。

1
元のクエリの複雑さに応じて、これを解決GROUP BYすると、クエリ変換にいくつかの追加の課題が生じ、目的の出力が得られます(たとえば、元のクエリにすでにGROUP BYor HAVING句がある場合)
Lukas Eder

8

副選択なしの短いバージョンを次に示します。

SELECT COUNT(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems

これはMySQLで正常に機能し、オプティマイザがこれを理解するのが簡単になると思います。

編集:どうやら私はMSSQLとMySQLを誤って読みました-申し訳ありませんが、多分それはとにかく役立つでしょう。


6
SQL Server では、
和基。

これは私が考えていたものです。可能であれば、MSSQLでも同様の処理を行います。
Novitzky、2009

@Kamil Nowicki、SQL Serverでは、COUNT()に含めることができるフィールドは1つだけです。私の回答では、2つのフィールドを1つに連結して、このアプローチを試すことができることを示しています。ただし、クエリプランは同じになるので、元のバージョンをそのまま使用します。
和基。

1
@JayTeeの回答をご覧ください。それは魅力のように働きます。count ( distinct CHECKSUM ([Field1], [Field2])
Custodio 2012年

5

多くの(ほとんど?)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
Vytenis Bivainis 2018年

@VytenisBivainis私はPostgreSQLが知っている-どのバージョンからかはわからない。
karmakaze 2018年

3

クエリには何も問題はありませんが、次のように行うこともできます。

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

3

これがうまくいくことを願って、私はプリマビスタに書いています

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

7
これで最終的な回答が得られるようにするには、別のSELECT COUNT(*)FROM(...)でラップする必要があります。基本的に、この答えは、カウントする個別の値をリストする別の方法を提供するだけです。元のソリューションと同じです。
デイブ・コスタ

デイブ、ありがとう。私の場合は、distinctの代わりにgroup byを使用できます。1つのクエリだけを使用して最終結果が得られるかどうか疑問に思っていました。私は不可能だと思うが間違っているかもしれない。
ノビツキー、

3

私はこのアプローチを使用し、それが私のために働いています。

SELECT COUNT(DISTINCT DocumentID || DocumentSessionId) 
FROM  DocumentOutputItems

私の場合、それは正しい結果を提供します。


2つの列を組み合わせた個別の値の数は表示されません。少なくともMySQL 5.8ではできません。
アンワールシェイク

この質問にはSQL Serverのタグが付けられていますが、これはSQL Serverの構文ではありません
Tab Alleman '21

2

「DISTINCT」へのフィールドが1つしかない場合は、次のように使用できます。

SELECT COUNT(DISTINCT DocumentId) 
FROM DocumentOutputItems

また、SET SHOWPLAN_ALL ONでテストすると、元のクエリプランと同じクエリプランが返されます。ただし、2つのフィールドを使用しているため、次のようなおかしなことを試すことができます。

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

ただし、NULLが関係している場合は問題が発生します。元のクエリをそのまま使用します。


私からの+1。ありがとうございましたが、あなたが提案したように私は私のクエリを使い続けます。「変換」を使用すると、パフォーマンスがさらに低下する可能性があります。
Novitzky、2009

2

私は自分の問題をグーグル検索したときにこれを見つけました、DISTINCTオブジェクトをカウントすると、正しい数が返されることがわかりました(私はMySQLを使用しています)

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

5
上記のクエリは、OPが(個別の探していたものよりも、結果の異なるセットを返します。組み合わせDocumentIdDocumentSessionId)。OPがMySQLを使用していて、MS SQL Serverを使用していない場合、AlexanderKjällはすでに正しい回答を投稿しています。
Anthony Geoghegan 2014

1

MS SQLがCOUNT(DISTINCT A、B)のようなこともできるといいのですが。しかし、それはできません。

いくつかのテストCHECKSUM()が一意の値を作成できなかったため、最初はJayTeeの答えが解決策のように見えました。簡単な例として、CHECKSUM(31,467,519)とCHECKSUM(69,1120,823)はどちらも55である同じ答えを返します。

その後、調査を行ったところ、変更検出の目的でチェックサムを使用することをマイクロソフトが推奨していないことがわかりました。一部のフォーラムでは、いくつかの使用を提案

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

しかし、これも快適ではありません。

TSQL CHECKSUMの難問で提案されているように、HASHBYTES()関数を使用できます。ただし、これでも一意の結果が返されない可能性はわずかです。

私は使用をお勧めします

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

1

これはどう、

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

これにより、DocumentIdとDocumentSessionIdのすべての可能な組み合わせの数が取得されます


0

わたしにはできる。オラクルでは:

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;

0

同様の質問がありましたが、私が持っていたクエリは、メインクエリの比較データを持つサブクエリでした。何かのようなもの:

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

これが最終的に機能したものです


0

固定長のデータ型を使用している場合は、にキャストしbinaryて、これを非常に簡単かつ迅速に行うことができます。との両方がsであり、したがって4バイト長であると仮定DocumentIdします...DocumentSessionIdint

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

私の特定の問題は、分割する私を必要とSUMすることによりCOUNT、別の外部キーによってグループ化し、時折、特定の値またはキーによってフィルタリング、様々な外部キーの異なる組み合わせおよび日付フィールドの。テーブルは非常に大きく、サブクエリを使用するとクエリ時間が大幅に増加しました。そして、その複雑さのために、統計は実行可能な選択肢ではありませんでした。CHECKSUM解決策は、特に、さまざまなデータ型の結果として、その変換にあまりにも遅いでもあった、と私はその信頼性の欠如を危険にさらすことができませんでした。

ただし、上記のソリューションを使用しても、クエリ時間はほとんど増加せず(単にを使用する場合と比較してSUM)、完全に信頼できるはずです。同様の状況で他の人を助けることができるはずなので、ここに投稿します。


-1

カウント関数を2回使用できます。

この場合は、次のようになります。

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

これは質問で必要なようには機能しません。各列の個別をカウントします
naviram

-1

このコードは、distinct on 2パラメーターを使用し、これらの個別の値の行数に固有の行数のカウントを提供します。それはMySQLで魅力的に機能しました。

select DISTINCT DocumentId as i,  DocumentSessionId as s , count(*) 
from DocumentOutputItems   
group by i ,s;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.