Ich möchte durchsetzen, dass nur ein Datensatz in einer Tabelle als "Standard" -Wert für andere Abfragen oder Ansichten gilt, die möglicherweise auf diese Tabelle zugreifen.
Grundsätzlich möchte ich garantieren, dass diese Abfrage immer genau eine Zeile zurückgibt:
SELECT ID, Zip
FROM PostalCodes
WHERE isDefault=True
Wie würde ich das in SQL machen?
mysql
database-design
Emaynard
quelle
quelle
PostalCodes
leer ist? Wenn eine Zeile bereits die Eigenschaft a hat, sollte verhindert werden, dass sie auf false gesetzt wird, es sei denn, eine andere Zeile (falls vorhanden) wird in derselben SQL-Anweisung auf true gesetzt. Können Nullzeilen die Eigenschaft zwischen Transaktionsgrenzen haben? Soll die letzte Zeile in der Tabelle gezwungen werden, die Eigenschaft zu besitzen, und soll verhindert werden, dass sie gelöscht wird? Die Erfahrung zeigt mir, dass "genau eine Reihe garantieren" in der Realität etwas anderes bedeutet, oft einfach "höchstens eine Reihe".Antworten:
Bearbeiten: Dies ist auf SQL Server ausgerichtet, bevor wir MySQL kannten
Es gibt
4 bis5 Möglichkeiten, dies zu tun: in der Reihenfolge vom Gewünschten zum WenigstenWir wissen nicht, welches RDBMS dies ist, obwohl möglicherweise nicht alle zutreffen
Der beste Weg ist ein gefilterter Index. Hierbei wird DRI verwendet, um die Eindeutigkeit aufrechtzuerhalten.
Berechnete Spalte mit Eindeutigkeit (siehe Jack Douglas 'Antwort) (hinzugefügt durch Edit 2)
Eine indizierte / materialisierte Ansicht, die einem gefilterten Index mit DRI gleicht
Auslöser (wie bei anderen Antworten)
Überprüfen Sie die Einschränkung mit einer UDF. Dies ist nicht sicher für Parallelität und Snapshot-Isolation. Siehe Eins Zwei Drei Vier
Beachten Sie, dass die Anforderung für "einen Standard" in der Tabelle und nicht in der gespeicherten Prozedur enthalten ist. Die gespeicherte Prozedur hat dieselben Parallelitätsprobleme wie eine Check-Einschränkung mit einem udf
Hinweis: SO oft gefragt:
quelle
Hinweis: Diese Antwort wurde gegeben, bevor klar war, dass der Fragesteller etwas MySQL-spezifisches wollte. Diese Antwort ist voreingenommen gegenüber SQL Server.
Wenden Sie eine CHECK- Einschränkung auf die Tabelle an, die eine UDF aufruft, und stellen Sie sicher, dass der Rückgabewert <= 1 ist. Die UDF kann nur die Zeilen in der Tabelle WHERE zählen
isDefault = TRUE
. Dadurch wird sichergestellt, dass die Tabelle nicht mehr als eine Standardzeile enthält.Sie sollten der Spalte (je nach Plattform) eine Bitmap oder einen gefilterten Index
isDefault
hinzufügen, um sicherzustellen, dass diese UDF sehr schnell ausgeführt wird.Vorbehalte
PostalCodes
Tabelle durchführen. Dies ist jedoch weiterhin ein Leistungsproblem in Situationen, in denen Aktivitäten auf Gruppenbasis wahrscheinlich sind.Angesichts all dieser Einschränkungen empfehle ich, gbns Vorschlag eines gefilterten eindeutigen Index anstelle einer Prüfbedingung zu verwenden .
quelle
Hier ist eine gespeicherte Prozedur (MySQL Dialect):
Gehen Sie folgendermaßen vor, um sicherzustellen, dass Ihre Tabelle sauber ist und die gespeicherte Prozedur funktioniert, vorausgesetzt, ID 200 ist die Standardeinstellung:
Wie wäre es mit einem Trigger anstelle einer gespeicherten Prozedur?
Führen Sie die folgenden Schritte aus, um sicherzustellen, dass Ihre Tabelle sauber ist und der Trigger funktioniert, vorausgesetzt, ID 200 ist der Standardwert:
quelle
Sie können einen Trigger verwenden, um die Regeln anzuwenden. Wenn eine UPDATE- oder INSERT-Anweisung isDefault auf True setzt, kann die SQL im Trigger alle anderen Zeilen auf False setzen.
Sie müssen andere Situationen berücksichtigen, wenn Sie dies anwenden. Was soll zum Beispiel passieren, wenn mehr als eine Zeile in UPDATE oder INSERT isDefault auf True setzt? Welche Regeln gelten, um einen Verstoß gegen die Regel zu vermeiden?
Ist es auch möglich, dass es keinen Standard gibt? Was passiert, wenn ein Update den isDefault von True auf False setzt?
Sobald Sie die Regeln definiert haben, können Sie sie im Trigger erstellen.
Anschließend möchten Sie, wie in einer anderen Antwort angegeben, eine Prüfbedingung anwenden, um die Durchsetzung der Regeln zu unterstützen.
quelle
Folgendes erstellt eine Beispieltabelle und Daten:
Wenn Sie eine gespeicherte Prozedur erstellen, um das IsDefault-Flag zu setzen, und Berechtigungen zum Ändern der Basistabelle entfernen, können Sie dies mit der folgenden Abfrage erzwingen. Es würde jedes Mal einen Tabellenscan erfordern.
Abhängig von der Größe der Tabelle kann ein Index für IsDefault (DESC) dazu führen, dass eine oder beide der folgenden Abfragen einen vollständigen Scan verhindern.
Wenn Sie Berechtigungen nicht aus der Basistabelle entfernen können, die Integrität auf andere Weise erzwingen müssen und SQL2008 verwenden, können Sie einen gefilterten eindeutigen Index verwenden:
quelle
Für MySQL ohne gefilterte Indizes können Sie Folgendes tun. Es nutzt die Tatsache aus, dass MySQL mehrere NULL-Werte in eindeutigen Indizes zulässt und mithilfe einer dedizierten Tabelle und eines Fremdschlüssels einen Datentyp simuliert, der nur NULL und 1 als Werte haben kann.
Bei dieser Lösung würden Sie anstelle von true / false nur 1 / NULL verwenden.
Ich denke, das Einzige, was schief gehen kann, ist, wenn jemand der
DefaultFlag
Tabelle eine Zeile hinzufügt . Ich denke, das ist kein großes Problem, und Sie können es immer mit Berechtigungen beheben, wenn Sie wirklich sicher sein möchten.quelle
Das hat Ihnen im Wesentlichen Probleme bereitet, weil Sie das Feld an die falsche Stelle gelegt haben und das ist alles.
Haben Sie eine 'Defaults'-Tabelle mit PostalCode, die die ID enthält, nach der Sie suchen. Im Allgemeinen ist es auch gut, Ihre Tabellen in Übereinstimmung mit einem Datensatz zu benennen, sodass der Name Ihrer Anfangstabelle besser als "PostalCode" bezeichnet werden sollte. Die Standardeinstellungen sind eine einzelne Zeilentabelle, bis Sie Ihre Standardeinstellungen auf eine andere Konfiguration (z. B. Verlauf) spezialisieren müssen.
Die letzte Abfrage, die Sie möchten, lautet wie folgt:
quelle