1

複数のクライアントがデータの入力に使用するSaaSアプリケーションをセットアップしています。ただし、クライアントAが一意を強制したい場合、クライアントBが複製を許可したい場合がある特定のフィールドがあります。明らかに、1つのクライアントに重複を許可する場合、テーブルに一意の制約がない可能性があります。欠点は、一部のクライアントに一意の制約を適用したい場合、他の方法でそれを実行する必要があることです。

誰かがこのような問題に取り組んだことがありますか?もしそうなら、注意すべき一般的な解決策や潜在的な落とし穴は何ですか?

考えられる一意のフラグをチェックするトリガーが、これを正しく実行する唯一の方法である可能性があると考えています。私がビジネスレイヤーに依存している場合、アプリがすべての挿入の前に一意のチェックを行うという保証はありません。

解決策:
最初に一意のインデックスを検討しましたが、どのような種類の結合やルックアップも実行できず、値を表現するだけであるため、除外しました。また、クライアントが追加されたり、クライアントの一意性の設定が変更されたりするたびに、インデックスを変更したくありませんでした。

次に、CHECK CONSTRAINTSを調べ、いくつかの浮気の後、クライアントが一意であるかどうかを選択できる両方の仮想列に対してtrueを返す1つの関数を作成しました。

これは、チェック制約が必要なすべてを実行できることを確認するために使用したテストテーブル、データ、および関数です。

-- Clients Table
CREATE TABLE [dbo].[Clients](
    [ID] [int]  NOT NULL,
    [Name] [varchar](50) NOT NULL,
    [UniqueSSN] [bit] NOT NULL,
    [UniqueVIN] [bit] NOT NULL
) ON [PRIMARY]

-- Test Client Data
INSERT INTO Clients(ID, Name, UniqueSSN, UniqueVIN) VALUES(1,'A Corp',0,0)
INSERT INTO Clients(ID, Name, UniqueSSN, UniqueVIN) VALUES(2,'B Corp',1,0)
INSERT INTO Clients(ID, Name, UniqueSSN, UniqueVIN) VALUES(3,'C Corp',0,1)
INSERT INTO Clients(ID, Name, UniqueSSN, UniqueVIN) VALUES(4,'D Corp',1,1)

-- Cases Table
CREATE TABLE [dbo].[Cases](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [ClientID] [int] NOT NULL,
    [ClaimantName] [varchar](50) NOT NULL,
    [SSN] [varchar](12) NULL,
    [VIN] [varchar](17) NULL
) ON [PRIMARY]

-- Check Uniques Function
CREATE FUNCTION CheckUniques(@ClientID int)
RETURNS int -- 0: Ok to insert, 1: Cannot insert
AS
BEGIN
    DECLARE @SSNCheck int
    DECLARE @VinCheck int
    SELECT @SSNCheck = 0
    SELECT @VinCheck = 0
    IF (SELECT UniqueSSN FROM Clients WHERE ID = @ClientID) = 1
    BEGIN
        SELECT @SSNCheck = COUNT(SSN) FROM Cases cs WHERE ClientID = @ClientID AND (SELECT COUNT(SSN) FROM Cases c2 WHERE c2.SSN = cs.SSN) > 1
    END
    IF (SELECT UniqueVIN FROM Clients WHERE ID = @ClientID) = 1
    BEGIN
        SELECT @VinCheck = COUNT(VIN) FROM Cases cs WHERE ClientID = @ClientID AND (SELECT COUNT(VIN) FROM Cases c2 WHERE c2.VIN = cs.VIN) > 1
    END
    RETURN @SSNCheck + @VinCheck
END

-- Add Check Constraint to table
ALTER TABLE Cases
ADD Constraint chkClientUniques CHECK(dbo.CheckUniques(ClientID) = 0)

-- Now confirm constraint using test data

-- Client A: Confirm that both duplicate SSN and VIN's are allowed
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(1, 'Alice', '111-11-1111', 'A-1234')
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(1, 'Bob', '111-11-1111', 'A-1234')

-- Client B: Confirm that Unique SSN is enforced, but duplicate VIN allowed
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(2, 'Charlie', '222-22-2222', 'B-2345') -- Should work
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(2, 'Donna', '222-22-2222', 'B-2345') -- Should fail
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(2, 'Evan', '333-33-3333', 'B-2345') -- Should Work

-- Client C: Confirm that Unique VIN is enforced, but duplicate SSN allowed
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(3, 'Evan', '444-44-4444', 'C-3456') -- Should work
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(3, 'Fred', '444-44-4444', 'C-3456') -- Should fail
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(3, 'Ginny', '444-44-4444', 'C-4567') -- Should work

-- Client D: Confirm that both Unique SSN and VIN are enforced
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(4, 'Henry', '555-55-5555', 'D-1234') -- Should work
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(4, 'Isaac', '666-66-6666', 'D-1234') -- Should fail
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(4, 'James', '555-55-5555', 'D-2345') -- Should fail
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(4, 'Kevin', '555-55-5555', 'D-1234') -- Should fail
INSERT INTO Cases (ClientID, ClaimantName, SSN, VIN) VALUES(4, 'Lisa', '777-77-7777', 'D-3456') -- Should work

編集:
重複チェックでNULL値をキャッチするために関数を数回変更する必要がありましたが、すべてが機能しているように見えます。

4

8 に答える 8

3

1つのアプローチは、一意ではなくCHECK制約を使用することです。このCHECK制約は、SCALAR関数によってサポートされます。

  1. 入力としてクライアントIDを取ります
  2. 重複が許可されているかどうかを確認するために、ルックアップテーブルに対してClientIDを相互参照します(client.dups)
  3. 許可されていない場合は、テーブル内の重複を確認してください

何かのようなもの

ALTER TABLE TBL ADD CONSTRAINT CK_TBL_UNIQ CHECK(dbo.IsThisOK(clientID)=1)
于 2011-01-13T22:34:42.623 に答える
2

これは、SQL Server 2008で完全に可能です(ここのSQL Server 2008ボックスでテスト済み):

create table a
(
cx varchar(50)
);

create unique index ux_cx on a(cx) where cx <> 'US';

insert into a values('PHILIPPINES'),('JAPAN'),('US'),('CANADA');


-- still ok to insert duplicate US
insert into a values('US');

-- will fail here
insert into a values('JAPAN');

関連記事:http ://www.ienablemuch.com/2010/12/postgresql-said-sql-server2008-said-non.html

于 2011-01-13T23:27:47.583 に答える
2

各クライアントのテーブル内の行を識別できる場合は、DBMSに応じて、次のようにすることができます。

一意のインデックスを作成uq_some_col
   ON the_table(some_column、other_column、client_id)
   WHERE client_id IN(1,2,3);

(上記はPostgreSQLに有効であり、SQL Server 2005だと思います)

ダウンサイズは、それらの列が一意である必要がある新しいクライアントが追加されるたびに、そのインデックスを再作成する必要があるということです。

ほとんどの場合、適切なエラーメッセージを表示できるようにするために、ビジネスレイヤーにもいくつかのチェックがあります。

于 2011-01-13T22:35:49.110 に答える
1

これを使ってできることがいくつかありますが、これをいつ/どのように処理したいかによって異なります。

  1. CheckConstrainを使用してこれを変更し、それを使用していたクライアントに基づいてさまざまなルックアップを実行できます。
  2. ビジネス層を使用してこれを処理することはできますが、生のデータベース更新から保護することはできません。

私は個人的に、特にクライアントの数が多い場合、#1を維持するのが難しくなる可能性があることを発見しました。ビジネスレベルでそれを行う方がはるかに簡単であり、一元化された場所でそれを制御できることがわかりました。

これらは、クライアントごとのテーブルや他のオプションなど、同様に機能する可能性のある他のオプションですが、これら2つは、少なくとも私が見た中で最も一般的なものです。

于 2011-01-13T22:33:16.597 に答える
1

ヘルパー列を追加できます。この列は、重複を許可するアプリケーションの主キーと、他のアプリケーションの定数値に等しくなります。次に、に一意性制約を作成しますUniqueHelper, Col1

非重複クライアントの場合、ヘルパー列に定数があり、列が一意になるように強制されます。

複製列の場合、ヘルパー列は主キーと等しいため、その列だけで一意性制約が満たされます。そのアプリケーションは、任意の数の複製を追加できます。

于 2011-01-13T22:35:15.613 に答える
1

1つの可能性は、一意性を選択的に適用できるBEFOREINSERTトリガーとBEFOREUPDATEトリガーを使用することです。

また、別の可能性(一種の応急修理)は、一方の顧客の一意の値ともう一方の顧客の重複値が入力される追加のダミーフィールドを持つことです。次に、ダミーフィールドと表示フィールドの組み合わせに一意のインデックスを作成します。

于 2011-01-13T22:36:11.683 に答える
0

@ニール。私は上記のコメントで、すべてを同じテーブルに入れる理由を尋ねましたが、あなたは私のコメントのその側面を単に無視し、すべてが「単純で単純」であると言いました。Saasコンテキストでの条件付き制約アプローチの欠点を本当に聞きたいですか?

Saasアプリケーションのこのテーブルに最終的に組み込む必要のあるルールのセットがいくつあるかはわかりません。バリエーションは2つだけですか?

パフォーマンスは考慮事項です。各顧客は専用の条件付きインデックスにアクセスできますが、ベーステーブルからのデータのフェッチは、追加の顧客からのデータが追加されてテーブルが大きくなるにつれて、ますます遅くなる可能性があります。

Saasアプリケーションを開発している場合は、必要に応じて専用のトランザクションテーブルを使用します。顧客は、郵便番号、郡などの標準テーブルを共有でき、Products、Categories、WidgetTypesなどのドメイン固有のテーブルも共有できます。私はおそらく、現在の顧客の正しいテーブルが選択され、構築中のステートメントに配置されたストアドプロシージャで動的SQLステートメントを構築します。

        sql = "select * from " + DYNAMIC_ORDERS_TABLE + " where ... ")

動的ステートメントを常にコンパイルする必要があるためにパフォーマンスが低下した場合は、専用のストアドプロシージャジェネレーターを作成することを検討してください。sp_ORDERS_SELECT_25_v1.0{ここで、「25」はSaasアプリの特定のユーザーに割り当てられたIDであり、バージョンサフィックス}。

条件付きインデックスを利用するには、アドホッククエリのすべてのWHERE句に顧客IDを追加する必要があるため、動的SQLを使用する必要があります。

        sql = " select * from orders  where ... and customerid = " + CURRENT_CUSTOMERID

条件付きインデックスには顧客/ユーザー列が含まれるため、顧客の行のサブセットのみがテーブルから選択されるようにするには、列をすべてのクエリの一部にする必要があります。

したがって、すべてが完了したら、専用のテーブルを作成するために必要な労力を実際に節約し、パンとバターのクエリで動的SQLを回避できます。パンとバターのクエリ用に動的SQLを作成するのはそれほど手間がかからず、同じ共有テーブルで複数の顧客固有のインデックスを管理するよりも確かに面倒ではありません。また、動的SQLを作成している場合は、すべてのクエリにcustomerid = 25句を追加するのと同じくらい簡単に、専用のテーブル名に置き換えることができます。動的SQLのパフォーマンスの低下は、専用テーブルのパフォーマンスの向上によって十分に相殺されます。

PSアプリが1年ほど実行されていて、複数の顧客がいて、テーブルが大きくなっているとしましょう。別の顧客とその顧客固有のインデックスの新しいセットを大規模な本番テーブルに追加するとします。通常の営業時間中にこれらの新しいインデックスと制約をスリップストリームできますか、それとも使用量が比較的少ない時間にこれらのインデックスの作成をスケジュールする必要がありますか?

于 2011-01-14T12:33:05.427 に答える
-1

別々のユニバースからのデータを同じテーブルに混在させることの利点が何であるかを明確にしないでください。

一意性の制約はエンティティ定義の一部であり、各エンティティには独自のテーブルが必要です。別のテーブルを作成します。

于 2011-01-13T22:49:12.247 に答える