Servidor SQL equivalente de una función agregada COUNTIF

164

Estoy creando una consulta con una GROUP BYcláusula que necesita la capacidad de contar registros basados ​​solo en una determinada condición (por ejemplo, contar solo registros donde un determinado valor de columna es igual a 1).

SELECT  UID, 
        COUNT(UID) AS TotalRecords, 
        SUM(ContractDollars) AS ContractDollars,
        (COUNTIF(MyColumn, 1) / COUNT(UID) * 100) -- Get the average of all records that are 1
FROM    dbo.AD_CurrentView
GROUP BY UID
HAVING  SUM(ContractDollars) >= 500000

La COUNTIF()línea obviamente falla ya que no se llama a una función SQL nativa COUNTIF, pero la idea aquí es determinar el porcentaje de todas las filas que tienen el valor '1' para MyColumn.

¿Alguna idea sobre cómo implementar esto correctamente en un entorno MS SQL 2005?

senfo
fuente

Respuestas:

339

Puede usar un SUM( COUNT¡ no !) Combinado con una CASEdeclaración, como esta:

SELECT SUM(CASE WHEN myColumn=1 THEN 1 ELSE 0 END)
FROM AD_CurrentView

Nota: en mis propias pruebas NULL, no fueron un problema, aunque esto puede depender del entorno. Puede manejar nulos como:

SELECT SUM(CASE WHEN ISNULL(myColumn,0)=1 THEN 1 ELSE 0 END)
FROM AD_CurrentView
JoshBerke
fuente
3
(Sé que el OP preguntó acerca de MS SQL, pero solo un pequeño comentario para los usuarios de SQLite que hacen lo mismo) SQLite no tiene ISNULL, en cambio puede hacer CASE WHEN myColumn IS NULLo usar ifnull( stackoverflow.com/a/799406/1861346 )
Matt
54

Usualmente hago lo que Josh me recomendó, pero hice una lluvia de ideas y probé una alternativa un poco tonta que me apetecía compartir.

Puede aprovechar el hecho de que COUNT (ColumnName) no cuenta NULL y usar algo como esto:

SELECT COUNT(NULLIF(0, myColumn))
FROM AD_CurrentView

NULLIF: devuelve NULL si los dos valores pasados ​​son iguales.

Ventaja: expresa su intención de COUNT filas en lugar de tener la notación SUM (). Desventaja: no está tan claro cómo funciona ("magia" suele ser malo).

Chris Shaffer
fuente
2
Esta solución puede dar respuestas diferentes a la suma cuando un grupo solo contiene valores nulos, resulta en 1 en lugar de 0.
KimvdLinde
Publicación anterior, pero gracias esto ayudó. Extendí la magia y llegué alrededor de la "única NULLS" problema mediante la adición ISNULLde la siguiente manera: SELECT COUNT(NULLIF(0, ISNULL(myColumn, 0))). Espera, eso se ve feo ...
pcdev
1
Sería perfecto si hubiera una función
NULLIFNOT
21

Yo usaría esta sintaxis. Logra lo mismo que las sugerencias de Josh y Chris, pero con la ventaja de que cumple con ANSI y no está vinculado a un proveedor de base de datos en particular.

select count(case when myColumn = 1 then 1 else null end)
from   AD_CurrentView
asgeo1
fuente
2
La respuesta de Chris es compatible con el estándar SQL (pista: NULLIFse incluye el estándar SQL-92). La respuesta de Josh se puede transformar fácilmente en SQL estándar reemplazándola isnullpor COALESCE.
cuando el
En realidad, me gusta más esta respuesta, porque tiene la idea de "contar filas" que Chris estaba mostrando, pero es más extensible, ya que puede usar cualquier operador de comparación; no sólo =. Lo estoy usando para "contar el número de respuestas> = 2".
Kristen Hammack
3

Agregando a la respuesta de Josh,

SELECT COUNT(CASE WHEN myColumn=1 THEN AD_CurrentView.PrimaryKeyColumn ELSE NULL END)
FROM AD_CurrentView

Me funcionó bien (en SQL Server 2012) sin cambiar el 'conteo' a una 'suma' y la misma lógica es portable a otros 'agregados condicionales'. Por ejemplo, suma basada en una condición:

SELECT SUM(CASE WHEN myColumn=1 THEN AD_CurrentView.NumberColumn ELSE 0 END)
FROM AD_CurrentView
Sturgus
fuente
2

Qué tal si

SELECT id, COUNT(IF status=42 THEN 1 ENDIF) AS cnt
FROM table
GROUP BY table

Más corto que CASE:)

Funciona porque COUNT()no cuenta valores nulos y IF/ CASEreturn null cuando no se cumple la condición y no hay ELSE.

Creo que es mejor que usar SUM().

maf-soft
fuente
1

No es específico del producto, pero el estándar SQL proporciona

SELECT COUNT() FILTER WHERE <condition-1>, COUNT() FILTER WHERE <condition-2>, ... FROM ...

para este propósito. O algo que se parece mucho a eso, no lo sé.

Y, por supuesto, los proveedores preferirán seguir con sus soluciones patentadas.

Erwin Smout
fuente
1
Nunca había oído hablar de esto antes, así que lo busqué. Según modern-sql.com/feature/filter, el único DBMS principal que realmente ofrece la FILTERcláusula es PostgreSQL, pero está emulado CASEen todos ellos.
Kristen Hammack
1

¿Por qué no así?

SELECT count(1)
FROM AD_CurrentView
WHERE myColumn=1
Michal
fuente
1
Porque necesita mucho más que solo el recuento. Está tratando de obtener el recuento de filas de una parte de un grupo, y luego un agregado de todo el grupo, lo que no se puede hacer con un DONDE.
Kristen Hammack
1

Tuve que usar COUNTIF () en mi caso como parte de mis columnas SELECT Y para imitar un% del número de veces que cada elemento apareció en mis resultados.

Así que usé esto ...

SELECT COL1, COL2, ... ETC
       (1 / SELECT a.vcount 
            FROM (SELECT vm2.visit_id, count(*) AS vcount 
                  FROM dbo.visitmanifests AS vm2 
                  WHERE vm2.inactive = 0 AND vm2.visit_id = vm.Visit_ID 
                  GROUP BY vm2.visit_id) AS a)) AS [No of Visits],
       COL xyz
FROM etc etc

Por supuesto, deberá formatear el resultado de acuerdo con sus requisitos de visualización.

Fandango68
fuente
-2
SELECT COALESCE(IF(myColumn = 1,COUNT(DISTINCT NumberColumn),NULL),0) column1,
COALESCE(CASE WHEN myColumn = 1 THEN COUNT(DISTINCT NumberColumn) ELSE NULL END,0) AS column2
FROM AD_CurrentView
Andres Sarria
fuente