テーブルにID、値、ステータスの3つの列がある状況があります。個別のIDの場合、値が1のステータスは1つだけであり、IDが値0のステータスを複数持つことができるようにする必要があります。一意のキーを使用すると、IDが複数のステータス(0または1)を持つことを防ぐことができます。
おそらく制約を使用して、これを解決する方法はありますか?
ありがとう
テーブルにID、値、ステータスの3つの列がある状況があります。個別のIDの場合、値が1のステータスは1つだけであり、IDが値0のステータスを複数持つことができるようにする必要があります。一意のキーを使用すると、IDが複数のステータス(0または1)を持つことを防ぐことができます。
おそらく制約を使用して、これを解決する方法はありますか?
ありがとう
ID
の一意性を維持するという制約を維持するインデックス付きビューを作成できます[Status] = 1
。
create view dbo.v_YourTable with schemabinding as
select ID
from dbo.YourTable
where [Status] = 1
go
create unique clustered index UX_v_UniTest_ID on v_YourTable(ID)
SQL Server 2008では、代わりに一意のフィルター処理されたインデックスを使用できます。
テーブルに重複するID
値が含まれる可能性がある場合、チェック制約は状況に応じて機能しません。トリガーを使うのが唯一の方法だと思います。あなたが例を探しているなら、私はそれを投稿することができます。ただし、要約すると、トリガーを使用して、挿入/更新されたIDのステータスが1であり、同じID間で重複しているかどうかをテストします。
編集:とには常に一意の制約を使用できID
ますValue
。私はそれがあなたが探しているものをあなたに与えるだろうと思っています。
これを挿入/更新トリガーに入れて、1つの値との組み合わせが1つだけ存在することを確認できます。条件が満たされない場合は、トラップ可能なエラーをスローして、操作を強制的にロールバックすることができます。
ゼロステータスに0の代わりにNULLを使用できる場合は、ペアにUNIQUE制約を使用でき、それが機能するはずです。NULLは実際の値ではないため(NULL!= NULL)、複数のnullを持つ行が競合しないようにする必要があります。
私見、これは基本的に正規化の問題です。「id」という名前の列は行を一意にアドレス指定しないため、PKになることはできません。少なくとも新しい(代理)キー(要素)が必要です。制約自体は「行内」の式として表現できないため、FKで表現する必要があります。
したがって、2つのテーブルに分割されます。1つはPK = idで、もう1つはFKREFERENCINGtwo.sidです。
PK =サロゲートキーを持つ2つ、およびFK idREFERENCINGone.id元のペイロード「値」もここにあります。
「1ビット変数」は、EXISTSで表現できるため、表示されなくなります。(事実上、テーブル1は、トークンを保持する行を指します)
[Postgresルールシステムを使用して、上記の2つのテーブルモデルを使用して、OPの意図された動作をエミュレートできると思います。しかし、それは醜いハックになるでしょう...]
編集/更新:
Postgresは部分的/条件付きインデックスをサポートしています。(ms-sqlについてはわかりません)
DROP TABLE tmp.one;
CREATE TABLE tmp.one
( sid INTEGER NOT NULL PRIMARY KEY -- surrogate key
, id INTEGER NOT NULL
, status INTEGER NOT NULL DEFAULT '0'
/* ... payload */
);
INSERT INTO tmp.one(sid,id,status) VALUES
(1,1,0) , (2,1,1) , (3,1,0)
, (4,2,0) , (5,2,0) , (6,2,1)
, (7,3,0) , (8,3,0) , (9,3,1)
;
CREATE UNIQUE INDEX only_one_non_zero ON tmp.one (id)
WHERE status > 0 -- "partial index"
;
\echo this should succeed
BEGIN ;
UPDATE tmp.one SET status = 0 WHERE sid=2;
UPDATE tmp.one SET status = 1 WHERE sid=1;
COMMIT;
\echo this should fail
BEGIN ;
UPDATE tmp.one SET status = 1 WHERE sid=4;
UPDATE tmp.one SET status = 0 WHERE sid=9;
COMMIT;
SELECT * FROM tmp.one ORDER BY sid;
私は解決策を思いついた
最初に関数を作成します
CREATE FUNCTION [dbo].[Check_Status] (@ID int)
RETURNS INT
AS
BEGIN
DECLARE @r INT;
SET @r =
(SELECT SUM(status) FROM dbo.table where ID= @ID);
RETURN @r;
END
次に、テーブルに制約を作成します
([dbo].[Check_Status]([ID])<(2))
このようにして、1つのIDが単一のステータス(1)と可能な限り多くのステータス(0)を持つことができます。
create function dbo.IsValueUnique
(
@proposedValue varchar(50)
,@currentId int
)
RETURNS bit
AS
/*
--EXAMPLE
print dbo.IsValueUnique() -- fail
print dbo.IsValueUnique(null) -- fail
print dbo.IsValueUnique(null,1) -- pass
print dbo.IsValueUnique('Friendly',1) -- pass
*/
BEGIN
DECLARE @count bit
set @count =
(
select count(1)
from dbo.MyTable
where @proposedValue is not null
and dbo.MyTable.MyPkColumn != @currentId
and dbo.MyTable.MyColumn = @proposedValue
)
RETURN case when @count = 0 then 1 else 0 end
END
GO
ALTER TABLE MyTable
WITH CHECK
add constraint CK_ColumnValueIsNullOrUnique
CHECK ( 1 = dbo.IsValueNullOrUnique([MyColumn],[MyPkColumn]) )
GO