web-dev-qa-db-ja.com

SQL Server-レコードを挿入し、一意であることを確認する方法

アイテムがまだ存在しない場合にのみ、単一のテーブルにレコードを挿入する最良の方法を見つけようとしています。この場合のKEYはNVARCHAR(400)フィールドです。この例では、Oxford English DictionaryのWordの名前のふりをして、ここにfav辞書を挿入します。また、Wordフィールドを主キーにする必要があると思います。 (テーブルには一意の識別子PKもあります)。

だから..私はテーブルに追加する必要があるこれらの単語を取得するかもしれません...

例えば。

  • ネコ
  • フー
  • バー
  • ピューピュー
  • 等...

だから伝統的に、私は次のことを試してみました(擬似コード)

SELECT WordID FROM Words WHERE Word = @Word
IF WordID IS NULL OR WordID <= 0
    INSERT INTO Words VALUES (@Word)

すなわち。 Wordが存在しない場合は挿入します。

今..私が心配している問題は、ヒットがたくさんあるということです。そのため、SELECTとINSERTの間に別のプロセスからWordを挿入できる可能性があります。これにより、制約エラーがスローされます? (すなわち レース条件 )。

その後、私は次のことができると思いました...

INSERT INTO Words (Word)
SELECT @Word
WHERE NOT EXISTS (SELECT WordID FROM Words WHERE Word = @Word)

基本的に、Wordが存在しないときに挿入します。

悪い構文は別として、これがテーブルをロックダウンする方法が原因でこれが悪いか良いかはわかりません(もしそうなら)、大量の読み取りと大量の書き込みを行うテーブルでそのパフォーマンスがありません。

だから-あなたはSQLの達人はどう思う/何をしますか?

私は、スローされたエラーに対して単純な挿入と「キャッチ」を望んでいました。

22
Pure.Krome

あなたのソリューション:

INSERT INTO Words (Word)
    SELECT @Word
WHERE NOT EXISTS (SELECT WordID FROM Words WHERE Word = @Word)

...得られるものとほぼ同じくらいです。これに単純化できます:

INSERT INTO Words (Word)
    SELECT @Word
WHERE NOT EXISTS (SELECT * FROM Words WHERE Word = @Word)

... EXISTSは実際にはレコードを返す必要がないため、クエリオプティマイザーは、どのフィールドを要求したかを気にしません。

ただし、これはINSERT中にテーブル全体をロックするため、これは特に効果的ではありません。ただし、一意のインデックス(主キーである必要はありません)をWordに追加する場合は、関連するページをロックするだけで済みます。

最適なオプションは、予想される負荷をシミュレートし、SQL Server Profilerでパフォーマンスを確認することです。他のフィールドと同様に、時期尚早な最適化は悪いことです。許容可能なパフォーマンスメトリックを定義し、他の作業を行う前に測定します。

それでも十分なパフォーマンスが得られない場合は、データウェアハウジングの分野で役立つテクニックがたくさんあります。

28
Roger Lipscombe

私はこれに対するより良い(または少なくともより速い)答えを見つけたと思います。次のようなインデックスを作成します。

CREATE UNIQUE NONCLUSTERED INDEX [IndexTableUniqueRows] ON [dbo].[table] 
(
    [Col1] ASC,
    [Col2] ASC,

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = ON, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

一意性を定義するすべての列を含めます。重要な部分はIGNORE_DUP_KEY = ONです。これにより、一意でない挿入が警告に変わります。 SSISはこれらの警告を無視しますが、fastloadも使用できます。

5
WebWeasel

MS SQL Serverを使用している場合、一意にする必要があるテーブルの列に一意のインデックスを作成できます(ドキュメントに記載されている here )。

CREATE UNIQUE [ CLUSTERED | NONCLUSTERED ] INDEX <index_name>
    ON Words ( Word [ ASC | DESC ])

場合に応じて、ClusteredまたはNonClusteredを指定します。また、(より高速なシークを可能にするために)ソートする場合は、ソート順序にASCまたはDESCを指定します。

インデックスアーキテクチャの詳細については、 here を参照してください。

それ以外の場合は、文書化された here のようなUNIQUE CONSTRAINTSを使用できます。

ALTER TABLE Words
ADD CONSTRAINT UniqueWord
UNIQUE (Word); 
3
Bogdan Maxim

私は同様の問題を抱えていましたが、これが私がそれを解決した方法です

insert into Words
( selectWord , Fixword)
SELECT Word,'theFixword'
FROM   OldWordsTable
WHERE 
(
    (Word LIKE 'junk%') OR
     (Word LIKE 'orSomthing') 

)
and Word not in 
    (
        SELECT selectWord FROM words WHERE selectWord = Word
    ) 
3
Pbearne

一意の制約は確かに1つの方法ですが、これを挿入ロジックにも使用できます。 http://www.sqlteam.com/article/application-locks-or-mutexes-in-sql-server-2005

基本的に、下の表にはロックを設定しないため、存在チェックが正常に実行されている間は読み取りを心配しません。

これは、SQLコードのミューテックスです。

1
Mladen Prajdic

MS SQLの詳細について話すことはできませんが、SQLの主キーの1つのポイントは、一意性を確保することです。したがって、一般的なSQL用語の定義では、主キーはテーブルに固有の1つ以上のフィールドです。この動作を強制するさまざまな方法がありますが(古いエントリを新しいエントリに置き換えるか、新しいエントリを拒否するか)、MS SQLの両方にこの動作を強制するメカニズムがなく、そうでない場合は驚くでしょう新しいエントリを拒否します。主キーをWordフィールドに設定し、それがshould workであることを確認してください。

繰り返しになりますが、これはすべてMySQLプログラミングとデータベースクラスの知識によるものであるため、MS SQLの複雑さを理解していない場合はおologiesび申し上げます。

0
Dan Fego