it-swarm.com.de

Warum müssen TVPs BEREIT sein und warum können Parameter anderer Typen nicht BEREIT sein

Laut diesem Blog sind Parameter für eine Funktion oder eine gespeicherte Prozedur im Wesentlichen Pass-by-Wert, wenn sie keine OUTPUT -Parameter sind, und werden im Wesentlichen als sicherere Version von Pass-by behandelt -Referenz, wenn es sich um OUTPUT Parameter handelt.

Zuerst dachte ich, dass das Ziel, TVP zur Deklaration von READONLY zu zwingen, darin bestand, den Entwicklern klar zu signalisieren, dass TVP nicht als OUTPUT -Parameter verwendet werden kann, aber es muss mehr los sein, weil Wir können Nicht-TVP nicht als READONLY deklarieren. Zum Beispiel schlägt Folgendes fehl:

create procedure [dbo].[test]
@a int readonly
as
    select @a

Nachricht 346, Stufe 15, Zustand 1, Verfahrenstest
Der Parameter "@a" kann nicht READONLY deklariert werden, da es sich nicht um einen tabellenwertigen Parameter handelt.

  1. Da Statistiken werden nicht gespeichert auf TVP, was ist der Grund für die Verhinderung von DML-Operationen?
  2. Hängt es damit zusammen, dass TVP aus irgendeinem Grund nicht OUTPUT Parameter sein soll?
19
Erik

Die Erklärung scheint mit einer Kombination verbunden zu sein: a) einem Detail aus dem verlinkten Blog, das in dieser Frage nicht erwähnt wurde, b) der Pragmatik von TVPs, die dazu passt, wie Parameter immer ein- und ausgegeben wurden, c) und der Art von Tabellenvariablen.

  1. Das fehlende Detail im verlinkten Blog-Beitrag ist genau, wie Variablen in gespeicherte Prozeduren und Funktionen ein- und ausgegeben werden (dies bezieht sich auf die Formulierung in der Frage "Eine sicherere Version der Referenzübergabe, wenn es sich um OUTPUT-Parameter handelt)). ::

    TSQL verwendet eine Copy-In/Copy-Out-Semantik, um Parameter an gespeicherte Prozeduren und Funktionen zu übergeben.

    ... wenn der gespeicherte Prozess die Ausführung beendet hat (ohne einen Fehler zu treffen), wird eine Kopie erstellt, die den übergebenen Parameter mit allen Änderungen aktualisiert, die im gespeicherten Prozess vorgenommen wurden.

    Der eigentliche Vorteil dieses Ansatzes liegt im Fehlerfall. Wenn während der Ausführung einer gespeicherten Prozedur ein Fehler auftritt, werden Änderungen an den Parametern nicht an den Aufrufer zurückgegeben.

    Wenn das Schlüsselwort OUTPUT nicht vorhanden ist, wird keine Kopie erstellt.

    Das Endergebnis:
    Parameter für gespeicherte Prozesse spiegeln niemals die teilweise Ausführung des gespeicherten Prozesses wider, wenn ein Fehler aufgetreten ist.

    Teil 1 dieses Puzzles ist, dass Parameter immer "nach Wert" übergeben werden. Und nur wenn der Parameter als OUTPUT und markiert ist, wird die gespeicherte Prozedur erfolgreich abgeschlossen, und der aktuelle Wert wird tatsächlich zurückgesendet. Wenn OUTPUT -Werte wirklich "als Referenz" übergeben würden, wäre der Zeiger auf die Position im Speicher dieser Variablen das übergebene Objekt und nicht der Wert selbst. Wenn Sie den Zeiger (d. H. Die Speicheradresse) übergeben, werden alle vorgenommenen Änderungen sofort wiedergegeben, selbst wenn die nächste Zeile der gespeicherten Prozedur einen Fehler verursacht und die Ausführung abbricht.

    Um Teil 1 zusammenzufassen: Variablenwerte werden immer kopiert; Sie werden nicht durch ihre Speicheradresse referenziert.

  2. In Anbetracht von Teil 1 kann eine Richtlinie zum ständigen Kopieren von Variablenwerten zu Ressourcenproblemen führen, wenn die übergebene Variable ziemlich groß ist. Ich habe nicht getestet, wie Blob-Typen behandelt werden (VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX), XML und diejenigen, die nicht verwendet werden sollten mehr: TEXT, NTEXT und IMAGE), aber man kann mit Sicherheit sagen, dass jede übergebene Datentabelle ziemlich groß sein kann. Es wäre für diejenigen, die die TVP-Funktion entwickeln, sinnvoll, sich eine echte "Pass-by-Reference" -Fähigkeit zu wünschen, um zu verhindern, dass ihre coole neue Funktion eine gesunde Anzahl von Systemen zerstört (d. H. Einen skalierbareren Ansatz wünscht). Wie Sie in der Dokumentation sehen können, haben sie Folgendes getan:

    Transact-SQL übergibt tabellenwertige Parameter als Referenz an Routinen, um eine Kopie der Eingabedaten zu vermeiden.

    Dieses Problem der Speicherverwaltung war auch kein neues Konzept, da es in der SQLCLR-API enthalten ist, die in SQL Server 2005 eingeführt wurde (TVPs wurden in SQL Server 2008 eingeführt). Wenn Sie NVARCHAR- und VARBINARY -Daten an SQLCLR-Code übergeben (dh Eingabeparameter für die .NET-Methoden in einer SQLCLR-Assembly), haben Sie die Möglichkeit, den Ansatz "nach Wert" zu verwenden, indem Sie entweder verwenden SqlString oder SqlBinary, oder Sie können mit dem Ansatz "Nach Referenz" fortfahren, indem Sie entweder SqlChars oder SqlBytes verwenden. Die Typen SqlChars und SqlBytes ermöglichen das vollständige Streaming der Daten in die .NET-CLR, sodass Sie kleine Blöcke mit großen Werten abrufen können, anstatt ganze 200 MB (bis zu 2 GB) zu kopieren , rechts) Wert.

    Um Teil 2 zusammenzufassen: TVPs neigen von Natur aus dazu, viel Speicher zu verbrauchen (und damit die Leistung zu verschlechtern), wenn sie im Modell "Immer den Wert kopieren" bleiben. Daher machen TVPs einen echten "Pass by Reference".

  3. Das letzte Stück ist, warum Teil 2 wichtig ist: Warum sollte die Übergabe eines TVP wirklich "durch Bezugnahme", anstatt eine Kopie davon anzufertigen, irgendetwas ändern? Und dies wird durch das Entwurfsziel beantwortet, das die Grundlage für Teil 1 bildet: Gespeicherte Prozeduren, die nicht erfolgreich abgeschlossen werden, sollten in keiner Weise einen der Eingabeparameter ändern, unabhängig davon, ob sie als OUTPUT markiert sind oder nicht . Das Zulassen von DML-Vorgängen hat unmittelbare Auswirkungen auf den Wert des TVP, wie er im aufrufenden Kontext vorhanden ist (da das Übergeben als Referenz bedeutet, dass Sie das übergebene Objekt ändern, nicht eine Kopie dessen, was übergeben wurde).

    Jetzt spricht irgendwo jemand mit seinem Monitor und sagt: "Nun, bauen Sie einfach eine automatische Einrichtung ein, um alle Änderungen an den TVP-Parametern zurückzusetzen, falls diese an die gespeicherte Prozedur übergeben wurden. Duh. Problem gelöst." Nicht so schnell. Hier kommt die Art der Tabellenvariablen ins Spiel: Änderungen an Tabellenvariablen sind nicht an Transaktionen gebunden! Es gibt also keine Möglichkeit, die Änderungen zurückzusetzen. Tatsächlich ist dies ein Trick, mit dem innerhalb einer Transaktion generierte Informationen gespeichert werden, wenn ein Rollback erforderlich ist :-).

    Zusammenfassend lässt sich sagen, dass Teil 3: Tabellenvariablen im Falle eines Fehlers, der zum Abbruch der gespeicherten Prozedur führt, das "Rückgängigmachen" von Änderungen nicht zulassen. Dies verstößt gegen das Entwurfsziel, dass Parameter niemals die teilweise Ausführung widerspiegeln (Teil 1).

Ergo: Das Schlüsselwort READONLY wird benötigt, um DML-Operationen auf TVPs zu verhindern, da es sich um Tabellenvariablen handelt, die tatsächlich "als Referenz" übergeben werden. Änderungen an ihnen werden daher sofort angezeigt, selbst wenn bei der gespeicherten Prozedur ein Fehler auftritt, und es gibt keine andere Möglichkeit, dies zu verhindern.

Darüber hinaus können Parameter anderer Datentypen READONLY nicht verwenden, da sie bereits Kopien der übergebenen Daten sind und daher nichts schützen, was noch nicht geschützt ist. Das und die Art und Weise, wie Parameter der anderen Datentypen funktionieren, sollten schreibgeschützt sein. Daher wäre es wahrscheinlich noch aufwändiger, diese API so zu ändern, dass sie jetzt ein schreibgeschütztes Konzept enthält.

19
Solomon Rutzky

Community Wiki Antwort generiert aus einem Kommentar zu der Frage von Martin Smith

Hierfür gibt es ein aktives Connect-Element (eingereicht von Erland Sommarskog):

Lockerung der Einschränkung, dass Tabellenparameter schreibgeschützt sein müssen, wenn sich SPs gegenseitig aufrufen

Die einzige Antwort von Microsoft lautet bisher (Hervorhebung hinzugefügt):

Vielen Dank für das Feedback dazu. Wir haben ähnliche Rückmeldungen von einer großen Anzahl von Kunden erhalten. Das Lesen/Schreiben von Parametern mit Tabellenwerten erfordert sowohl auf der SQL Engine-Seite als auch auf Client-Protokollen einiges an Arbeit. Aufgrund von Zeit-/Ressourcenbeschränkungen Neben anderen Prioritäten können wir diese Arbeit im Rahmen der SQL Server 2008-Version nicht übernehmen. Wir haben dieses Problem jedoch untersucht und haben es fest in unserem Radar, um es im Rahmen der nächsten Version von SQL Server zu beheben. Wir freuen uns über das Feedback hier.

Srini Acharya
Senior Program Manager
SQL Server Relational Engine

5
Paul White 9