假设您有一个具有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语句涉及,在表中添加了一个额外的列,并计算了多少个字段未空。 (因此,在示例中,规则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

不幸的是,我没有足够的示例数据来找到我们的哪种方法可以更好地表现。在开始创建随机规则之前,我想我在这里问是否有更好的方法。

笔记: 数据挖掘技术和列约束在这里不可行。必须在进入系统时检查数据,以便可以立即标记通过/失败。并且,用户控制规则的添加或删除,因此我无法将规则转换为列约束或其他数据定义语句。

最后一件事,最后我需要数据未能通过的所有规则的列表。该解决方案在第一次故障时无法中止。

谢谢。

有帮助吗?

解决方案

您提供的第一个查询非常完美。我真的怀疑添加您所说的列会给您带来更多速度,因为无论如何都会检查每个条目的无效属性,因为与无效的每个比较都会产生faster。所以我想 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 扫描而不是 ref_or_null 扫描,如果有适用的索引。如果索引覆盖了所有列,则允许使用整个索引(实际上,如果索引涵盖了所有列,则索引 桌子)。

与您的查询一起 ref_or_null 访问是完成的,而不是 index 访问,仅使用多列索引中的第一列。和 ref_or_null, ,MySQL必须搜索索引以进行匹配,然后再次搜索nulls。因此,我们两次使用索引,但切勿使用整个索引。

但是,使用Cocece,您拥有在每个列值上执行Cocece函数的开销。更快的速度可能取决于您有多少规则,每行中有多少列以及使用的索引(如果有)。

是否更可读性是一个意见问题。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top