Résolution des problèmes d'instruction CASE dans les clauses WHERE de SQL Server 2008
L'emploi d'instructions CASE dans les clauses WHERE dans SQL Server 2008 peut présenter des difficultés. La requête suivante illustre un problème courant :
<code class="language-sql">SELECT tl.storenum 'Store #', co.ccnum 'FuelFirst Card #', co.dtentered 'Date Entered', CASE st.reasonid WHEN 1 THEN 'Active' WHEN 2 THEN 'Not Active' WHEN 0 THEN st.ccstatustypename ELSE 'Unknown' END 'Status', CASE st.ccstatustypename WHEN 'Active' THEN ' ' WHEN 'Not Active' THEN ' ' ELSE st.ccstatustypename END 'Reason', UPPER(REPLACE(REPLACE(co.personentered,'RT\\',''),'RACETRAC\\','')) 'Person Entered', co.comments 'Comments or Notes' FROM comments co INNER JOIN cards cc ON co.ccnum=cc.ccnum INNER JOIN customerinfo ci ON cc.customerinfoid=ci.customerinfoid INNER JOIN ccstatustype st ON st.ccstatustypeid=cc.ccstatustypeid INNER JOIN customerstatus cs ON cs.customerstatuscd=ci.customerstatuscd INNER JOIN transactionlog tl ON tl.transactionlogid=co.transactionlogid LEFT JOIN stores s ON s.StoreNum = tl.StoreNum WHERE CASE LEN('TestPerson') WHEN 0 THEN co.personentered = co.personentered ELSE co.personentered LIKE '%TestPerson' END AND cc.ccnum = CASE LEN('TestFFNum') WHEN 0 THEN cc.ccnum ELSE 'TestFFNum' END AND CASE LEN('2011-01-09 11:56:29.327') WHEN 0 THEN co.DTEntered = co.DTEntered ELSE CASE LEN('2012-01-09 11:56:29.327') WHEN 0 THEN co.DTEntered >= '2011-01-09 11:56:29.327' ELSE co.DTEntered BETWEEN '2011-01-09 11:56:29.327' AND '2012-01-09 11:56:29.327' END END AND tl.storenum < 699 ORDER BY tl.StoreNum</code>
La solution : refactoriser avec la logique booléenne
Le problème principal réside dans l'utilisation inappropriée des instructions CASE directement dans la logique conditionnelle de la clause WHERE. Les expressions CASE doivent produire une valeur ; ils ne devraient pas être la condition entière.
La solution consiste à remplacer les instructions CASE par une logique booléenne équivalente en utilisant les opérateurs AND
et OR
:
<code class="language-sql">SELECT tl.storenum 'Store #', co.ccnum 'FuelFirst Card #', co.dtentered 'Date Entered', CASE st.reasonid WHEN 1 THEN 'Active' WHEN 2 THEN 'Not Active' WHEN 0 THEN st.ccstatustypename ELSE 'Unknown' END 'Status', CASE st.ccstatustypename WHEN 'Active' THEN ' ' WHEN 'Not Active' THEN ' ' ELSE st.ccstatustypename END 'Reason', UPPER(REPLACE(REPLACE(co.personentered,'RT\\',''),'RACETRAC\\','')) 'Person Entered', co.comments 'Comments or Notes' FROM comments co INNER JOIN cards cc ON co.ccnum=cc.ccnum INNER JOIN customerinfo ci ON cc.customerinfoid=ci.customerinfoid INNER JOIN ccstatustype st ON st.ccstatustypeid=cc.ccstatustypeid INNER JOIN customerstatus cs ON cs.customerstatuscd=ci.customerstatuscd INNER JOIN transactionlog tl ON tl.transactionlogid=co.transactionlogid LEFT JOIN stores s ON s.StoreNum = tl.StoreNum WHERE (LEN('TestPerson') = 0 AND co.personentered = co.personentered) OR (LEN('TestPerson') > 0 AND co.personentered LIKE '%TestPerson') AND ((LEN('TestFFNum') = 0 AND cc.ccnum = cc.ccnum) OR (LEN('TestFFNum') > 0 AND cc.ccnum = 'TestFFNum')) AND ((LEN('2011-01-09 11:56:29.327') = 0 AND co.DTEntered = co.DTEntered) OR (LEN('2012-01-09 11:56:29.327') = 0 AND co.DTEntered >= '2011-01-09 11:56:29.327') OR (co.DTEntered BETWEEN '2011-01-09 11:56:29.327' AND '2012-01-09 11:56:29.327')) AND tl.storenum < 699 ORDER BY tl.StoreNum;</code>
Améliorations des performances
Bien que cette requête corrigée fonctionne correctement, les performances peuvent être encore améliorées :
OR
profondément imbriquées peuvent être moins efficaces. Envisagez des structures de requête alternatives (par exemple, en utilisant UNION ALL
pour des critères de recherche distincts).WHERE
(par exemple, co.personentered
, cc.ccnum
, co.DTEntered
, tl.storenum
).En abordant ces points, vous pouvez créer une requête SQL plus efficace et plus lisible.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!