3

null列ではなく1ビットの列を持つテーブルを作成していますIsDefaultUserId(同じテーブルのフィールド)ごとにデフォルト値が1つだけになるようにする制約を作成する必要があります。デフォルト以外の値が多数ある可能性があるため、これに一意性制約を使用することはできません。

MS SQL Server 2008を使用してこれを行うための最良のアプローチは何ですか?

ありがとう。

4

7 に答える 7

3

私が見る最も簡単な方法は、UDF(ユーザー定義関数)を使用したチェック制約です。

たとえば、ここを見てください。 http://sqljourney.wordpress.com/2010/06/25/check-constraint-with-user-defined-function-in-sql-server/

テストされていない例

CREATE FUNCTION dbo.CheckDefaultUnicity(@UserId int)
RETURNS int
AS 
BEGIN
   DECLARE @retval int
   SELECT @retval = COUNT(*) FROM <your table> where UserId = @UserId and <columnwithDefault> = 1-- or whatever is your default value
   RETURN @retval 
END;
GO

テーブルを変更します

ALTER TABLE <yourTable> 
ADD CONSTRAINT Ck_UniqueDefaultForUser 
CHECK (dbo.CheckDefaultUnicity(UserId) <2)
于 2012-06-11T11:20:23.707 に答える
1

もう1つの比較的単純なオプションは、を使用することCLUSTERED INDEXED VIEWです。これの要点は

  • UserIDビュー内のテーブルからすべてを選択しIsDefault=1ます。
  • に一意のインデックスを追加しますUserID

クラスター化インデックスビュー

CREATE VIEW dbo.VIEW_Users_IsDefault WITH SCHEMABINDING AS 
  SELECT  UserID, IsDefault
  FROM    dbo.Users
  WHERE   IsDefault = 1
GO  

CREATE UNIQUE CLUSTERED INDEX UIX_VIEW_USERS_ISDEFAULT 
  ON dbo.VIEW_Users_IsDefault (UserID)
GO

テストスクリプト

BEGIN TRAN

CREATE TABLE dbo.Users (UserID INT, IsDefault BIT)
GO

CREATE VIEW dbo.VIEW_Users_IsDefault WITH SCHEMABINDING AS 
  SELECT  UserID, IsDefault
  FROM    dbo.Users
  WHERE   IsDefault = 1
GO  

CREATE UNIQUE CLUSTERED INDEX UIX_VIEW_USERS_ISDEFAULT ON dbo.VIEW_Users_IsDefault (UserID)
GO

INSERT INTO dbo.Users VALUES (1, 0)
INSERT INTO dbo.Users VALUES (1, 1)
INSERT INTO dbo.Users VALUES (1, 1)  -- Fails because of clustered index

ROLLBACK TRAN
于 2012-06-11T11:36:10.253 に答える
1

チェック制約は間違いなく機能しますが、私の意見では設計上の選択としては適切ではありません。その理由は、制約のUDFが次のようになるためです。

SELECT @Count = COUNT(UserId) 
FROM   User
WHERE  IsDefault = 1
GROUP BY UserId
HAVING COUNT(UserId) > 1

IF @Count > 0 
  ....'FAIL

これは2つの列に関係するため、テーブルレベルの制約である必要があり、レコードが多いほど、挿入/更新/削除にかかる時間が遅くなります。

より良いオプションは、ストアドプロシージャを介してのみそのテーブルへのアクセスを許可することです。そのため、挿入/更新の前に、非常に迅速に実行できます。

IF EXISTS(SELECT UserId FROM User where UserId = @UserId and IsDefault = 1)

挿入/更新/削除の前

ただし、ORMを使用している可能性があり、システムにストアドプロシージャを含めたくない場合があるため、テーブルのデザインを以下に変更できます。これは、

tblUser:UserId、FirstName、Suranameなど

tblUserDefault:UserId(一意の制約)

システムでIsDefaultが何を表しているのかわからないので、上記ではユーザーがデフォルトであるかどうかを想定しています。あなたがそれを参照として使うことができる人は誰でも。これにより、USPまたは恐ろしいテーブル全体のチェック制約(またはトリガー)を使用せずに制約を適用でき、適切なORMにマッピングできます。

于 2012-06-11T11:37:42.260 に答える
0

はどうですかCHECK Constraints。ここを参照してください:http://msdn.microsoft.com/en-us/library/ms188258 (v = sql.105).aspx

ALTER TABLE yourtable
ADD CONSTRAINT IsDefaultChecked CHECK (IsDefault = T );
于 2012-06-11T11:20:03.723 に答える
0

この場合、トリガーを使用することをお勧めします。ユーザーがデフォルトを変更しているとき、トリガーは現在のユーザーの現在のデフォルトを自動的にfalseに反転させる可能性があります。

基本的に、AFTER挿入/更新トリガーを使用して、IsDefault値が1に設定されている挿入/更新のすべてのユーザーのIsDefault列を0に設定します。

CREATE TRIGGER dbo.tr_default
ON dbo.MyTable
AFTER INSERT, UPDATE 
AS

  if(exists(select * from inserted where IsDefault = 1)
  begin

      update dbo.MyTable
        set IsDefault = 0
      from inserted i
      join dbo.MyTable t on i.userid = t.userid
      where i.IsDefault = 1
        and i.TheValue != t.TheValue

  end
于 2012-06-11T11:31:40.910 に答える
0

これまでのところ、どの回答にも問題はありませんがCHECK CONSTRAINTSTRIGGERS少し逆の解決策のように思えます。

UserID私はあなたのテーブルでテーブルへの外部キーであると仮定することしかできないUserので、デフォルトとしてマークするのではなく、デフォルトのCardIDを格納するためにUserテーブルに列を追加してみませんか?これにより、ユーザーがコストのかかるトリガー/制約なしに複数のデフォルトのCardIDを持つことは不可能になります。列をnull不可にすると、必要に応じてユーザーがデフォルトのCardIDを持たないようにすることもできなくなります。

于 2012-06-11T11:38:57.207 に答える
0

トリガーと制約のソリューションの方が優れていると思いますが、ストアドプロシージャを介して挿入/更新を制御する場合、はるかに簡単なアプローチは、最初に競合する行を更新することです(新しいデフォルトが常に優先されると仮定します)。

ALTER PROCEDURE dbo.UserWhateverTable_<action>
  @UserID INT,
  @CardID INT
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE dbo.UserWhatever 
      SET IsDefault = 0 
      WHERE UserID = @UserID
        AND CardID = @CardID
        AND IsDefault = 1;

    -- insert or update here
END
GO

実際、トリガーまたは制約で保護することに加えて(DMLプロシージャでビジネスロジックが100%明確になるように)これを行うことは悪い考えではないかもしれません(プロシージャの外部で更新が行われる場合をキャッチするため) 。

于 2012-06-11T11:50:03.343 に答える