2009-09-24 40 views
133

¿Hay una mejor manera de hacer una consulta como esta:distinta sobre varias columnas

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

Necesito contar el número de elementos distintos de esta mesa, pero la diferencia es más de dos columnas.

Mi consulta funciona bien, pero me preguntaba si puedo conseguir el resultado final utilizando una sola consulta (sin utilizar un sub-consulta)

+0

IordanTanev, Mark Brackett, RC - gracias por las respuestas, fue un buen intento, pero debes comprobar lo que haces antes de publicar en SO. Las consultas que proporcionó no son equivalentes a mi consulta. Puede ver fácilmente que siempre tengo un resultado escalar pero su consulta devuelve varias filas. – Novitzky

+0

Acabo de actualizar la pregunta para incluir su comentario aclaratorio en una de las respuestas – Jeff

Respuesta

45

Si usted está tratando de mejorar el rendimiento, puede intentar crear una columna calculada PERSISTED a ambos un hash o el valor concatenado de las dos columnas

Una vez que se conserva, siempre que la columna es determinista y se está utilizando la configuración de base de datos "sano", que pueden ser indexados y/o estadísticas se puede crear en él.

que creen una cuenta distinta de la columna calculada sería equivalente a la consulta.

+3

¡Excelente sugerencia! Cuanto más Leí, más me doy cuenta de que SQL no se trata solo de conocer la sintaxis y las funciones y más acerca de la aplicación de la lógica pura ... ¡Desearía tener 2 votos favorables! – tumchaaditya

+0

Muy buena sugerencia. Me evitó escribir código innecesario en esto. –

2

Esperanza funciona esto que estoy escribiendo sobre prima vista

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

Para que esta pueda dar la respuesta final, deberá envolverla en otro SELECT COUNT (*) FROM (...). Esencialmente, esta respuesta simplemente le brinda otra forma de enumerar los distintos valores que desea contar. No es mejor que tu solución original. –

+0

Gracias Dave. Sé que puedes usar group by en lugar de distinct en mi caso. Me preguntaba si obtendrías el resultado final con solo una consulta. Creo que es imposible, pero podría estar equivocado. – Novitzky

16

¿Qué pasa con su consulta existente que no le gusta? Si le preocupa que DISTINCT en dos columnas no devuelva solo las permutaciones únicas, ¿por qué no intentarlo?

Sin duda funciona como es de esperar en 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> 

edición

Fui un callejón sin salida con la analítica, pero la respuesta fue obvia deprimente ...

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

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

SQL> 

edición 2

Teniendo en cuenta los siguientes datos la solución de concatenación provista arriba contradirá:

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

Así que para incluir un separador ...

select col1 + '*' + col2 from t23 
/

Obviamente el separador elegido debe ser un carácter o conjunto de caracteres, que nunca pueden aparecer en cualquiera de las columnas.

+0

+1 de mí. Gracias por tu respuesta. Mi consulta funciona bien pero me preguntaba si puedo obtener el resultado final con solo una consulta (sin usar una subconsulta) – Novitzky

11

¿Qué tal algo como:

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

Probablemente sólo hace lo mismo que ya se encuentra sin embargo pero evita los distintos.

+0

Sí, tiene razón. Hace el mismo trabajo que mi original. – Novitzky

+0

en mis pruebas (usando SET SHOWPLAN_ALL ON), tenía el mismo plan de ejecución y exactamente el mismo TotalSubtreeCost –

+0

+1 para un buen intento y la explicación. – Novitzky

6

Aquí hay una versión más corta y sin la subselección:

SELECT COUNT(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems 

Funciona bien en MySQL, y creo que el optimizador tiene un tiempo más fácil la comprensión de éste.

Editar: Al parecer, he leído mal MSSQL y MySQL, disculpa por eso, pero tal vez ayude de todos modos.

+5

en SQL Server obtienes: __Msg 102, nivel 15, estado 1, línea 1 Sintaxis incorrecta cerca de ',' .__ –

+0

Esto es lo que estaba pensando. Quiero hacer algo similar en MSSQL si es posible. – Novitzky

+0

@Kamil Nowicki, en SQL Server, solo puede tener un campo en un COUNT(), en mi respuesta le muestro que puede concatenar los dos campos en uno y probar este enfoque. Sin embargo, me quedaría con el original ya que los planes de consulta terminarían igual. –

2

si tuviera sólo un campo de "distinta", podría utilizar:

SELECT COUNT(DISTINCT DocumentId) 
FROM DocumentOutputItems 

y que hace volver el mismo plan de consulta que el original, como puede comprobarse con SET SHOWPLAN_ALL ON. Sin embargo está utilizando dos campos de lo que podría intentar algo loco como:

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

pero vas a tener problemas si se trata de valores NULL.Me quedaría con la consulta original.

+0

+1 por mi parte. Gracias pero me quedaré con mi consulta tal como sugirió. Usar "convertir" puede disminuir el rendimiento aún más. – Novitzky

3

No hay nada de malo en la consulta, pero también podría hacerlo de esta manera:

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

Editar: Alteración de la menor a la suma de comprobación fiable de sólo consulta he descubierto una manera de hacer esto (en SQL Server 2005) que funciona bastante bien para mí y que puedo utilizar tantas columnas como necesito (añadiéndolos a la función SUMA dE CONTROL()). La función de inversión() convierte los enteros en varchars para hacer la diferencia más fiable

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

+ 1 Bueno, funciona perfecto (cuando tienes los tipos de columna correctos para realizar un CheckSum en ...;) –

+7

Con hashes como Checksum(), hay pocas posibilidades de que se devuelva el mismo hash para diferentes entradas, por lo que el recuento puede ser muy leve. HashBytes() es una posibilidad aún menor pero aún no es cero. Si esos dos Ids fueran int (32b), entonces un "hash sin pérdidas" podría combinarlos en un bigint (64b) como Id1 << 32 + Id2. – crokusek

+1

la posibilidad no es tan pequeña incluso, especialmente cuando comienzas a combinar columnas (que es para lo que se suponía que estaba destinado). Tenía curiosidad sobre este enfoque y, en un caso particular, la suma de verificación terminó con un conteo 10% más pequeño. Si lo piensas un poco más, Checksum simplemente devuelve un int, por lo que si tuvieras un rango completo de bigint, terminarás con un recuento distinto unos 2 mil millones de veces más pequeño de lo que realmente es. -1 – pvolders

1

Deseo MS SQL también podría hacer algo como COUNT (DISTINCT A, B). Pero no puede.

En respuesta primera de JayTee parecía una solución a mi bu Después de algunas pruebas CHECKSUM() no pudo crear valores únicos. Un ejemplo rápido es que tanto CHECKSUM (31,467,519) como CHECKSUM (69,1120,823) dan la misma respuesta que es 55.

Luego realicé algunas investigaciones y descubrí que Microsoft NO recomienda el uso de CHECKSUM para fines de detección de cambios. En algunos foros de algunos sugirieron el uso de

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

pero esto tampoco es conforting.

Puede utilizar la función HASHBYTES() como se sugiere en TSQL CHECKSUM conundrum. Sin embargo, esto también tiene una pequeña posibilidad de no devolver resultados únicos.

se recomienda usar

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

me encontré con esto cuando busqué en Google para mi propio tema, encontré que si se cuentan objetos distintos, se obtiene el número correcto devuelto (estoy usando MySQL)

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

La consulta anterior arrojará un conjunto de resultados diferente al que estaba buscando el OP (las distintas combinaciones ** ** de 'DocumentId' y ' DocumentSessionId'). Alexander Kjäll ya publicó la respuesta correcta si el OP estaba usando MySQL y no MS SQL Server. –

4

para ejecutarse como una sola consulta, concatenar las columnas, a continuación, obtener el recuento diferenciado de las instancias de la cadena concatenada.

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

En MySQL puede hacer lo mismo sin la etapa de concatenación de la siguiente manera:

SELECT count(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems; 

Esta característica se menciona en la documentación de MySQL:

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

+0

Esta era una pregunta de SQL Server, y las dos opciones que publicó ya se han mencionado en las siguientes respuestas a esta pregunta: http://stackoverflow.com/a/1471444/4955425 y http://stackoverflow.com/a/1471713/4955425. – sstan

-2

Esto se planteó y respondió en Quora (https://www.quora.com/In-SQL-how-to-I-count-DISTINCT-over-multiple-columns):

select col1, col2, col3, count(*) 
from table 
group by col1, col2, col3 

Estaba trabajando en esto en SAS, y SAS Proc SQL no le gusta DISTINCT con más de una columna.

+0

La consulta original en la pregunta devuelve el número de combinaciones en columnas determinadas. Esta respuesta en cambio devuelve el número de ocurrencias para cada combinación en columnas dadas. – jumxozizi

-2

Esta consulta continuación trabajó para mí en MySQL:

SELECT COUNT(DISTINCT col_1,col_2,..) from table_name; 

Las columnas que figuran en el anterior a saber consulta. col_1, col_2 tiene una restricción combinada UNIQUE en ellos. Eso significa que en mi tabla table_name, he creado un índice UNIQUE en col_1 + col_2.