Escribí una declaración de caso con> 100 opciones donde estoy usando la misma declaración en 4 lugares en una consulta simple.
La misma consulta dos veces con una unión entre ellos, pero también está haciendo un recuento y, por lo tanto, el grupo también contiene la declaración del caso.
Esto es para volver a etiquetar algunos nombres de compañías donde los diferentes registros para la misma compañía se escriben de manera diferente.
Traté de declarar una variable como VarChar (MAX)
declare @CaseForAccountConsolidation varchar(max)
SET @CaseForAccountConsolidation = 'CASE
WHEN ac.accountName like ''AIR NEW Z%'' THEN ''AIR NEW ZEALAND''
WHEN ac.accountName LIKE ''AIR BP%'' THEN ''AIR BP''
WHEN ac.accountName LIKE ''ADDICTION ADVICE%'' THEN ''ADDICTION ADVICE''
WHEN ac.accountName LIKE ''AIA%'' THEN ''AIA''
...
Cuando fui a usarlo en mi declaración select, la consulta solo devolvió la declaración del caso como texto y no la evaluó.
Tampoco pude usarlo en el grupo: recibí este mensaje de error:
Each GROUP BY expression must contain at least one column that is not an outer reference.
Idealmente, me gustaría tener el CASO en un solo lugar, para que no haya posibilidad de actualizar una línea y no replicarla en otro lugar.
¿Hay alguna forma de hacer esto?
Estoy abierto a otras formas (como tal vez una función, pero no estoy seguro de cómo usarlas así)
Aquí hay una muestra de SELECT que estoy usando actualmente
SELECT
SUM(c.charge_amount) AS GSTExcl
,dl.FirstDateOfMonth AS MonthBilled
,dl.FirstDateOfWeek AS WeekBilled
,CASE
WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
ELSE ac.accountName
END AS accountName
,dl.FinancialYear
,CONVERT(Date,c.date_charged) AS date_charged
FROM [accession] a
LEFT JOIN account_code ac ON a.account_code_id = ac.account_code_id
LEFT Join charge c ON a.accession_id = c.accession_id
LEFT JOIN dateLookup dl ON convert(date,c.date_charged) = dl.date
WHERE a.datecreated = CONVERT(DATE,now())
GROUP BY
dl.FirstDateOfMonth
,dl.FinancialYear
,dl.FirstDateOfWeek
,CONVERT(Date,c.date_charged)
,CASE
WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
ELSE ac.accountName
END
UNION
SELECT
SUM(c.charge_amount) AS GSTExcl
,dl.FirstDateOfMonth AS MonthBilled
,dl.FirstDateOfWeek AS WeekBilled
,CASE
WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
ELSE ac.accountName
END AS accountName
,dl.FinancialYear
,CONVERT(Date,c.date_charged) AS date_charged
FROM [accession] a
LEFT JOIN account_code ac ON a.account_code_id = ac.account_code_id
LEFT Join charge c ON a.accession_id = c.accession_id
LEFT JOIN dateLookup dl ON convert(date,c.date_charged) = dl.date
WHERE a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))
GROUP BY
dl.FirstDateOfMonth
,dl.FinancialYear
,dl.FirstDateOfWeek
,CONVERT(Date,c.date_charged)
,CASE
WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
ELSE ac.accountName
END
El propósito de esta UNIÓN es devolver todos los datos de un período de tiempo, y TAMBIÉN devolver datos del mismo período de tiempo de 12 meses anteriores
EDITAR: se agregó un
EDIT2 "CATCH-ALL" que faltaba: se agregó una segunda mitad de la declaración UNION
EDIT3: se corrigió GROUP BY para incluir algunos otros elementos necesarios
fuente
WHERE a.datecreated = CONVERT(DATE,now()) OR a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))
?Respuestas:
Una manera fácil de eliminar la repetición de la expresión CASE es usar CROSS APPLY de esta manera:
Con la ayuda de CROSS APPLY, asigna un nombre a su expresión CASE de tal manera que se pueda hacer referencia a ella en cualquier parte de su declaración. Funciona porque, estrictamente hablando, está definiendo la columna calculada en un SELECT anidado, el SELECCIONAR SIN DE que sigue a la APLICACIÓN CRUZADA.
Esto es lo mismo que hacer referencia a una columna con alias de una tabla derivada, que técnicamente es este SELECT anidado. Es una subconsulta correlacionada y una tabla derivada. Como subconsulta correlacionada, se le permite hacer referencia a las columnas del ámbito externo y, como tabla derivada, permite que el ámbito externo haga referencia a las columnas que define.
Para una consulta UNION que use la misma expresión CASE, debe definirla en cada tramo, no hay solución para eso, excepto usar un método de reemplazo completamente diferente en lugar del CASE. Sin embargo, en su caso específico, es posible obtener los resultados sin UNION.
Las dos patas difieren solo en la condición WHERE. Uno tiene esto:
y el otro esto:
Puedes combinarlos así:
y aplíquelo al SELECT modificado al comienzo de esta respuesta.
fuente
CTE
- ¡No estoy seguro de cuál es el mejor enfoque!UNION
e incluya ladatecreated
columna en suGROUP BY
cláusula (y actualice laWHERE
cláusula para incluir las dos fechas que le interesan).datecreated
columna en GROUP BY. Aparte de eso, estoy completamente de acuerdo, pueden combinar las cláusulas WHERE y deshacerse de UNION.Poner los datos en una tabla
y únete a él.
De esta forma, puede evitar mantener los datos actualizados en varios lugares. Solo usa el
COALESCE
donde lo necesites. Puede incorporar esto en CTE oVIEW
s según las otras sugerencias.fuente
Otra opción, creo que si necesita reutilizarla en varios lugares, una función con valor de tabla en línea será una buena opción.
Su selección será así.
Además, no he probado esto y también se debe determinar el rendimiento del código.
EDIT1 : Creo que andriy ya ha dado uno que usa la aplicación cruzada que redacta el código. Bueno, este puede ser centralizado ya que cualquier cambio en la función se reflejará en todo ya que está repitiendo lo mismo en otras partes del código.
fuente
Yo usaría un
VIEW
para hacer lo que estás tratando de hacer. Podría, por supuesto, corregir los datos subyacentes, pero con frecuencia en este sitio, los que hacen preguntas (consultores / dbas /) no tienen la autoridad para hacerlo. ¡Usar unVIEW
puede resolver este problema! También utilicé laUPPER
función, una forma económica de resolver errores en casos como este.¡Ahora, solo declaras
VIEW
una vez y puedes usarla en cualquier lugar! De esta manera, solo tiene un lugar en el que se almacena y ejecuta su algoritmo de conversión de datos, lo que aumenta la fiabilidad y la solidez de su sistema.También puede usar un CTE ( expresión de tabla común ); consulte la parte inferior de la respuesta.
Para responder a su pregunta, hice lo siguiente:
Crea una tabla de muestra:
Inserte un par de registros de muestra:
Luego cree un
VIEW
como se sugiere:Entonces,
SELECT
de tuVIEW
:Resultado:
Et voilà!
Puedes encontrar todo esto en el violín aquí .
El
CTE
enfoque:Igual que el anterior, excepto que
CTE
se sustituye por elVIEW
siguiente:El resultado es el mismo. Luego puede tratar la
CTE
tabla como lo haría con cualquier otra tabla,SELECT
¡solo por s! Fiddle disponible aquí .En general, creo que el
VIEW
enfoque es mejor en este caso.fuente
Mesa incrustada
Omita la unión y use un
OR
en el lugar según lo sugerido por otros.fuente