web-dev-qa-db-ja.com

同じテーブルの複数の列から異なる値を選択する

すべて同じテーブルにある複数の列から一意の非null値を返す単一の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クエリは、2つの列、つまりAB、BC、DE、EFからすべての一意の非null値を返す必要があります。

私はSQLを初めて使います。上記のステートメントは機能しますが、列が同じテーブルからのものであるため、このSQLステートメントを作成するよりクリーンな方法はありますか?

22
regulus

曖昧なテキストデータではなく、質問にコードを含める方がよいので、全員が同じデータを使用します。以下は、私が想定しているサンプルのスキーマとデータです。

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;

2つの列が同じテーブルにあるかどうかは関係ありません。列が異なるテーブルにある場合でも、ソリューションは同じです。

同じフィルター句を2回指定する冗長性が気に入らない場合は、フィルター処理する前に、ユニオンクエリを仮想テーブルにカプセル化できます。

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

2番目の構文はより見苦しいですが、論理的にはきれいです。しかし、どれがより優れたパフォーマンスを発揮しますか?

SQL Server 2005のクエリオプティマイザーが2つの異なるクエリに対して同じ実行プランを生成することを示す sqlfiddle を作成しました。

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

SQL Serverが2つのクエリに対して同じ実行計画を生成する場合、それらは実際上および論理的に同等です。

上記を質問のクエリの実行計画と比較します。

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

クエリオプティマイザーは、最初のクエリでDISTINCTによってフィルター処理された重複が後でDISTINCTによってフィルター処理されることを知らないため、UNION句によってSQL Server 2005は冗長な並べ替え操作を実行します。

このクエリは論理的に他の2つと同等ですが、冗長な操作により効率が低下します。大規模なデータセットでは、クエリが結果セットを返すのに、ここの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 [〜#〜]値を返します。

5
John Woo

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
0
Khati Lx

必要な行データがタイプ、値などの点で類似している場合は、ユニオンが適用されます。結果が同じままになるため、同じテーブルまたは他の列に取得する列があるかどうかは関係ありません。しかし)。

重複を望んでいないので、UNION ALLを使用しても意味がありません。また、UNIONは個別のデータを提供するため、distinctの使用は不要です。

ビューはテーブルの仮想表現であるため、ビューを作成できるのが最善の選択です。作成されたビューで変更を適切に行うことができます

       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);
0
Himanshu Ahuja