Color scheme: Light
You are here: start » de » Dokumentation » Datenintegration und Datenzugriff » WKL Database Gateway » Konzepte » Schreiben und Schreibkonflikte

Schreiben und Schreibkonflikte

Schreiben ist so dynamisch wie Lesen: Methode, Tabelle, Schlüssel und Felder kommen aus der Anfrage, das SQL erzeugt der Dienst. Jede so erzeugte Änderung trifft genau einen Satz. Eine fremde Änderung seit dem Lesen erkennt der Dienst, statt sie zu überschreiben – sofern der Client die gelesene Version mit If-Match mitschickt; ohne sie oder mit If-Match: * gilt die letzte Änderung. Das gilt auf Wunsch des Betreibers auch für Tabellen ohne Zeilenversion und ohne Schlüssel. Anweisungen, die der Aufrufer selbst schreibt (geprüfte Anweisungen und die Durchreiche), können wie in SQL Server mehrere Sätze ändern; für sie gilt diese Seite nicht.

Wichtige Funktionen

  • Ein Satz über seinen vollständigen eindeutigen Schlüssel, nie über eine freie Bedingung.
  • Optimistische Sperre über die Zeilenversion (If-Match, in Access der Schreibkonflikt).
  • Berechneter Zeilenstempel für Tabellen ohne Zeilenversion.
  • Ändern und Löschen über die alten Werte für Tabellen ohne Schlüssel.

Grundkonzepte

Ein Satz, ein Schlüssel

Ein Satz wird über seinen vollständigen eindeutigen Schlüssel angesprochen, über nichts sonst. Der Schlüssel ist der Primärschlüssel der Tabelle, ersatzweise ihr erster eindeutiger Index. Fehlt ein Schlüsselteil oder steht ein weiteres Argument oder ein $filter daneben, wird die Anfrage abgewiesen. Ein Teilschlüssel oder eine freie Bedingung würde eine Anfrage, der ein Wert fehlt, jede Zeile ändern oder löschen lassen, auf die der Rest passt. Die erzeugte Anweisung sichert das zusätzlich ab: Sie läuft in einer Transaktion, zählt die getroffenen Zeilen und rollt bei allem außer genau einer zurück – bis zu ihrem eigenen Sicherungspunkt, sodass sie in einer Transaktion des Clients nur sich selbst zurücknimmt.

Optimistische Sperre

Optimistische Sperre über If-Match. Führt die Tabelle eine Zeilenversion – oder ist sie für den Zeilenstempel freigeschaltet –, kann ein Schreiben daran gebunden werden. Angenommen wird der Wert so, wie ihn das Lesen liefert (Base64), oder in der Schreibweise von SQL Server (0x…). Hat sich der Satz inzwischen geändert, antwortet der Dienst mit 412 und schreibt nichts. Ohne If-Match gewinnt der letzte Schreiber – bei den Feldern, die er nennt.

Tabellen ohne Zeilenversion: der Zeilenstempel

Eine Änderung, die keine fremde Änderung überschreiben darf, braucht einen Wert, der sich mit jeder Änderung des Satzes ändert. Eine Tabelle mit Zeilenversion hat ihn. Bei einer Tabelle ohne Zeilenversion bleibt einem Client nur, die alten Werte aller Spalten in die Bedingung zu stellen – so macht es Access über eine direkte Verbindung –, und das kann keine der beiden Oberflächen tragen: If-Match nennt ein Merkmal, keine Werteliste.

Für solche Tabellen berechnet der Dienst auf Wunsch einen Zeilenstempel: eine SHA-256-Prüfsumme über alle gespeicherten Spalten des Satzes, in der Datenbank selbst gebildet. Er erscheint wie eine Zeilenversion – als nur lesbare Spalte gw_rowstamp (binary(32)), als @odata.etag und Kopfzeile ETag, im Schemadokument mit Core.OptimisticConcurrency angekündigt – und wird beim Ändern und Löschen in derselben Anweisung neu berechnet und verglichen, die schreibt. Zwischen Prüfen und Schreiben kann sich deshalb niemand einschieben. Ein Client, der mit Zeilenversionen umgehen kann, braucht nichts weiter, die Datenbank keine neue Spalte.

Wie der Stempel gebildet wird. Jede Spalte trägt ihre gespeicherten Bytes bei, mit vorangestellter Länge; ein fehlender Wert trägt ein einzelnes 0xFF bei. Die Länge macht die Kodierung eindeutig – ohne sie ergäben ('ab', 'c') und ('a', 'bc') dieselben Bytes –, und die Markierung trennt „fehlt“ von „leer“. Bytes statt Text, weil eine Textform von Dingen abhängt, die nicht zum Wert gehören: von der Sprache der Sitzung bei einem Datum und von der Rundung bei money und float. Der Stempel ist bei wiederholtem Lesen gleich, und jede Änderung eines gespeicherten Werts ändert ihn – auch die vierte Nachkommastelle eines money, die kleinste Änderung eines float, 100 ns an einem datetime2, eine Änderung nur der Groß- und Kleinschreibung, gleiche Bytes in anderem Typ bei sql_variant, geography, hierarchyid, json und vector, eine Spalte mit Leerzeichen im Namen sowie NULL gegenüber Leertext. text, ntext, json und vector lassen sich nicht direkt in Bytes umwandeln und gehen deshalb über varchar(max) bzw. nvarchar(max).

Was er nicht leistet – und weshalb er nicht pauschal gilt:

  • Ein Wert, der geändert und wieder zurückgesetzt wird, ergibt wieder denselben Stempel. Er beschreibt den Zustand, er zählt keine Änderungen; eine von SQL Server gepflegte Zeilenversion hat diese Lücke nicht.
  • Jedes Lesen berechnet ihn für jede gelieferte Zeile über jede Spalte, große Textspalten eingeschlossen. Auf breiten Tabellen kostet das bei jedem Lesevorgang.
  • Er vergleicht Bytes, nicht Text unter einer Sortierfolge: Eine Änderung nur der Groß- und Kleinschreibung gilt als Änderung, obwohl der Vergleich von SQL Server beide Werte gleich nennt. Das ist strenger, nie schwächer.

Wo das Schema geändert werden darf, bleibt eine Zeilenversionsspalte die bessere Lösung: genau, von SQL Server gepflegt und beim Lesen kostenlos.

Für eine Tabelle ohne Schlüssel, die zugleich unter KeylessWriteTables steht, gibt es den Stempel ebenfalls. Dort ist er Teil der Adresse und deckt auch den Langtext ab, den ein Vergleich der Einzelwerte nicht erfasst.

Tabellen ohne Schlüssel

Eine Tabelle ohne eindeutigen Schlüssel hat keine Adresse für „diesen Satz“. Ohne Freigabe ist sie deshalb nur lesbar. Eine Anwendung, die eine solche Tabelle über eine direkte Verbindung bearbeitet, bestimmt den Satz stattdessen über seine alten Werte. So schreibt Access über eine direkte Verbindung, nachdem beim Verknüpfen ein Ersatzschlüssel gewählt wurde:

UPDATE "t" SET "Text"=? WHERE "Nummer" = ? AND "Text" = ? AND "Betrag" IS NULL AND "Wert" IS NULL AND "Datum" IS NULL AND "Aktiv" = ?

Access nennt dabei jede Spalte außer dem Langtext (nvarchar(max)) und schreibt für einen fehlenden alten Wert selbst IS NULL. Genau diese Form nimmt der Dienst für Tabellen an, die am Alias freigegeben sind (KeylessWriteTables):

  • Die alten Werte müssen den Satz vollständig nennen – jede vergleichbare Spalte oder die Zeilenversion bzw. den Zeilenstempel; weniger Spalten träfen auch Sätze, die sich nur anderswo unterscheiden.
  • Ein fehlender alter Wert wird zu IS NULL.
  • Geändert oder gelöscht wird genau ein Satz. Treffen die alten Werte mehrere Sätze (gleich in allen vergleichbaren Spalten), nimmt der Dienst die Änderung zurück und antwortet mit 409 – wie Access über eine direkte Verbindung, das dann den Fehler 3362 meldet und ebenfalls zurücknimmt.
  • Trifft die Bedingung keinen Satz, antwortet der Dienst mit 412: Der Satz wurde inzwischen geändert oder gelöscht; ohne Schlüssel lassen sich beide Fälle nicht unterscheiden.

Die Aktionen der OData-Oberfläche und ihre Antworten stehen unter Tabellen ohne Schlüssel: Aktionen.

Verwandte Themen

de/wiki/data-conectivity/database-gateway/concepts/writing-and-write-conflicts.txt · Last modified: by Wolfgang van der Stille