web-dev-qa-db-ja.com

重複する値を無視してテーブルに挿入する

私はこれを理解する上でおならをしている。次の2つのテーブルがあります。

Table: parts
    part_id          INT IDENTITY(1,1) NOT NULL,
    part_number      VARCHAR(50) UNIQUE NOT NULL,
    part_description VARCHAR(MAX) NOT NULL,
    information      VARCHAR(MAX) NULL,
    manufacturer_id  INT NOT NULL,
    subcategory_id   INT NOT NULL

Table: part_temp
    part_num  VARCHAR(50) NOT NULL,
    part_desc VARCHAR(MAX) NULL,
    info      VARCHAR(MAX) NULL,
    man_id    INT NULL,
    sub_id    INT NULL

part_tempは、CSVファイルからのデータを含む一時テーブルです。そのため、NOT NULLに設定されている列は1つだけです。 part_tempからpartsにデータを挿入する必要があります。

テーブルのデータを適切にクリーンアップしたので、値を必要とするnull値が行に挿入されないようにしています。しかし、私の問題は、UNIQUEテーブルのpart_number列のparts制約にあります。 part_tempテーブル内に重複する値があるため、挿入中にそれらをスキップできる方法が必要です。これは私がこれまでに試したことですが、うまくいきません:

INSERT INTO parts
SELECT DISTINCT pt.part_num, pt.part_desc, pt.info, m.manufacturer_id, s.subcategory_id
  FROM part_temp                AS pt
       FULL OUTER JOIN man_temp AS mt ON pt.man_id = mt.man_id
       INNER JOIN manufacturers AS m  ON mt.man_name = m.manufacturer_name
       FULL OUTER JOIN cat_temp AS ct ON pt.sub_id = ct.category_id
       INNER JOIN subcategories AS s  ON ct.category_name = s.subcategory_name
 WHERE NOT EXISTS(SELECT part_number FROM parts WHERE part_number = pt.part_num)

これらは、上記にリストされていない結合に含まれるテーブルです

Table: man_temp
    man_id INT NOT NULL,
    man_name VARCHAR(100) NOT NULL

Table: manufacturers
    manufacturer_id   INT IDENTITY(1,1) NOT NULL,
    manufacturer_name VARCHAR(100) NOT NULL

Table: cat_temp
    category_id   INT NOT NULL,
    category_name VARCHAR(100) NOT NULL

Table: subcategories
    subcategory_id   INT IDENTITY(1,1) NOT NULL,
    subcategory_name VARCHAR(100) NOT NULL

INSERTクエリの何が問題になっていますか?

私が得ている特定のエラーは:

メッセージ2627、レベル14、状態1、行1

UNIQUE KEY制約の違反。オブジェクト 'dbo.parts'に重複するキーを挿入することはできません。重複するキーの値は(31335A11)です

part_num 31335A11はcsvファイルに複数回出現します。そのため、part_tempテーブルに複数回出現します。このエントリだけでも簡単ですが、1,000を超えるリピートエントリがあるため、すべての重複を削除するには永久に時間がかかります。 partsには何も存在しません。これは、値を入れようとしている真新しい空のテーブルだからです。

2
Skitzafreak

Violation of UNIQUE KEY constraintに遭遇した場合、私が最初に行うことは、次の質問を自分に問うことです。

  1. 重複するキーは既にターゲットテーブルに存在しますか?
  2. ソーステーブルでキーが重複していますか?

上記に答えることで、重複する値がどこから取得されているのか、そして原因の合理的な考えがわかります。可能性の例は次のとおりです。

  • ソーステーブルに重複が存在します
  • ソースに重複は存在しませんが、データの変換により重複が返されます

問題の重複する値があるので、最終的にはこの問題を基本に戻す必要があります。問題の重複値があるので、データを掘り下げ、ソースクエリに細心の注意を払う必要があります。幸いにもあなたの例ではそれは単純なクエリであり、簡単に結合を取り除いてそれらのいずれかが重複を導入しているかどうか、または実際に非キー属性が異なるために重複が存在し、そのためDISTINCTがあなたが期待することをしないでください。

私が言おうとしているのは、この種の問題には必ずしもすぐに成功するわけではなく、SQLによって提供される手掛かりを使用して、古き良きファッション探偵の仕事をする必要があるということです。

Mootポイントですが、ターゲットテーブルの列の順序がソースクエリの列の順序と異なる場合でも何も壊れないように、挿入の列を明示的にリストするのが好きです。

2
CasualFisher

まず、SQL SERVERにはCONFLICTコマンドがありません。

第二:

DISTINCTとWHERE NOT EXISTSを使用して間違った方向に進んでいます。どちらのコマンドでも、データの重複を防ぐことはできません。

part_tempに関する次のデータがあるとします。

part_num    part_desc         info        man_id     sub_id
============================================================
000345      something1        some info   2          1
000345      something2        some info1  4          6
000345      something3        some info2  5          8

Part_num 000345がpartsテーブルに存在しないとします。クエリは、3つのレコード(互いに異なるレコード)を挿入しようとします。

最後に:

あなたが望むものを達成するために。 SQL ServerはMERGEというコマンドを提供します

MERGEを使用すると、競合が発生するたびに何を行うかを決定できます。これは、前に挿入されたレコードをupdateするか、または単にskip挿入の新しい候補

競合ケースの挿入をスキップするには、次のコードを使用します。

MERGE INTO parts A
USING (

 --YOUR SELECT QUERY HERE; NO NEED TO USE DISTINCT / WHERE

) B
ON(A.part_num = B.part_num)
WHEN MATCHED THEN UPDATE SET A.part_num=A.part_num
WHEN NOT MATCHED THEN INSERT(part_number,part_description,....) 
VALUES(B.part_num,B.part_desc,....);

幸運を!

繰り返しデータを2番目のテーブルに格納する場合は、UPDATE文の最後にOUTPUT句を含めます(これは実際にはDO NOTHINGとして機能します)。..

...
WHEN MATCHED THEN 
   UPDATE SET A.part_num=A.part_num
   OUTPUT DELETED.part_id, B.* INTO part_duplicates
WHEN NOT MATCHED THEN 
   INSERT(part_number,part_description,....) 
   VALUES(B.part_num,B.part_desc,....);
0
AMG