現在、列の値が重複しているテーブルがあります。
これらの誤った重複を削除することはできませんが、一意でない値が追加されないようにしたいと思います。
既存のコンプライアンスをチェックしないUNIQUE
を作成できますか?
NOCHECK
を使用してみましたが、失敗しました。
この場合、ライセンス情報を「CompanyName」に関連付けるテーブルがあります。
編集:同じ "CompanyName"を持つ複数の行を持つことは悪いデータですが、現時点ではそれらの重複を削除または更新することはできません。 1つのアプローチは、INSERT
sに、重複して失敗するストアドプロシージャを使用させることです... SQLが独自に一意性をチェックすることが可能である場合、それが望ましいでしょう。
このデータは、会社名で照会されます。いくつかの既存の重複については、これは複数の行が返されて表示されることを意味します...これは間違っていますが、私たちのユースケースでは許容されます。目標は、将来的にそれを防ぐことです。コメントから、ストアドプロシージャでこのロジックを実行する必要があるようです。
答えは「はい」です。フィルターされたインデックスを使用してこれを行うことができます(ドキュメントについては here を参照してください)。
たとえば、次のことができます。
create unique index t_col on t(col) where id > 1000;
これにより、古い行ではなく、new行のみに一意のインデックスが作成されます。この特定の定式化により、既存の値との重複が可能になります。
重複が少ししかない場合は、次のようにすることができます。
create unique index t_col on t(col) where id not in (<list of ids for duplicate values here>);
はい、できます。
重複した表は次のとおりです。
CREATE TABLE dbo.Party
(
ID INT NOT NULL
IDENTITY ,
CONSTRAINT PK_Party PRIMARY KEY ( ID ) ,
Name VARCHAR(30) NOT NULL
) ;
GO
INSERT INTO dbo.Party
( Name )
VALUES ( 'Frodo Baggins' ),
( 'Luke Skywalker' ),
( 'Luke Skywalker' ),
( 'Harry Potter' ) ;
GO
既存のものを無視して、新しい重複が追加されないようにします:
-- Add a new column to mark grandfathered duplicates.
ALTER TABLE dbo.Party ADD IgnoreThisDuplicate INT NULL ;
GO
-- The *first* instance will be left NULL.
-- *Secondary* instances will be set to their ID (a unique value).
UPDATE dbo.Party
SET IgnoreThisDuplicate = ID
FROM dbo.Party AS my
WHERE EXISTS ( SELECT *
FROM dbo.Party AS other
WHERE other.Name = my.Name
AND other.ID < my.ID ) ;
GO
-- This constraint is not strictly necessary.
-- It prevents granting further exemptions beyond the ones we made above.
ALTER TABLE dbo.Party WITH NOCHECK
ADD CONSTRAINT CHK_Party_NoNewExemptions
CHECK(IgnoreThisDuplicate IS NULL);
GO
SELECT * FROM dbo.Party;
GO
-- **THIS** is our pseudo-unique constraint.
-- It works because the grandfathered duplicates have a unique value (== their ID).
-- Non-grandfathered records just have NULL, which is not unique.
CREATE UNIQUE INDEX UNQ_Party_UniqueNewNames ON dbo.Party(Name, IgnoreThisDuplicate);
GO
このソリューションをテストしてみましょう:
-- cannot add a name that exists
INSERT INTO dbo.Party
( Name )
VALUES ( 'Frodo Baggins' );
Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.
-- cannot add a name that exists and has an ignored duplicate
INSERT INTO dbo.Party
( Name )
VALUES ( 'Luke Skywalker' );
Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.
-- can add a new name
INSERT INTO dbo.Party
( Name )
VALUES ( 'Hamlet' );
-- but only once
INSERT INTO dbo.Party
( Name )
VALUES ( 'Hamlet' );
Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.
フィルター処理された一意のインデックスは素晴らしいアイデアですが、_WHERE identity_column > <current value>
_条件またはWHERE identity_column NOT IN (<list of ids for duplicate values here>)
を使用するかどうかに関係なく、マイナーな欠点があります。
最初のアプローチでは、将来的に既存の(現在の)データの複製である重複データを挿入することができます。たとえば、_CompanyName = 'Software Inc.'
_を含む行が(1つでも)ある場合、インデックスは同じ会社名の行をもう1つ挿入することを禁止しません。二度試した場合にのみ禁止されます。
2番目のアプローチには改善点がありますが、上記は機能しません(これは良いことです)。ただし、重複または既存の重複を挿入することはできます。たとえば、2つ以上の行に_CompanyName = 'DoubleData Co.'
_が含まれている場合、インデックスは同じ会社名の行をもう1つ挿入することを禁止しません。二度試した場合にのみ禁止されます。
(Update)これは、重複する名前ごとに1つのIDを除外リストに含めない場合に修正できます。上記の例のように、重複する_CompanyName = DoubleData Co.
_およびID _4,6,8,9
_が4行ある場合、除外リストにはこれらのIDが3つだけ含まれている必要があります。
2番目のアプローチでは、SQL-ServerがWHERE
で_NOT IN
_演算子をサポートしていないように見えるため、もう1つの欠点は厄介な条件です(どのくらい厄介なのかは、最初から重複の数によって異なります)。フィルターされたインデックスの一部。 SQL-Fiddle を参照してください。 WHERE (CompanyID NOT IN (3,7,4,6,8,9))
の代わりに、WHERE (CompanyID <> 3 AND CompanyID <> 7 AND CompanyID <> 4 AND CompanyID <> 6 AND CompanyID <> 8 AND CompanyID <> 9)
のようなものが必要になります。このような条件で効率に影響があるかどうか、名前が重複している場合はわかりません。
別のソリューション(@Alex Kuznetsovと同様)は、別の列を追加し、ランク番号を入力して、この列を含む一意のインデックスを追加することです。
_ALTER TABLE Company
ADD Rn TINYINT DEFAULT 1;
UPDATE x
SET Rn = Rnk
FROM
( SELECT
CompanyID,
Rn,
Rnk = ROW_NUMBER() OVER (PARTITION BY CompanyName
ORDER BY CompanyID)
FROM Company
) x ;
CREATE UNIQUE INDEX CompanyName_UQ
ON Company (CompanyName, Rn) ;
_
次に、_DEFAULT 1
_プロパティと一意のインデックスが原因で、名前が重複する行を挿入すると失敗します。これはまだ100%フールプルーフではありません(アレックスの場合はそうです)。 Rn
ステートメントでINSERT
が明示的に設定されている場合、またはRn
の値が悪意を持って更新されている場合は、重複が引き続き発生します。