Imagine la siguiente tabla (llamada TestTable
):
id somedate somevalue
-- -------- ---------
45 01/Jan/09 3
23 08/Jan/09 5
12 02/Feb/09 0
77 14/Feb/09 7
39 20/Feb/09 34
33 02/Mar/09 6
Me gustaría una consulta que devuelva un total acumulado en orden de fecha, como:
id somedate somevalue runningtotal
-- -------- --------- ------------
45 01/Jan/09 3 3
23 08/Jan/09 5 8
12 02/Feb/09 0 8
77 14/Feb/09 7 15
39 20/Feb/09 34 49
33 02/Mar/09 6 55
Sé que hay varias formas de hacerlo en SQL Server 2000/2005/2008.
Estoy particularmente interesado en este tipo de método que utiliza el truco de agregación-conjunto-declaración:
INSERT INTO @AnotherTbl(id, somedate, somevalue, runningtotal)
SELECT id, somedate, somevalue, null
FROM TestTable
ORDER BY somedate
DECLARE @RunningTotal int
SET @RunningTotal = 0
UPDATE @AnotherTbl
SET @RunningTotal = runningtotal = @RunningTotal + somevalue
FROM @AnotherTbl
... esto es muy eficiente, pero he oído que hay problemas en torno a esto porque no necesariamente puede garantizar que la UPDATE
instrucción procesará las filas en el orden correcto. Tal vez podamos obtener algunas respuestas definitivas sobre ese tema.
¿Pero quizás hay otras formas en que la gente puede sugerir?
editar: ahora con un SqlFiddle con la configuración y el ejemplo de 'truco de actualización' anterior
sql
sql-server
tsql
running-total
codeulike
fuente
fuente
Respuestas:
Actualización , si está ejecutando SQL Server 2012, consulte: https://stackoverflow.com/a/10309947
El problema es que la implementación de SQL Server de la cláusula Over es algo limitada .
Oracle (y ANSI-SQL) le permiten hacer cosas como:
SQL Server no le ofrece una solución limpia a este problema. Mi instinto me dice que este es uno de esos casos raros en los que un cursor es el más rápido, aunque tendré que hacer algunos puntos de referencia para obtener grandes resultados.
El truco de actualización es útil pero siento que es bastante frágil. Parece que si está actualizando una tabla completa, procederá en el orden de la clave primaria. Entonces, si establece su fecha como una clave principal ascendente, estará
probably
a salvo. Pero confía en un detalle de implementación de SQL Server no documentado (también si la consulta termina siendo realizada por dos procesos, me pregunto qué sucederá, vea: MAXDOP):Muestra de trabajo completo:
Usted solicitó un punto de referencia, este es el detalle.
La forma SEGURA más rápida de hacer esto sería el cursor, es un orden de magnitud más rápido que la subconsulta correlacionada de unión cruzada.
La forma más rápida es el truco ACTUALIZAR. Mi única preocupación es que no estoy seguro de que, en todas las circunstancias, la actualización se realice de forma lineal. No hay nada en la consulta que lo diga explícitamente.
En pocas palabras, para el código de producción iría con el cursor.
Datos de prueba:
Prueba 1:
Prueba 2:
Prueba 3:
Prueba 4:
fuente
En SQL Server 2012 puede usar SUM () con la cláusula OVER () .
Violín de SQL
fuente
Si bien Sam Saffron hizo un gran trabajo al respecto, aún no proporcionó un código de expresión de tabla común recursivo para este problema. Y para nosotros que trabajamos con SQL Server 2008 R2 y no con Denali, sigue siendo la forma más rápida de ejecutar el total, es aproximadamente 10 veces más rápido que el cursor en mi computadora de trabajo para 100000 filas, y también es una consulta en línea.
Entonces, aquí está (supongo que hay una
ord
columna en la tabla y su número secuencial sin espacios, para un procesamiento rápido también debería haber una restricción única en este número):sql fiddle demo
actualización También tenía curiosidad acerca de esta actualización con actualización variable o peculiar . Por lo general, funciona bien, pero ¿cómo podemos estar seguros de que funciona siempre? bueno, aquí hay un pequeño truco (lo encontró aquí: http://www.sqlservercentral.com/Forums/Topic802558-203-21.aspx#bm981258 ): simplemente verifique la asignación actual y anterior
ord
y use la1/0
asignación en caso de que sean diferentes de lo que usted espera:Por lo que he visto si tiene una clave primaria / índice agrupada adecuada en su tabla (en nuestro caso sería indexar por
ord_id
), la actualización continuará de forma lineal todo el tiempo (nunca se encontró dividir por cero). Dicho esto, depende de usted decidir si desea usarlo en el código de producción :)actualización 2 Estoy vinculando esta respuesta, porque incluye información útil sobre la falta de fiabilidad de la actualización peculiar: concatenación / índice / nvarchar (max) comportamiento inexplicable de nvarchar .
fuente
El operador APLICAR en SQL 2005 y superior funciona para esto:
fuente
También puede usar la función ROW_NUMBER () y una tabla temporal para crear una columna arbitraria para usar en la comparación en la instrucción SELECT interna.
fuente
Use una subconsulta correlacionada. Muy simple, aquí tienes:
El código puede no ser exactamente correcto, pero estoy seguro de que la idea sí lo es.
GROUP BY es en caso de que aparezca una fecha más de una vez, solo querrá verla una vez en el conjunto de resultados.
Si no le importa ver fechas repetidas, o si desea ver el valor original y la identificación, entonces lo siguiente es lo que desea:
fuente
También puede desnormalizar: almacenar totales acumulados en la misma tabla:
http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/23/denormalizing-to-enforce-business-rules-running-totals.aspx
Las selecciones funcionan mucho más rápido que cualquier otra solución, pero las modificaciones pueden ser más lentas
fuente
Suponiendo que las ventanas funcionan en SQL Server 2008 como lo hace en otros lugares (que he probado), intente esto:
MSDN dice que está disponible en SQL Server 2008 (¿y quizás también en 2005?), Pero no tengo una instancia a mano para probarlo.
EDITAR: bueno, aparentemente SQL Server no permite una especificación de ventana ("OVER (...)") sin especificar "PARTITION BY" (dividiendo el resultado en grupos pero no agregando de la forma en que GROUP BY lo hace). Molesto: la referencia de sintaxis de MSDN sugiere que es opcional, pero solo tengo instancias de SqlServer 2000 en este momento.
La consulta que di funciona tanto en Oracle 10.2.0.3.0 como en PostgreSQL 8.4-beta. Entonces dile a MS que se ponga al día;)
fuente
1 partitionme
y partición por eso. Además, la partición por probablemente sea necesaria en situaciones de la vida real al hacer informes.Si está utilizando el servidor SQL Server 2008 R2 anterior. Entonces, sería la forma más corta de hacerlo;
LAG se usa para obtener el valor de fila anterior. Puedes hacer google para más información.
[1]:
fuente
SUM(somevalue) OVER(...)
que me parece mucho más limpioCreo que se puede lograr un total acumulado utilizando la simple operación INNER JOIN a continuación.
fuente
Lo siguiente producirá los resultados requeridos.
Tener un índice agrupado en SomeDate mejorará en gran medida el rendimiento.
fuente
Usar join Otra variación es usar join. Ahora la consulta podría verse así:
Para más información, puede visitar este enlace http://askme.indianyouth.info/details/calculating-simple-running-totals-in-sql-server-12
fuente
Aunque la mejor manera de hacerlo es usando una función de ventana, también se puede hacer usando una simple subconsulta correlacionada .
fuente
fuente
Aquí hay 2 formas simples de calcular el total acumulado:
Enfoque 1 : se puede escribir de esta manera si su DBMS admite funciones analíticas
Enfoque 2 : puede utilizar OUTER APPLY si la versión de su base de datos / DBMS en sí no admite funciones analíticas
Nota: - Si tiene que calcular el total acumulado para diferentes particiones por separado, puede hacerlo tal como se publica aquí: Cálculo de totales acumulados en filas y agrupación por ID
fuente