2012-07-02 28 views
16

Estoy intentando construir una sola instrucción SQL que devuelva valores únicos, no nulos, de varias columnas ubicadas todas en la misma tabla.Seleccionar valores distintos de varias columnas en la misma tabla

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; 

Por ejemplo, tbl_data es el siguiente:

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

Para la tabla anterior, la consulta SQL debe devolver todos los valores no nulos únicas de las dos columnas, a saber: AB, BC, DE , EF.

Soy bastante nuevo en SQL. Mi afirmación anterior funciona, pero ¿hay alguna manera más clara de escribir esta declaración SQL, ya que las columnas son de la misma tabla?

+0

Esa estructura de tabla me da la sensación de que su base de datos no está normalizada ... – gdoron

+4

No necesita 'distinct' en la primera consulta -' union' hará eso por usted. – Blorgbeard

+0

@gdoron: Los códigos corresponden a varias designaciones, que de hecho pueden repetirse, es decir, un registro particular puede tener BC y BC para los códigos 1 y 2. La designación del código 1 contra 2 también es significativa. Hay una tercera tabla de búsqueda de tabla para varios códigos. No es el mejor, pero eso es a lo que me refiero. – regulus

Respuesta

18

Es mejor incluir el código de su pregunta, en lugar de datos de texto ambiguos, para que todos trabajemos con los mismos datos. Aquí está el esquema de ejemplo y datos que he asumido:

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'); 

Como Blorgbeard comentó, la cláusula DISTINCT en su solución no es necesario porque el operador UNION elimina filas duplicadas. Hay un operador UNION ALL que no elimina los duplicados, pero no es apropiado aquí.

Reescritura de la consulta sin la cláusula DISTINCT es una buena solución a este problema:

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; 

No importa que las dos columnas están en la misma mesa. La solución sería la misma incluso si las columnas estuvieran en tablas diferentes.

Si no te gusta la redundancia de especificar la misma cláusula de filtro dos veces, se puede encapsular la consulta de unión en una tabla virtual antes de filtrar que:

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

encuentro la sintaxis de la segunda más fea , pero es lógicamente más limpio. ¿Pero cuál se comporta mejor?

he creado un sqlfiddle que demuestra que el optimizador de consultas de SQL Server 2005 produce el mismo plan de ejecución de las dos consultas diferentes:

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

Si SQL Server genera el mismo plan de ejecución de dos consultas, entonces son prácticamente tan bien como lógicamente equivalentes.

Compare lo anterior con el plan de ejecución de la consulta en su pregunta:

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

El DISTINCT cláusula hace que SQL Server 2005 realizar una operación de tipo redundante, ya que el optimizador de consultas no sabe que los duplicados filtrado por el DISTINCT en la primera consulta sería filtrado por el UNION más tarde de todos modos.

Esta consulta es lógicamente equivalente a las otras dos, pero la operación redundante la hace menos eficiente. En un gran conjunto de datos, esperaría que tu consulta tardara más en devolver un conjunto de resultados que los dos aquí. No tome mi palabra para eso; ¡Experimenta en tu propio entorno para estar seguro!

+0

¡Gracias por esta explicación! – regulus

+0

Una de las mejores respuestas que creo que he leído en SO. Excelentes detalles y ejemplos, y resultados de pruebas. – htm11h

4

intentar algo así como 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 

El UNION ya vuelve DISTINCT valores de la consulta combinada.

+0

¡Gracias por este ejemplo! – regulus

+0

¡Gracias! Parece la respuesta perfecta que estoy buscando! – NidhinSPradeep

Cuestiones relacionadas