MySQL ON DUPLICATE KEY UPDATE mit null-Spalte in eindeutiger Schlüssel

Unserer MySQL-web-analytics-Datenbank enthält eine zusammenfassende Tabelle, die aktualisiert wird, den ganzen Tag als neue Aktivität wird importiert. Wir nutzen ON DUPLICATE KEY UPDATE damit die Verdichtung überschreibt frühere Berechnungen, aber Schwierigkeiten haben, weil eine der Spalten in der Zusammenfassung Tabelle EINDEUTIGER SCHLÜSSEL ist ein optionaler Fremdschlüssel und NULL-Werte enthält.

Diese Null-Werte sind gemeint "nicht vorhanden, und alle diese Fälle sind äquivalent". Natürlich, MySQL in der Regel behandelt Null-Werte im Sinne von "unbekannt ist, und alle diese Fälle sind nicht gleichwertig".

Grundlegende Struktur ist wie folgt:

"Tätigkeit" - Tabelle enthält einen Eintrag für jede Sitzung, jede Zugehörigkeit zu einer Kampagne, mit optionalen filter-und Transaktions-IDs für einige Einträge.

CREATE TABLE `Activity` (
    `session_id` INTEGER AUTO_INCREMENT
    , `campaign_id` INTEGER NOT NULL
    , `filter_id` INTEGER DEFAULT NULL
    , `transaction_id` INTEGER DEFAULT NULL
    , PRIMARY KEY (`session_id`)
);

Einer "Zusammenfassung" Tabelle mit täglichen rollups der Gesamtzahl der Sitzungen, die in der Aktivität Tisch, ein d die Gesamtzahl der auf diese Sitzungen enthalten eine Transaktions-ID. Diese Zusammenfassungen werden aufgeteilt, mit einer für jede Kombination von Kampagne und (optional) filter. Dies ist eine nicht transaktionale Tabelle mit MyISAM.

CREATE TABLE `Summary` (
    `day` DATE NOT NULL
    , `campaign_id` INTEGER NOT NULL
    , `filter_id` INTEGER DEFAULT NULL
    , `sessions` INTEGER UNSIGNED DEFAULT NULL
    , `transactions` INTEGER UNSIGNED DEFAULT NULL
    , UNIQUE KEY (`day`, `campaign_id`, `filter_id`)
) ENGINE=MyISAM;

Die tatsächliche Verdichtung Abfrage ist so etwas wie das folgende ein, wobei die Anzahl der Sitzungen und Transaktionen, dann wird die Gruppierung durch die Kampagnen-und (optional) filter.

INSERT INTO `Summary` 
    (`day`, `campaign_id`, `filter_id`, `sessions`, `transactions`)
    SELECT `day`, `campaign_id`, `filter_id
        , COUNT(`session_id`) AS `sessions`
        , COUNT(`transaction_id` IS NOT NULL) AS `transactions`
    FROM Activity
    GROUP BY `day`, `campaign_id`, `filter_id`
ON DUPLICATE KEY UPDATE
    `sessions` = VALUES(`sessions`)
    , `transactions` = VALUES(`transactions`)
;

Alles funktioniert Super, außer für die Zusammenfassung von Fällen, in denen die filter_id ist NULL. In diesen Fällen werden die ON DUPLICATE KEY UPDATE-Klausel nicht mit der vorhandenen Reihe, und eine neue Zeile geschrieben wird, jedes mal. Dies ist aufgrund der Tatsache, dass "NULL != NULL". Was wir brauchen, ist jedoch "NULL = NULL" beim Vergleich der eindeutige Schlüssel.

Ich bin auf der Suche nach Ideen für workarounds oder feedback zu denen wir gekommen sind so weit. Workarounds, die wir gedacht haben, der so weit Folgen.

  1. Löschen Sie alle summary-Einträge mit einer NULL-Schlüssel-Wert vor der Ausführung der Verdichtung. (Dies ist, was wir jetzt tun)
    Dies hat den negativen Nebeneffekt der Rückgabe von Ergebnissen mit fehlenden Daten, wenn eine Abfrage ausgeführt wird, während der Verdichtung Prozess.

  2. Ändern Sie die STANDARD-NULL-Spalte DEFAULT 0, wodurch der EINDEUTIGE SCHLÜSSEL werden konsequent abgestimmt.
    Dies hat den negativen Nebeneffekt übermäßig erschwert die Entwicklung von Abfragen für die zusammenfassende Tabelle aus. Es zwingt uns, eine Menge "FALL filter_id = 0 then NULL ELSE filter_id ENDE", und macht für unangenehme Eintritt, da alle anderen Tische haben die tatsächlichen Null-Werte für filter_id.

  3. Erstellen Sie eine Ansicht, die Renditen "FALL filter_id = 0 then NULL ELSE filter_id ENDE", und mit dieser Ansicht statt die Tabelle direkt.
    Die übersichtstabelle enthält ein paar hundert tausend Zeilen, und ich habe gesagt, zeigen Sie die Leistung sehr schlecht ist.

  4. Ermöglichen die doppelten Einträge werden erstellt, und löschen Sie alte Einträge nach Verdichtung abgeschlossen ist.
    Hat der ähnliche Probleme wie löschen Sie vor der Zeit.

  5. Fügen Sie ein Surrogat Spalte enthält 0 für NULL, und verwenden Sie, dass Ersatz in den EINDEUTIGEN SCHLÜSSEL (eigentlich könnten wir PRIMÄRE SCHLÜSSEL, wenn Sie alle Spalten, die NICHT NULL sind).

    Diese Lösung scheint vernünftig, außer dass das Beispiel oben ist nur ein Beispiel; die tatsächlichen Datenbank enthält ein halbes Dutzend Zusammenfassung Tabellen, eine davon enthält vier nullwertfähige Spalten in den EINDEUTIGEN SCHLÜSSEL. Es gibt Bedenken von einigen, dass der overhead ist zu viel.

Haben Sie eine bessere Lösung, die die Struktur einer Tabelle, aktualisieren von Prozess-oder MySQL-best practice-was kann helfen?

EDIT: Zu klären, die "Bedeutung der null"

Die Daten in der Zusammenfassung die Zeilen mit NULL-Spalten werden als zusammen zu gehören, nur in dem Sinne, dass ein single "catch-all" - Zeile in der Zusammenfassung der Berichte, Zusammenfassung derjenigen Elemente, für die die Daten Punkt nicht existiert oder unbekannt ist. So im Rahmen der Zusammenfassung der Tabelle selbst, die Bedeutung ist "die Summe der Einträge, für die kein Wert bekannt". Innerhalb der relationalen Tabellen, auf der anderen Seite, diese sind wirklich NULL Ergebnisse.

Der einzige Grund, warum man Sie in einen eindeutigen Schlüssel auf der übersichtstabelle ist zu ermöglichen die automatische update (by ON DUPLICATE KEY UPDATE), wenn re-Berechnung der zusammenfassenden berichten.

Vielleicht einen besseren Weg zu beschreiben, ist es durch das spezifische Beispiel, dass man in der übersicht, Tabellen, Gruppen, Resultate geografisch durch die zip-code-Präfix des Geschäfts angegebene Adresse durch den Beklagten. Nicht alle Befragten stellen eine business-Adresse, also die Beziehung zwischen der Transaktion und die Adressen-Tabelle ist durchaus richtig NULL. In der Zusammenfassung der Tabelle für diese Daten, wird eine Zeile generiert, die für jede zip-code Präfix, mit der Zusammenfassung der Daten in diesem Bereich. Eine zusätzliche Zeile erzeugt, um die Zusammenfassung von Daten, für die keine zip-code-Präfix bekannt.

Änderung der übrigen Daten Tabellen haben eine explizite "THERE_IS_NO_ZIP_CODE" 0-Wert, und platzieren Sie einen besonderen Rekord in der ZipCodePrefix Tabelle mit diesem Wert, dass es falsch ist--, dass die Beziehung wirklich NULL ist.

InformationsquelleAutor ryandenki | 2009-08-19
Schreibe einen Kommentar