Según MSDN , Median no está disponible como una función agregada en Transact-SQL. Sin embargo, me gustaría saber si es posible crear esta funcionalidad (usando la función Crear Agregado , la función definida por el usuario o algún otro método).
¿Cuál sería la mejor manera (si es posible) de hacer esto: permitir el cálculo de un valor medio (suponiendo un tipo de datos numéricos) en una consulta agregada?
sql
sql-server
aggregate-functions
median
Yaakov Ellis
fuente
fuente
Respuestas:
ACTUALIZACIÓN 2019: en los 10 años desde que escribí esta respuesta, se han descubierto más soluciones que pueden arrojar mejores resultados. Además, las versiones de SQL Server desde entonces (especialmente SQL 2012) han introducido nuevas características de T-SQL que pueden usarse para calcular medianas. Las versiones de SQL Server también han mejorado su optimizador de consultas que puede afectar el rendimiento de varias soluciones medianas. Net-net, mi publicación original de 2009 todavía está bien, pero puede haber mejores soluciones para las aplicaciones modernas de SQL Server. Eche un vistazo a este artículo de 2012, que es un gran recurso: https://sqlperformance.com/2012/08/t-sql-queries/median
Este artículo encontró que el siguiente patrón es mucho, mucho más rápido que todas las demás alternativas, al menos en el esquema simple que probaron. Esta solución fue 373 veces más rápida (!!!) que la
PERCENTILE_CONT
solución más lenta ( ) probada. Tenga en cuenta que este truco requiere dos consultas separadas que pueden no ser prácticas en todos los casos. También requiere SQL 2012 o posterior.Por supuesto, solo porque una prueba en un esquema en 2012 arrojó excelentes resultados, su kilometraje puede variar, especialmente si está en SQL Server 2014 o posterior. Si el rendimiento es importante para su cálculo medio, le sugiero encarecidamente que pruebe y pruebe varias de las opciones recomendadas en ese artículo para asegurarse de que ha encontrado la mejor para su esquema.
También sería especialmente cuidadoso al usar la función (nueva en SQL Server 2012)
PERCENTILE_CONT
que se recomienda en una de las otras respuestas a esta pregunta, porque el artículo vinculado anteriormente encontró que esta función incorporada es 373 veces más lenta que la solución más rápida. Es posible que esta disparidad haya mejorado en los últimos 7 años, pero personalmente no usaría esta función en una mesa grande hasta que verifique su rendimiento en comparación con otras soluciones.EL POST ORIGINAL 2009 ES ABAJO:
Hay muchas formas de hacerlo, con un rendimiento que varía drásticamente. Aquí hay una solución particularmente bien optimizada, de Medianas, ROW_NUMBERs y rendimiento . Esta es una solución particularmente óptima cuando se trata de E / S reales generadas durante la ejecución: parece más costoso que otras soluciones, pero en realidad es mucho más rápido.
Esa página también contiene una discusión de otras soluciones y detalles de pruebas de rendimiento. Tenga en cuenta el uso de una columna única como desambigador en caso de que haya varias filas con el mismo valor de la columna mediana.
Al igual que con todos los escenarios de rendimiento de la base de datos, siempre intente probar una solución con datos reales en hardware real; nunca se sabe cuándo un cambio en el optimizador de SQL Server o una peculiaridad en su entorno hará que una solución normalmente más lenta sea más lenta.
fuente
Si está utilizando SQL 2005 o mejor, este es un buen cálculo de mediana simple para una sola columna en una tabla:
fuente
select gid, median(score) from T group by gid
. ¿Necesita una subconsulta correlacionada para eso?En SQL Server 2012 debe usar PERCENTILE_CONT :
Ver también: http://blog.sqlauthority.com/2011/11/20/sql-server-introduction-to-percentile_cont-analytic-functions-introduced-in-sql-server-2012/
fuente
DISTINCT
oGROUPY BY SalesOrderID
? De lo contrario, tendrá muchas filas duplicadas.PERCENTILE_DISC
Mi respuesta rápida original fue:
Esto le dará la mediana y el rango intercuartil de una sola vez. Si realmente solo quieres una fila que sea la mediana, descomenta la cláusula where.
Cuando inserta eso en un plan de explicación, el 60% del trabajo está ordenando los datos que son inevitables al calcular estadísticas dependientes de la posición como esta.
Modifiqué la respuesta para seguir la excelente sugerencia de Robert Ševčík-Robajz en los comentarios a continuación:
Esto debería calcular los valores correctos de mediana y percentil cuando tiene un número par de elementos de datos. Nuevamente, elimine el comentario de la cláusula where final si solo desea la mediana y no toda la distribución del percentil.
fuente
Aun mejor:
¡Del maestro mismo, Itzik Ben-Gan !
fuente
MS SQL Server 2012 (y posterior) tiene la función PERCENTILE_DISC que calcula un percentil específico para los valores ordenados. PERCENTILE_DISC (0.5) calculará la mediana: https://msdn.microsoft.com/en-us/library/hh231327.aspx
fuente
Simple, rápido, preciso.
fuente
Si desea utilizar la función Crear agregado en SQL Server, así es cómo hacerlo. Hacerlo de esta manera tiene la ventaja de poder escribir consultas limpias. Tenga en cuenta que este proceso podría adaptarse para calcular un valor de percentil con bastante facilidad.
Cree un nuevo proyecto de Visual Studio y establezca el marco de destino en .NET 3.5 (esto es para SQL 2008, puede ser diferente en SQL 2012). Luego cree un archivo de clase y ponga el siguiente código, o equivalente en c #:
Luego compílelo y copie el archivo DLL y PDB en su máquina SQL Server y ejecute el siguiente comando en SQL Server:
Luego puede escribir una consulta para calcular la mediana de esta manera: SELECCIONE dbo.Median (Field) FROM Table
fuente
Acabo de encontrar esta página mientras buscaba una solución basada en un conjunto para la mediana. Después de ver algunas de las soluciones aquí, se me ocurrió lo siguiente. La esperanza es ayuda / funciona.
fuente
La siguiente consulta devuelve la mediana de una lista de valores en una columna. No se puede usar como o junto con una función agregada, pero aún se puede usar como una subconsulta con una cláusula WHERE en la selección interna.
SQL Server 2005+:
fuente
Aunque la solución de Justin Grant parece sólida, descubrí que cuando tiene varios valores duplicados dentro de una clave de partición dada, los números de fila para los valores duplicados de ASC terminan fuera de secuencia, por lo que no se alinean correctamente.
Aquí hay un fragmento de mi resultado:
Usé el código de Justin como base para esta solución. Aunque no es tan eficiente dado el uso de múltiples tablas derivadas, resuelve el problema de ordenamiento de filas que encontré. Cualquier mejora sería bienvenida ya que no tengo mucha experiencia en T-SQL.
fuente
El ejemplo de Justin anterior es muy bueno. Pero esa necesidad de la clave primaria debe establecerse muy claramente. He visto ese código en la naturaleza sin la clave y los resultados son malos.
La queja que recibo sobre Percentile_Cont es que no le dará un valor real del conjunto de datos. Para llegar a una "mediana" que sea un valor real del conjunto de datos, utilice Percentile_Disc.
fuente
En un UDF, escriba:
fuente
Hallazgo mediano
Este es el método más simple para encontrar la mediana de un atributo.
fuente
Vea otras soluciones para el cálculo de la mediana en SQL aquí: " Forma simple de calcular la mediana con MySQL " (las soluciones son en su mayoría independientes del proveedor).
fuente
Para una variable / medida continua 'col1' de 'table1'
fuente
Usando el agregado COUNT, primero puede contar cuántas filas hay y almacenar en una variable llamada @cnt. Luego, puede calcular los parámetros para que el filtro OFFSET-FETCH especifique, en función del orden de la cantidad, cuántas filas omitir (valor de compensación) y cuántas filtrar (valor de recuperación).
El número de filas a omitir es (@cnt - 1) / 2. Está claro que para un recuento impar este cálculo es correcto porque primero resta 1 para el valor medio único, antes de dividir por 2.
Esto también funciona correctamente para un recuento par porque la división utilizada en la expresión es división entera; entonces, al restar 1 de un conteo par, te quedas con un valor impar.
Al dividir ese valor impar entre 2, la parte de fracción del resultado (.5) se trunca. El número de filas para recuperar es 2 - (@cnt% 2). La idea es que cuando el recuento es impar, el resultado de la operación del módulo es 1, y debe obtener 1 fila. Cuando el recuento es par, el resultado de la operación del módulo es 0, y debe buscar 2 filas. Al restar el resultado 1 o 0 de la operación de módulo de 2, obtiene el 1 o 2 deseado, respectivamente. Finalmente, para calcular la cantidad mediana, tome las cantidades de uno o dos resultados y aplique un promedio después de convertir el valor entero de entrada a uno numérico de la siguiente manera:
fuente
Quería encontrar una solución por mí mismo, pero mi cerebro tropezó y cayó en el camino. Yo creo que funciona, pero no me pidas que explicarlo por la mañana. :PAGS
fuente
fuente
Esto funciona con SQL 2000:
fuente
Para los novatos como yo que están aprendiendo los conceptos básicos, personalmente considero que este ejemplo es más fácil de seguir, ya que es más fácil entender exactamente qué está sucediendo y de dónde provienen los valores medios ...
Sin embargo, en absoluto asombro de algunos de los códigos anteriores.
fuente
Esta es una respuesta tan simple como se me ocurrió. Funcionó bien con mis datos. Si desea excluir ciertos valores, simplemente agregue una cláusula where a la selección interna.
fuente
La siguiente solución funciona bajo estos supuestos:
Código:
fuente
fuente
Intento con varias alternativas, pero debido a que mis registros de datos tienen valores repetidos, las versiones ROW_NUMBER parecen no ser una opción para mí. Entonces, aquí la consulta que utilicé (una versión con NTILE):
fuente
Sobre la base de la respuesta de Jeff Atwood aquí arriba, es con GROUP BY y una subconsulta correlacionada para obtener la mediana de cada grupo.
fuente
Con frecuencia, es posible que necesitemos calcular la mediana no solo para toda la tabla, sino también para los agregados con respecto a alguna ID. En otras palabras, calcule la mediana de cada ID en nuestra tabla, donde cada ID tiene muchos registros. (basado en la solución editada por @gdoron: buen rendimiento y funciona en muchos SQL)
Espero eso ayude.
fuente
Para su pregunta, Jeff Atwood ya había dado la solución simple y efectiva. Pero, si está buscando un enfoque alternativo para calcular la mediana, el siguiente código SQL lo ayudará.
Si está buscando calcular la mediana en MySQL, este enlace github será útil.
fuente
Esta es la solución más óptima para encontrar medianas que se me ocurra. Los nombres en el ejemplo se basan en el ejemplo de Justin. Asegúrese de que exista un índice para la tabla Sales.SalesOrderHeader con las columnas de índice CustomerId y TotalDue en ese orden.
ACTUALIZAR
No estaba seguro de qué método tiene el mejor rendimiento, así que hice una comparación entre mi método Justin Grants y Jeff Atwoods ejecutando una consulta basada en los tres métodos en un lote y el costo del lote de cada consulta fue:
Sin índice:
Y con indice
Traté de ver qué tan bien se escalan las consultas si tiene un índice creando más datos de alrededor de 14 000 filas por un factor de 2 a 512, lo que significa al final alrededor de 7,2 millones de filas. Tenga en cuenta que me aseguré de que el campo CustomeId fuera único para cada vez que hice una sola copia, por lo que la proporción de filas en comparación con la instancia única de CustomerId se mantuvo constante. Mientras hacía esto, ejecuté ejecuciones donde reconstruí el índice después, y noté que los resultados se estabilizaron en un factor de alrededor de 128 con los datos que tenía para estos valores:
Me preguntaba cómo podría haber afectado el rendimiento al escalar el número de filas pero manteniendo constante el CustomerId único, así que configuré una nueva prueba donde hice exactamente esto. Ahora, en lugar de estabilizarse, la relación de costo del lote se mantuvo divergente, también en lugar de aproximadamente 20 filas por CustomerId por promedio que tenía al final alrededor de 10000 filas por dicho ID único. Los números donde:
Me aseguré de implementar correctamente cada método comparando los resultados. Mi conclusión es que el método que utilicé es generalmente más rápido siempre que exista un índice. También noté que este método es lo que se recomienda para este problema en particular en este artículo https://www.microsoftpressstore.com/articles/article.aspx?p=2314819&seqNum=5
Una forma de mejorar aún más el rendimiento de las llamadas posteriores a esta consulta es persistir la información de conteo en una tabla auxiliar. Incluso podría mantenerlo al tener un activador que se actualiza y contiene información sobre el recuento de filas SalesOrderHeader que dependen de CustomerId, por supuesto, también puede almacenar la mediana también.
fuente
Para conjuntos de datos a gran escala, puede probar este GIST:
https://gist.github.com/chrisknoll/1b38761ce8c5016ec5b2
Funciona agregando los distintos valores que encontraría en su conjunto (como las edades o el año de nacimiento, etc.), y utiliza las funciones de la ventana SQL para localizar cualquier posición de percentil que especifique en la consulta.
fuente