2012-07-02 11 views
16

Я пытаюсь создать единый оператор SQL, который возвращает уникальные ненулевые значения из нескольких столбцов, расположенных в одной таблице.Выберите различные значения из нескольких столбцов в одной таблице

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; 

Например, tbl_data выглядит следующим образом:

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

Для приведенных выше таблиц, SQL-запрос должен возвращать все уникальные ненулевые значения из двух столбцов, а именно: AB, BC, DE , EF.

Я довольно новичок в SQL. Мое утверждение выше работает, но есть ли более чистый способ написать этот оператор SQL, поскольку столбцы из одной таблицы?

+0

Эта структура таблицы дает мне ощущение, что ваша БД не нормализована ... – gdoron

+4

Вам не нужен 'отдельный' в первом запросе -' union' сделает это за вас. – Blorgbeard

+0

@gdoron: коды соответствуют различным обозначениям, которые действительно могут быть повторены, то есть конкретная запись может иметь BC и BC для кодов 1 и 2. Обозначение кода 1 по сравнению с 2 также является значительным. Для разных кодов есть таблица поиска таблиц третьего стола. Не самое лучшее, но это то, с чем я имею дело. – regulus

ответ

18

Лучше включить код вопроса, а не двусмысленные текстовые данные, чтобы мы все работали с одними и теми же данными. Вот пример схемы и данных я предположил:

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

Blorgbeard Как прокомментировал, предложение в своем решении DISTINCT нет необходимости, поскольку оператор UNION устраняет повторяющиеся строки. Существует оператор UNION ALL, который не устраняет дубликаты, но здесь это не подходит.

Переписав запрос без предложения DISTINCT является прекрасным решением этой проблемы:

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; 

Это не имеет значения, что два столбца в одной таблице. Решение будет таким же, даже если столбцы были в разных таблицах.

Если вам не нравится избыточность с указанием же положения фильтра в два раза, вы можете инкапсулировать запрос накидного в виртуальной таблице перед фильтрацией, что:

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

Я считаю, синтаксис второй более уродливого , но он логически более аккуратный. Но какой из них лучше работает?

Я создал sqlfiddle, подтверждающие, что оптимизатор запросов в SQL Server 2005 производит один и тот же план выполнения для двух различных запросов:

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

Если SQL Server генерирует один и тот же план выполнения двух запросов, то они практически так же логически эквивалентны.

Сравним выше плана выполнения для запроса в вашем вопросе:

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

DISTINCT положение делает SQL Server 2005 выполняет избыточную операцию сортировки, потому что оптимизатор запросов не знает, что все дубликаты отфильтрованный DISTINCT в первом запросе, в любом случае будет отфильтрован UNION.

Этот запрос логически эквивалентен двум другим, но избыточная операция делает его менее эффективным. В большом наборе данных я ожидаю, что ваш запрос займет больше времени, чтобы вернуть результирующий набор, чем два здесь. Не верьте мне на слово; экспериментируйте в своей собственной среде, чтобы быть уверенным!

+0

Спасибо за это объяснение! – regulus

+0

Один из лучших ответов, о котором я когда-либо читал. Отличные детали и примеры, а также результаты тестов. – htm11h

4

попробовать что-то вроде 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 

UNION уже возвращает DISTINCT значения из комбинированного запроса.

+0

Спасибо за этот пример! – regulus

+0

Спасибо! Кажется, прекрасный ответ, который я ищу! – NidhinSPradeep

Смежные вопросы