web-dev-qa-db-fra.com

Sélectionner des valeurs distinctes à partir de plusieurs colonnes dans la même table

J'essaie de construire une seule instruction SQL qui renvoie des valeurs uniques non nulles à partir de plusieurs colonnes situées dans la même table.

 SELECT distinct tbl_data.code_1 FROM tbl_data
      WHERE tbl_data.code_1 is not null
 UNION
 SELECT tbl_data.code_2 FROM tbl_data
      WHERE tbl_data.code_2 is not null;

Par exemple, tbl_data est la suivante:

 id   code_1    code_2
 ---  --------  ----------
 1    AB        BC
 2    BC        
 3    DE        EF
 4              BC

Pour la table ci-dessus, la requête SQL doit renvoyer toutes les valeurs uniques non nulles des deux colonnes, à savoir: AB, BC, DE, EF.

Je suis assez nouveau pour SQL. Mon instruction ci-dessus fonctionne, mais existe-t-il une méthode plus propre pour écrire cette instruction SQL, puisque les colonnes proviennent de la même table?

18
regulus

Il est préférable d’inclure du code dans votre question plutôt que des données textuelles ambiguës, afin que nous travaillions tous avec les mêmes données. Voici l'exemple de schéma et de données que j'ai supposé:

CREATE TABLE tbl_data (
  id INT NOT NULL,
  code_1 CHAR(2),
  code_2 CHAR(2)
);

INSERT INTO tbl_data (
  id,
  code_1,
  code_2
)
VALUES
  (1, 'AB', 'BC'),
  (2, 'BC', NULL),
  (3, 'DE', 'EF'),
  (4, NULL, 'BC');

Comme Blorgbeard a commenté, la clause DISTINCT dans votre solution est inutile car l'opérateur UNION élimine les lignes en double. Il existe un opérateur UNION ALL qui n'élimine pas les doublons, mais il n'est pas approprié ici.

La réécriture de votre requête sans la clause DISTINCT est une bonne solution à ce problème:

SELECT code_1
FROM tbl_data
WHERE code_1 IS NOT NULL
UNION
SELECT code_2
FROM tbl_data
WHERE code_2 IS NOT NULL;

Peu importe que les deux colonnes soient dans la même table. La solution serait la même, même si les colonnes se trouvaient dans des tables différentes.

Si vous n'aimez pas la redondance de spécifier deux fois la même clause de filtre, vous pouvez encapsuler la requête d'union dans une table virtuelle avant de filtrer cela:

SELECT code
FROM (
  SELECT code_1
  FROM tbl_data
  UNION
  SELECT code_2
  FROM tbl_data
) AS DistinctCodes (code)
WHERE code IS NOT NULL;

Je trouve la syntaxe de la seconde plus moche, mais c'est logiquement plus ordonné. Mais lequel fait mieux?

J'ai créé un sqlfiddle qui montre que l'optimiseur de requêtes de SQL Server 2005 produit le même plan d'exécution pour les deux requêtes différentes:

The query optimizer produces this execution plan for both queries: two table scans, a concatenation, a distinct sort, and a select.

Si SQL Server génère le même plan d'exécution pour deux requêtes, celles-ci sont pratiquement aussi bien que logiquement équivalentes.

Comparez ce qui précède au plan d'exécution de la requête dans votre question:

The DISTINCT clause makes SQL Server 2005 perform a redundant sort operation.

La clause DISTINCT oblige SQL Server 2005 à effectuer une opération de tri redondant, car l'optimiseur de requêtes ne sait pas que les doublons filtrés par DISTINCT dans la première requête seront filtrés par UNION ultérieurement.

Cette requête est logiquement équivalente aux deux autres, mais l'opération redondante la rend moins efficace. Sur un grand ensemble de données, je m'attendrais à ce que votre requête prenne plus de temps pour renvoyer un ensemble de résultats que les deux ici. Ne prenez pas ma parole pour cela; expérimentez dans votre propre environnement pour en être sûr!

21

essayez quelque chose comme SubQuery:

SELECT derivedtable.NewColumn
FROM
(
    SELECT code_1 as NewColumn FROM tbl_data 
    UNION
    SELECT code_2 as NewColumn FROM tbl_data 
) derivedtable
WHERE derivedtable.NewColumn IS NOT NULL

La UNION renvoie déjà les valeursDISTINCTESde la requête combinée.

5
John Woo

Essayez ceci si vous avez plus de deux colonnes CREATE TABLE #temptable (Nom1 VARCHAR (25), Nom2 VARCHAR (25))

INSERT INTO #temptable (Nom1, Nom2) VALEURS ('JON', 'Harry'), ('JON', 'JON'), ('Sam', 'harry')

SELECT t.Name1 + ',' + 't.Name2 Noms INTO #t FROM #temptable AS tSELECT DISTINCT ss.value FROM # t AS t CROSS APPLIQUER STRING_SPLIT (T.Names,', ') AS ss 

0
Khati Lx