Escrevi uma declaração de caso com> 100 opções em que estou usando a mesma declaração em 4 locais em uma consulta simples.
A mesma consulta duas vezes com uma união entre eles, mas também está fazendo uma contagem e, portanto, o grupo by também contém a instrução case.
Isso é para rotular novamente alguns nomes de empresas em que registros diferentes para a mesma empresa são escritos de maneira diferente.
Tentei declarar uma variável como um 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''
...
Quando fui usá-lo na minha instrução select - a consulta retornou a instrução case como texto e não a avaliou.
Também não consegui usá-lo no grupo por - recebi esta mensagem de erro:
Each GROUP BY expression must contain at least one column that is not an outer reference.
Idealmente, gostaria de ter o CASE em apenas um único local - para que não haja chance de eu atualizar uma linha e não replicá-la em outro lugar.
Existe alguma maneira de fazer isso?
Estou aberto a outras formas (como talvez uma função - mas não sei como usá-las dessa maneira)
Aqui está um exemplo do SELECT que estou usando atualmente
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
O objetivo deste UNION é retornar todos os dados por um período de tempo e TAMBÉM retornar dados pelo mesmo período de 12 meses
EDIT: Adicionado um "CATCH-ALL" ausente
EDIT2: Adicionado um segundo ½ da instrução UNION
EDIT3: Corrigido o GROUP BY para incluir alguns outros elementos necessários
fonte
WHERE a.datecreated = CONVERT(DATE,now()) OR a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))
?Respostas:
Uma maneira fácil de eliminar a repetição da expressão CASE é usar CROSS APPLY assim:
Com a ajuda do CROSS APPLY, você atribui um nome à sua expressão CASE de forma que ela possa ser referenciada em qualquer lugar da sua declaração. Funciona porque, estritamente falando, você está definindo a coluna computada em um SELECT aninhado - o SELECT FROM-less que segue o CROSS APPLY.
É o mesmo que referenciar uma coluna com alias de uma tabela derivada - que tecnicamente é esse SELECT aninhado. É uma subconsulta correlacionada e uma tabela derivada. Como uma subconsulta correlacionada, é permitido fazer referência às colunas do escopo externo e, como uma tabela derivada, permite que o escopo externo faça referência às colunas que define.
Para uma consulta UNION que usa a mesma expressão CASE, é necessário defini-la em cada perna, não há solução alternativa para isso, exceto para usar um método de substituição completamente diferente em vez do CASE. No entanto, no seu caso específico, é possível buscar os resultados sem UNION.
As duas pernas diferem apenas na condição WHERE. Um tem o seguinte:
e o outro isso:
Você pode combiná-los assim:
e aplique-o ao SELECT modificado no início desta resposta.
fonte
CTE
- Não tenho certeza de qual é a melhor abordagem!UNION
e inclua adatecreated
coluna na suaGROUP BY
cláusula (e atualize aWHERE
cláusula para incluir as duas datas em que você está interessado).datecreated
coluna no GROUP BY. Fora isso, concordo plenamente, eles podem apenas combinar as cláusulas WHERE e abandonar a UNION.Coloque os dados em uma tabela
e junte-se a ele.
Dessa forma, você pode evitar manter os dados atualizados em vários locais. Basta usar o
COALESCE
local que você precisar. Você pode incorporar isso ao CTE ouVIEW
s, conforme as outras sugestões.fonte
Outra opção, acho que, se você precisar reutilizá-lo em vários lugares, uma função com valor de tabela Inline será boa.
Sua seleção será assim.
Além disso, eu não testei isso e o desempenho do código também deve ser determinado.
EDIT1 : Eu acho que andriy já deu um que usa cross apply que redige o código. Bem, este pode ser centralizado, pois quaisquer alterações na função serão refletidas em todos, pois você repetirá o mesmo em outras partes do código.
fonte
Eu usaria um
VIEW
para fazer o que você está tentando fazer. Obviamente, você pode corrigir os dados subjacentes, mas frequentemente neste site, aqueles que fazem perguntas (consultants / dbas /) não têm autoridade para fazer isso. Usando umVIEW
pode resolver este problema! Também usei aUPPER
função - uma maneira barata de resolver erros em casos como este.Agora, você declara apenas
VIEW
uma vez e pode usá-lo em qualquer lugar! Dessa forma, você só tem um local em que seu algoritmo de conversão de dados é armazenado e executado, aumentando assim a confiabilidade e a robustez do seu sistema.Você também pode usar um CTE ( Common Table Expression ) - veja o final da resposta!
Para responder sua pergunta, fiz o seguinte:
Crie uma tabela de amostra:
Insira alguns registros de amostra:
Em seguida, crie um
VIEW
como sugerido:Então,
SELECT
do seuVIEW
:Resultado:
Et voilà!
Você pode encontrar tudo isso no violino aqui .
A
CTE
abordagem:O mesmo que acima, exceto que o
CTE
é substituído peloVIEW
seguinte:O resultado é o mesmo. Você pode então tratá-
CTE
lo como faria com qualquer outra tabela -SELECT
apenas por s! Violino disponível aqui .No geral, acho que a
VIEW
abordagem é melhor neste caso!fonte
Mesa embutida
Pule a união e use um
OR
no local, conforme sugerido por outros.fonte