Выберите различные значения из нескольких столбцов в одной таблице

Я пытаюсь построить один оператор 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, поскольку столбцы из одной таблицы?

Ответы

Ответ 1

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

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 позже.

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

Ответ 2

попробуйте что-то вроде 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 из комбинированного запроса.

Ответ 3

Попробуйте это, если у вас есть более двух столбцов

CREATE TABLE #temptable (Name1 VARCHAR(25),Name2 VARCHAR(25))

    INSERT INTO #temptable(Name1, Name2)
    VALUES('JON', 'Harry'), ('JON', 'JON'), ('Sam','harry')

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

Ответ 4

Объединение применяется везде, где требуемые данные строки похожи по типу, значениям и т.д. Неважно, есть ли у вас столбец в той же таблице или другой, из которого можно извлечь данные, поскольку результаты останутся прежними (в одном из вышеупомянутых ответов уже упоминалось хоть).

Так как вы не хотели дублировать, нет смысла использовать UNION ALL, и использование различных просто не нужно, так как объединение дает разные данные

Можно создать представление будет лучшим выбором, так как представление является виртуальным представлением таблицы. Изменения могут быть сделаны аккуратно в этом представлении

       Create VIEW getData AS 
       (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);