webentwicklung-frage-antwort-db.com.de

DISTINCT wird über mehrere Spalten gezählt

Gibt es eine bessere Möglichkeit, eine Abfrage wie folgt durchzuführen:

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

Ich muss die Anzahl der verschiedenen Elemente aus dieser Tabelle zählen, aber das eindeutige Element besteht aus zwei Spalten.

Meine Abfrage funktioniert gut, aber ich habe mich gefragt, ob ich das Endergebnis mit nur einer Abfrage erhalten kann (ohne eine Unterabfrage).

154
Novitzky

Wenn Sie versuchen, die Leistung zu verbessern, können Sie versuchen, eine persistente berechnete Spalte für einen Hashwert oder einen verketteten Wert der beiden Spalten zu erstellen.

Wenn die Spalte dauerhaft ist und die Datenbank deterministisch ist und Sie "normale" Datenbankeinstellungen verwenden, kann sie indiziert werden und/oder Statistiken können erstellt werden. 

Ich glaube, dass eine bestimmte Anzahl der berechneten Spalte Ihrer Abfrage entspricht.

55
JasonHorner

Bearbeiten: Von der weniger als zuverlässigen Prüfsummen-Abfrage geändert Ich habe einen Weg gefunden (SQL Server 2005), der für mich ziemlich gut funktioniert, und ich kann so viele Spalten wie verwenden Ich brauche (indem ich sie zur CHECKSUM () - Funktion hinzufüge). Die REVERSE () - Funktion wandelt die Ints in Varchars um, um die Unterscheidung zuverlässiger zu machen

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

Was gefällt Ihnen an Ihrer bestehenden Abfrage nicht? Wenn Sie befürchten, dass DISTINCT in zwei Spalten nicht nur die eindeutigen Permutationen zurückgibt, warum versuchen Sie es nicht? 

Es funktioniert sicherlich so, wie Sie es von Oracle erwarten.

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>

bearbeiten

Ich ging mit einer Analyse in eine Sackgasse, aber die Antwort war deprimierend offensichtlich ...

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

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

SQL>

edit 2

Aufgrund der folgenden Daten wird die oben angegebene Verkettungslösung falsch berechnet:

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

Also fügen wir ein Trennzeichen ein ...

select col1 + '*' + col2 from t23
/

Offensichtlich muss das gewählte Trennzeichen ein Zeichen oder eine Gruppe von Zeichen sein, die niemals in einer der Spalten angezeigt werden können. 

22
APC

Wie wäre es mit etwas wie:

 Anzahl (*) auswählen 
 aus 
 (Wählen Sie count (*) cnt 
 aus der DocumentOutputItems 
-Gruppe über DocumentId, DocumentSessionId aus.) t1 

Wahrscheinlich macht es nur das Gleiche wie Sie, aber das DISTINCT wird vermieden.

14
Trevor Tippins

Verketten Sie die Spalten, um sie als einzelne Abfrage auszuführen, und ermitteln Sie dann die eindeutige Anzahl der Instanzen der verketteten Zeichenfolge.

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

In MySQL können Sie dasselbe tun, ohne den Verkettungsschritt wie folgt auszuführen:

SELECT count(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems;

Diese Funktion wird in der MySQL-Dokumentation erwähnt:

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

9
spelunk1

Hier ist eine kürzere Version ohne die Unterauswahl:

SELECT COUNT(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems

Es funktioniert gut in MySQL, und ich denke, dass der Optimierer es einfacher hat, diesen zu verstehen.

Edit: Anscheinend habe ich MSSQL und MySQL falsch gelesen - sorry, aber vielleicht hilft es trotzdem.

7

Ich habe dies gefunden, als ich für mein eigenes Problem gegoogelt habe und festgestellt habe, dass, wenn Sie DISTINCT-Objekte zählen, die richtige Anzahl zurückgegeben wird (ich verwende MySQL).

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

Es gibt nichts falsches an Ihrer Anfrage, aber Sie könnten es auch so machen:

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

Ich hoffe, das funktioniert, ich schreibe auf Prima Vista

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

wenn Sie nur ein Feld für "DISTINCT" hätten, könnten Sie Folgendes verwenden:

SELECT COUNT(DISTINCT DocumentId) 
FROM DocumentOutputItems

und das gibt denselben Abfrageplan wie das Original zurück, wie mit SET SHOWPLAN_ALL ON getestet. Sie verwenden jedoch zwei Felder, um etwas Verrücktes auszuprobieren:

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

sie haben jedoch Probleme, wenn NULLs betroffen sind. Ich würde einfach bei der ursprünglichen Anfrage bleiben.

2
KM.

Viele (die meisten?) SQL-Datenbanken können mit Tupeln wie Werten arbeiten, sodass Sie Folgendes tun können: SELECT COUNT(DISTINCT (DocumentId, DocumentSessionId)) FROM DocumentOutputItems; .__ Wenn Ihre Datenbank dies nicht unterstützt, kann sie gemäß dem Vorschlag von @ oncel-umut-turer von CHECKSUM simuliert werden oder eine andere Skalarfunktion, die eine gute Eindeutigkeit bietet, zB COUNT(DISTINCT CONCAT(DocumentId, ':', DocumentSessionId)).

Eine verwandte Verwendung von Tupeln führt IN-Abfragen aus, wie zum Beispiel: SELECT * FROM DocumentOutputItems WHERE (DocumentId, DocumentSessionId) in (('a', '1'), ('b', '2'));

2
karmakaze

Ich wünschte, MS SQL könnte auch etwas wie COUNT (DISTINCT A, B) tun. Aber das geht nicht.

Zunächst schien mir JayTees Antwort eine Lösung zu sein, aber nach einigen Tests konnte CHECKSUM () keine eindeutigen Werte erstellen. Ein schnelles Beispiel ist, dass sowohl CHECKSUM (31.467.519) als auch CHECKSUM (69,1120,823) dieselbe Antwort geben, nämlich 55.

Dann habe ich einige Nachforschungen angestellt und festgestellt, dass Microsoft die Verwendung von CHECKSUM zu Änderungszwecken NICHT empfiehlt. In einigen Foren haben einige die Verwendung vorgeschlagen 

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

das ist aber auch nicht angenehm.

Sie können die HASHBYTES () - Funktion wie in TSQL CHECKSUM Conundrum vorgeschlagen verwenden. Dies hat jedoch auch eine geringe Chance, dass keine eindeutigen Ergebnisse zurückgegeben werden.

Ich würde vorschlagen, verwenden

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

Ich hatte eine ähnliche Frage, aber die Abfrage, die ich hatte, war eine Unterabfrage mit den Vergleichsdaten in der Hauptabfrage. so etwas wie:

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

ich ignorierte die Komplexität dieses Problems und stellte fest, dass ich mit der in der ursprünglichen Frage beschriebenen doppelten Unterabfrage den Wert von a.code nicht in die Unterabfrage bekommen konnte

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

Irgendwann habe ich herausgefunden, dass ich schummeln und die Spalten kombinieren kann:

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

Dies ist, was am Ende funktioniert hat

0
Mark Rogers

Sie können die Zählfunktion einfach zweimal verwenden.

In diesem Fall wäre es:

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

Wie wäre es damit,

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

Dadurch erhalten Sie die Anzahl aller möglichen Kombinationen von DocumentId und DocumentSessionId

0
Nikhil Singh

Ich habe diesen Ansatz verwendet und es hat für mich funktioniert.

SELECT COUNT(DISTINCT DocumentID || DocumentSessionId) 
FROM  DocumentOutputItems

Für meinen Fall liefert es das richtige Ergebnis.

0
Jaanis Veinberg

Für mich geht das. In Oracle:

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

In jpql:

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