J'ai écrit une déclaration de cas avec> 100 choix où j'utilise la même déclaration à 4 endroits dans une simple requête.
La même requête deux fois avec une union entre eux mais effectue également un comptage et donc le groupe par contient également l'instruction case.
Il s'agit de réétiqueter certains noms de société où différents enregistrements pour la même société sont orthographiés différemment.
J'ai essayé de déclarer une variable comme 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''
...
Lorsque je suis allé l'utiliser dans mon instruction select - la requête a simplement renvoyé l'instruction case sous forme de texte et ne l'a pas évaluée.
J'ai également été incapable de l'utiliser dans le groupe par - j'ai reçu ce message d'erreur:
Each GROUP BY expression must contain at least one column that is not an outer reference.
Idéalement, j'aimerais avoir le CASE dans un seul endroit - afin qu'il n'y ait aucune chance que je mette à jour une ligne et que je ne le reproduise pas ailleurs.
Y a-t-il un moyen de le faire?
Je suis ouvert à d'autres façons (comme peut-être une fonction - mais je ne sais pas comment les utiliser comme ça)
Voici un échantillon du SELECT que j'utilise actuellement
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
Le but de cette UNION est de renvoyer toutes les données pour une période de temps, et AUSSI de retourner les données pour la même période de 12 mois auparavant
EDIT: Ajout d'un "CATCH-ALL"
manquant EDIT2: Ajout d'un deuxième ½ de la déclaration UNION
EDIT3: Correction du GROUP BY pour inclure d'autres éléments nécessaires
la source
WHERE a.datecreated = CONVERT(DATE,now()) OR a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))
?Réponses:
Une manière simple d'éliminer la répétition de l'expression CASE est d'utiliser CROSS APPLY comme ceci:
À l'aide de CROSS APPLY, vous attribuez un nom à votre expression CASE de manière à ce qu'il puisse être référencé n'importe où dans votre instruction. Cela fonctionne car à proprement parler, vous définissez la colonne calculée dans un SELECT imbriqué - le SELECT FROM-less qui suit le CROSS APPLY.
Cela revient à référencer une colonne aliasée d'une table dérivée - ce qui est techniquement ce SELECT imbriqué. Il s'agit à la fois d'une sous-requête corrélée et d'une table dérivée. En tant que sous-requête corrélée, elle est autorisée à référencer les colonnes de la portée externe et, en tant que table dérivée, elle permet à la portée externe de référencer les colonnes qu'elle définit.
Pour une requête UNION qui utilise la même expression CASE, vous devez la définir dans chaque jambe, il n'y a pas de solution pour cela, sauf pour utiliser une méthode de remplacement complètement différente au lieu de CASE. Cependant, dans votre cas spécifique, il est possible de récupérer les résultats sans UNION.
Les deux jambes diffèrent dans la condition O only seulement. On a ceci:
et l'autre ceci:
Vous pouvez les combiner comme ceci:
et l'appliquer au SELECT modifié au début de cette réponse.
la source
CTE
- je ne sais pas quelle est la meilleure approche!UNION
et incluez simplement ladatecreated
colonne dans votreGROUP BY
clause (et mettez à jour laWHERE
clause pour inclure les deux dates qui vous intéressent).datecreated
colonne dans le GROUP BY. En dehors de cela, je suis entièrement d'accord, ils peuvent simplement combiner les clauses WHERE et abandonner l'UNION.Mettez les données dans un tableau
et rejoignez-le.
De cette façon, vous pouvez éviter de garder les données à jour à plusieurs endroits. Utilisez simplement l'
COALESCE
endroit où vous en avez besoin. Vous pouvez l'incorporer dans CTE ouVIEW
s selon les autres suggestions.la source
Une autre option, je pense que si vous devez la réutiliser à plusieurs endroits, une fonction valorisée de la table Inline sera une bonne option.
Votre sélection sera comme ça.
De plus, je n'ai pas testé cela et les performances du code doivent également être déterminées.
EDIT1 : Je pense qu'andriy en a déjà donné un qui utilise cross apply qui expurge le code. Eh bien, celui-ci peut être centralisé car tout changement dans la fonction se reflétera dans l'ensemble car vous répétez la même chose dans d'autres parties du code.
la source
J'utiliserais un
VIEW
pour faire ce que vous essayez de faire. Vous pouvez, bien sûr, corriger les données sous-jacentes, mais fréquemment sur ce site, ceux qui posent des questions (consultants / dbas /) n'ont pas le pouvoir de le faire. L'utilisation d'unVIEW
peut résoudre ce problème! J'ai également utilisé laUPPER
fonction - un moyen peu coûteux de résoudre les erreurs dans des cas comme celui-ci.Maintenant, vous ne déclarez qu'une seule
VIEW
fois et vous pouvez l'utiliser n'importe où! De cette façon, vous n'avez qu'un seul endroit où votre algorithme de conversion de données est stocké et exécuté, augmentant ainsi la fiabilité et la robustesse de votre système.Vous pouvez également utiliser un CTE ( Common Table Expression ) - voir en bas de la réponse!
Pour répondre à votre question, j'ai fait ce qui suit:
Créez un exemple de table:
Insérez quelques exemples d'enregistrements:
Ensuite, créez un
VIEW
comme suggéré:Ensuite, à
SELECT
partir de votreVIEW
:Résultat:
Et voilà!
Vous pouvez trouver tout cela sur le violon ici .
L'
CTE
approche:Comme ci-dessus, sauf que le
CTE
est remplacé par leVIEW
suivant:Le résultat est le même. Vous pouvez alors traiter le
CTE
comme vous le feriez pour n'importe quelle autre table - pourSELECT
s seulement! Violon disponible ici .Dans l'ensemble, je pense que l'
VIEW
approche est meilleure dans ce cas!la source
Table intégrée
Sautez l'union et utilisez un
OR
dans le où suggéré par d'autres.la source