質問
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関数を実行するオーバーヘッドがあります。これはおそらく、あなたが持っているルールの数、各行の列の数、および使用されたインデックス(ある場合)に依存するでしょう。
読みやすいかどうかは意見の問題です。