Question

Je ne fais pas beaucoup de SQL, et la plupart du temps, je fais des opérations CRUD. De temps en temps, je vais quelque chose d'un peu plus compliqué. Donc, cette question peut être une question de débutant, mais je suis prêt. Je viens juste essayé de comprendre cela pendant des heures, et il a été inutile.

Alors, imaginez la structure de tableau suivant:

> | ID | Col1 | Col2 | Col3 | .. | Col8 |

Je veux sélectionner ID et une colonne calculée. La colonne calculée a une gamme de 0 - 8, et il contient le nombre de correspondances à la requête. Je tiens également à limiter le jeu de résultats pour inclure uniquement les lignes qui ont un certain nombre de correspondances.

Ainsi, à partir de cet échantillon de données:

> | 1 | 'a' | 'b' | 1 | 2 |  
> | 2 | 'b' | 'c' | 1 | 2 |  
> | 3 | 'b' | 'c' | 4 | 5 |  
> | 4 | 'x' | 'x' | 9 | 9 |  

Je veux interroger sur Col1 = 'a' OU Col2 = 'c' OU Col3 = 1 OU Col4 = 5 où le résultat calculé> 1 et ont le jeu de résultats ressembler à:

> | ID | Cal |
> | 1  |  2  |
> | 2  |  2  |
> | 3  |  2  |

J'utilise T-SQL et SQL Server 2005, si elle importe, et je ne peux pas changer le schéma DB.

Je préfère également garder comme une requête autonome et ne pas avoir à créer une procédure stockée ou d'une table temporaire.

Était-ce utile?

La solution

Cette réponse travaillera en collaboration avec SQL 2005, en utilisant un CTE pour nettoyer la table dérivée un peu.

WITH Matches AS
(
    SELECT ID, CASE WHEN Col1 = 'a' THEN 1 ELSE 0 END + 
                CASE WHEN Col2 = 'c' THEN 1 ELSE 0 END +
                CASE WHEN Col3 = 1  THEN 1 ELSE 0 END +
                CASE WHEN Col4 = 5  THEN 1 ELSE 0 END AS Result
    FROM Table1
    WHERE Col1 = 'a' OR Col2 = 'c' OR Col3 = 1 OR Col4 = 5 
)
SELECT ID, Result
FROM Matches
WHERE Result > 1 

Autres conseils

Voici une solution qui tire parti du fait qu'une comparaison booléenne renvoie les entiers 1 ou 0:

SELECT * FROM (
  SELECT ID, (Col1='a') + (Col2='c') + (Col3=1) + (Col4=5) AS calculated
  FROM MyTable
) q
WHERE calculated > 1; 

Notez que vous devez parenthésée les comparaisons booléennes parce + a une priorité supérieure =. En outre, vous devez tout mettre dans un sous-requête parce que vous pouvez normalement pas utiliser un alias de colonne dans une clause de WHERE de la même requête.

Il peut sembler que vous devez également utiliser une clause de WHERE dans la sous-requête pour limiter ses lignes, mais selon toute vraisemblance, vous allez finir avec un balayage table complète de toute façon il est donc probablement pas une grande victoire. D'autre part, si vous vous attendez qu'une telle restriction serait très réduire le nombre de lignes dans le résultat de sous-requête, il serait utile.


Commentaire de Re Quassnoi, si vous ne pouvez pas traiter les expressions booléennes comme des valeurs entières, il devrait y avoir un moyen de cartographier les conditions booléennes aux entiers, même si elle est un peu bavard. Par exemple:

SELECT * FROM (
  SELECT ID, 
      CASE WHEN Col1='a' THEN 1 ELSE 0 END
    + CASE WHEN Col2='c' THEN 1 ELSE 0 END 
    + CASE WHEN Col3=1   THEN 1 ELSE 0 END
    + CASE WHEN Col4=5   THEN 1 ELSE 0 END AS calculated
  FROM MyTable
) q
WHERE calculated > 1;

Cette requête est plus convivial index:

SELECT  id, SUM(match)
FROM    (
        SELECT  id, 1 AS match
        FROM    mytable
        WHERE   col1 = 'a'
        UNION ALL
        SELECT  id, 1 AS match
        FROM    mytable
        WHERE   col2 = 'c'
        UNION ALL
        SELECT  id, 1 AS match
        FROM    mytable
        WHERE   col3 = 1
        UNION ALL
        SELECT  id, 1 AS match
        FROM    mytable
        WHERE   col4 = 5
        ) q
GROUP BY
        id
HAVING  SUM(match) > 1

Ce ne sera efficace que si tous les colonnes que vous recherchez sont, d'abord, indexés et, d'autre part, ont une grande cardinalité (beaucoup de valeurs distinctes).

Voir cet article dans mon blog pour les détails de la performance:

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top