Frage

Nehmen wir an, Sie haben eine Tabelle Regeln mit 3 Spalten A, B und C, wenn Daten in das System eintreten alle Datenübereinstimmungen. Der offensichtliche SQL ist:

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

Also, wenn ich Regeln habe:

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

Eine Eingabe von (50, xyz, 456) entspricht den Regeln 1 und 4.

Frage: Gibt es einen besseren Weg, dies zu tun? Mit nur 3 Feldern ist dies kein Problem. Die tatsächliche Tabelle wird jedoch 15 Spalten haben und ich mache mir Sorgen darüber, wie gut diese SQL -Skalen.

Spekulation: Eine alternative SQL -Aussage, in der ich mit einer Anzahl der Felder keine zusätzliche Spalte in die Tabelle hinzugefügt habe, die nicht null sind. (Im Beispiel ist dieser Spalten Wert für die Regeln 1-4 1, 2, 2 bzw. 2.) Mit dieser Spalte "col_count" könnte die Auswahl sein:

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

Leider habe ich nicht genügend Beispieldaten, um zu ermitteln, welche dieser Ansätze besser abschneiden würden. Bevor ich anfange, zufällige Regeln zu erstellen, dachte ich, ich würde hier fragen, ob es einen besseren Ansatz gab.

Notiz: Data Mining -Techniken und Spaltenbeschränkungen sind hier nicht möglich. Die Daten müssen überprüft werden, wenn sie in das System eintreten, und so kann sie sofort eingestuft werden. Und die Benutzer kontrollieren die Addition oder Entfernung von Regeln, sodass ich die Regeln nicht in Spaltenbeschränkungen oder andere Datendefinitionsanweisungen umwandeln kann.

Eine letzte Sache, am Ende brauche ich eine Liste aller Regeln, die die Daten nicht bestehen. Die Lösung kann beim ersten Versagen nicht abbrechen.

Vielen Dank.

War es hilfreich?

Lösung

Die erste Abfrage, die Sie zur Verfügung gestellt haben, ist perfekt. Ich bezweifle wirklich, dass das Hinzufügen der Spalte, von der Sie gesprochen haben, eine weitere Geschwindigkeit verleihen, da die Nicht -Null -Eigenschaft jedes Eintrags sowieso überprüft wird, da jeder Vergleich zu Null falsch ist. Also würde ich das vermuten x=y wird erweitert auf x IS NOT NULL AND x=y im Inneren. Vielleicht kann das jemand anderes klarstellen.

Alle anderen Optimierungen, die ich mir vorstellen kann, beinhalten die Vorkalkulation oder das Zwischenspeichern. Sie können [temporäre] Tabellen erstellen, die bestimmte Regeln entsprechen oder weitere Spalten hinzufügen, die übereinstimmende Regeln enthalten.

Andere Tipps

Gibt es zu viele Zeilen/Regeln? Wenn es nicht der Fall ist (das ist subjektiv, sondern weniger als 10.000), können Sie Indizes für alle Spalten erstellen.

Das würde die Geschwindigkeit erheblich erhöhen und die Indizes werden nicht viel Platz einnehmen.

Wenn Sie nicht planen, eine riesige Regelnstabelle zu erstellen, dann ist Ihr Ansatz in Ordnung, vorausgesetzt, Sie indexieren alle Spalten.

Warum nicht Indizes Ihrer Regelnstabelle nach den Werten machen? Dann kannst du

SELECT myvalue FROM RULES_A

It sounds like what you really have are Rules and Rule Sets. Modeling it that way will not only make this particular coding much simpler, but will also make the model expandable when you decide that you need a 16 columns.

For example:

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)
)

Some data that would match your given rules:

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)

A test script that confirms the same answer that you expect:

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)

If you can present the input data in a set-based form rather than as individual parameters then the final SQL statement can be more dynamic and wouldn't have to grow as you add additional columns.

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

Depending on your RBDMS, this might or might not be more efficient, though not by much:

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

In MySQL (your RBDMS may do this differently), this query allows an index scan rather than a ref_or_null scan, if there is an applicable index. If the index covers all columns, it allows the entire index to be used (and indeed, if the index covers all columns, the index is the table).

With your query, a ref_or_null access is done rather than an index access, and only the first column in a multi-column index is used. With ref_or_null, MySQL has to search the index for matches, then search again for nulls. So we use the index twice, but never use the whole index.

But with coalesce, you have the overhead of executing the coalesce function on each column value. Which is faster probably depends on how many rules you have, how many columns in each row, and the index used, if any.

Whether it's more readable is a matter of opinion.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top