質問

3列A、B、およびCのテーブルルールがあるとします。データがシステムに入ると、ルールテーブルの行が私のデータと一致しているかどうかを知りたいです。 、すべてのデータが一致します。明らかなSQLは次のとおりです。

SELECT * FROM RULES
WHERE (A = :a OR A IS NULL)
  AND (B = :b OR B IS NULL)
  AND (C = :c OR C IS NULL)

だから私にルールがある場合:

RULE    A        B        C
1       50       NULL     NULL
2       51       xyz      NULL
3       51       NULL     123
4       NULL     xyz      456

(50、XYZ、456)の入力は、ルール1と4と一致します。

質問: これを行うためのより良い方法はありますか? 3つのフィールドのみで問題ありません。しかし、実際のテーブルには15の列があり、そのSQLがどれだけうまくスケーリングするか心配しています。

投機: 私が思いついた代替SQLステートメントは、テーブルに追加の列を追加して、nullではないフィールドの数がカウントされます。 (したがって、例では、ルール1-4のこの列値はそれぞれ1、2、2、2です。)この「col_count」列を使用すると、選択は次のとおりです。

SELECT * FROM RULES
WHERE (CASE WHEN A = :a THEN 1 ELSE 0 END)
    + (CASE WHEN B = :b THEN 1 ELSE 0 END)
    + (CASE WHEN C = :c THEN 1 ELSE 0 END)
    = COL_COUNT

残念ながら、これらのアプローチのどれがパフォーマンスが向上するかを見つけるのに十分なサンプルデータがありません。ランダムなルールの作成を開始する前に、ここでより良いアプローチがあるかどうかを尋ねると思いました。

ノート: ここでは、データマイニング技術と列の制約は実行可能ではありません。データはシステムに入るときにチェックする必要があるため、すぐにパス/故障してフラグを立てることができます。また、ユーザーはルールの追加または削除を制御するため、ルールを列の制約または他のデータ定義ステートメントに変換することはできません。

最後に、最終的には、データが渡されないというすべてのルールのリストが必要です。ソリューションは、最初の障害で中止することはできません。

ありがとう。

役に立ちましたか?

解決

最初に提供されたクエリは完璧です。あなたが話している列を追加すると、すべてのエントリのnullプロパティがとにかくチェックされているため、nullと比較するたびに虚偽が生じるため、これ以上速度が得られることを本当に疑います。だから私はそれを推測するでしょう x=y に拡張されています x IS NOT NULL AND x=y 初めの。たぶん他の誰かがそれを明確にすることができます。

私が考えることができる他のすべての最適化には、事前計算またはキャッシュが含まれます。特定のルールに一致する[一時]テーブルを作成したり、一致するルールを保持している列を追加したりできます。

他のヒント

行/ルールが多すぎますか?そうでない場合(主観的ですが、10,000未満と言う)、すべての列のインデックスを作成できます。

これにより、速度が大幅に向上し、インデックスはそれほどスペースをとることはありません。

ルールの巨大なテーブルを作成する予定がない場合は、すべての列をインデックスすると、あなたのアプローチは問題ありません。

値によってルールテーブルのインデックスを作成してみませんか?そうすれば、できます

SELECT myvalue FROM RULES_A

あなたが本当に持っているのはルールとルールセットのように聞こえます。そのようにモデル化すると、この特定のコーディングがはるかにシンプルになるだけでなく、16列が必要だと判断したときにモデルを拡張可能にします。

例えば:

CREATE TABLE Rules (
    rule_id         INT         NOT NULL,
    rule_category   CHAR(1)     NOT NULL, -- This is like your column idea
    rule_int_value  INT         NULL,
    rule_str_value  VARCHAR(20) NULL,
    CONSTRAINT PK_Rules PRIMARY KEY CLUSTERED (rule_id),
    CONSTRAINT CK_Rules_one_value CHECK (rule_int_value IS NULL OR rule_str_value IS NULL)
)

CREATE TABLE Rule_Sets (
    rule_set_id INT NOT NULL,
    rule_id     INT NOT NULL,
    CONSTRAINT PK_Rule_Sets PRIMARY KEY CLUSTERED (rule_set_id, rule_id)
)

指定されたルールに一致するデータ

INSERT INTO Rules (rule_id, rule_category, rule_int_value, rule_str_value)
VALUES (1, 'A', 50, NULL)
INSERT INTO Rules (rule_id, rule_category, rule_int_value, rule_str_value)
VALUES (2, 'A', 51, NULL)
INSERT INTO Rules (rule_id, rule_category, rule_int_value, rule_str_value)
VALUES (3, 'B', NULL, 'xyz')
INSERT INTO Rules (rule_id, rule_category, rule_int_value, rule_str_value)
VALUES (4, 'C', 123, NULL)
INSERT INTO Rules (rule_id, rule_category, rule_int_value, rule_str_value)
VALUES (5, 'C', 456, NULL)

INSERT INTO Rule_Sets (rule_set_id, rule_id) VALUES (1, 1)
INSERT INTO Rule_Sets (rule_set_id, rule_id) VALUES (2, 2)
INSERT INTO Rule_Sets (rule_set_id, rule_id) VALUES (2, 3)
INSERT INTO Rule_Sets (rule_set_id, rule_id) VALUES (3, 2)
INSERT INTO Rule_Sets (rule_set_id, rule_id) VALUES (3, 4)
INSERT INTO Rule_Sets (rule_set_id, rule_id) VALUES (4, 3)
INSERT INTO Rule_Sets (rule_set_id, rule_id) VALUES (4, 5)

あなたが期待するのと同じ答えを確認するテストスクリプト:

DECLARE
    @a  INT,
    @b  VARCHAR(20),
    @c  INT

SET @a = 50
SET @b = 'xyz'
SET @c = 456

SELECT DISTINCT
    rule_set_id AS failed_rule_set_id
FROM
    Rule_Sets RS
WHERE
    NOT EXISTS (SELECT * FROM Rules R WHERE R.rule_id = RS.rule_id AND @a = R.rule_int_value) AND
    NOT EXISTS (SELECT * FROM Rules R WHERE R.rule_id = RS.rule_id AND @b = R.rule_str_value) AND
    NOT EXISTS (SELECT * FROM Rules R WHERE R.rule_id = RS.rule_id AND @c = R.rule_int_value)

個々のパラメーターとしてではなく、設定ベースのフォームで入力データを表示できる場合、最終的なSQLステートメントはより動的であり、追加の列を追加する際に成長する必要はありません。

SELECT * FROM RULES
 WHERE (A = :a OR A IS NULL)
   AND (B = :b OR B IS NULL)
   AND (C = :c OR C IS NULL);

RBDMSに応じて、これはより効率的であるかもしれないし、そうでないかもしれませんが、それほどではありません。

SELECT * FROM RULES
 WHERE coalesce(A, :a) = :a
   AND coalesce(B, :b) = :b 
   AND coalesce(C, :c) = :c ;

mysql(rbdmsはこれを異なる方法で行う場合があります)、このクエリは index aではなくスキャン ref_or_null 該当するインデックスがある場合はスキャンします。インデックスがすべての列をカバーする場合、インデックス全体を使用できます(実際、インデックスがすべての列をカバーする場合、インデックスはインデックスを テーブル)。

クエリで、a ref_or_null アクセスはではなく行われます index アクセス、およびマルチコラムインデックスの最初の列のみが使用されます。と ref_or_null, 、MySQLは一致のインデックスを検索し、もう一度nullを検索する必要があります。そのため、インデックスを2回使用しますが、インデックス全体を使用しないでください。

しかし、Coalesceを使用すると、各列値でCoalesce関数を実行するオーバーヘッドがあります。これはおそらく、あなたが持っているルールの数、各行の列の数、および使用されたインデックス(ある場合)に依存するでしょう。

読みやすいかどうかは意見の問題です。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top