it-swarm.com.de

Erlaubt SQL Server DDL innerhalb einer Transaktion für die Transaktion vor dem Festschreiben (sichtbar zu machen)?

In PostgreSQL kann ich eine Tabelle mit einigen Testdaten erstellen und diese dann in einer Transaktion in eine neue Spalte eines anderen Typs migrieren, was zu einer Tabelle führt. umschreiben auf COMMIT,

CREATE TABLE foo ( a int );
INSERT INTO foo VALUES (1),(2),(3);

Gefolgt von,

BEGIN;
  ALTER TABLE foo ADD COLUMN b varchar;
  UPDATE foo SET b = CAST(a AS varchar);
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

Dasselbe in Microsoft SQL Server scheint jedoch einen Fehler zu erzeugen. Vergleichen Sie diese Funktion db fiddle , wobei sich der Befehl ADD (column) außerhalb der Transaktion befindet.

-- txn1
BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
COMMIT;

-- txn2
BEGIN TRANSACTION;
  UPDATE foo SET b = CAST( a AS varchar );
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

dazu db fiddle was nicht funktioniert,

-- txn1
BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
  UPDATE foo SET b = CAST( a AS varchar );
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

Aber stattdessen Fehler

Msg 207 Level 16 State 1 Line 2
Invalid column name 'b'.

Gibt es überhaupt eine Möglichkeit, diese Transaktion in Bezug auf DDL sichtbar zu machen und sich wie PostgreSQL zu verhalten?

9
Evan Carroll

Im Allgemeinen nein. SQL Server kompiliert den gesamten Stapel im aktuellen Bereich vor der Ausführung, sodass referenzierte Entitäten vorhanden sein müssen (Neukompilierungen auf Anweisungsebene können auch später erfolgen). Die Hauptausnahme ist Deferred Name Resolution , dies gilt jedoch für Tabellen, nicht für Spalten:

Die verzögerte Namensauflösung kann nur verwendet werden, wenn Sie auf nicht vorhandene Tabellenobjekte verweisen. Alle anderen Objekte müssen zum Zeitpunkt der Erstellung der gespeicherten Prozedur vorhanden sein. Wenn Sie beispielsweise auf eine vorhandene Tabelle in einer gespeicherten Prozedur verweisen, können Sie keine nicht vorhandenen Spalten für diese Tabelle auflisten.

Häufige Problemumgehungen umfassen dynamischen Code (wie in Joes Antwort ) oder die Trennung von DML und DDL in separate Stapel.

Für diesen speziellen Fall könnten Sie auch schreiben:

BEGIN TRANSACTION;

    ALTER TABLE dbo.foo
        ALTER COLUMN a varchar(11) NOT NULL
        WITH (ONLINE = ON);

    EXECUTE sys.sp_rename
        @objname = N'dbo.foo.a',
        @newname = N'b',
        @objtype = 'COLUMN';

COMMIT TRANSACTION;

Sie können immer noch nicht auf die umbenannte Spalte b im selben Stapel und Bereich zugreifen, aber die Aufgabe wird erledigt.

In Bezug auf SQL Server gibt es eine Denkschule, die besagt, dass das Mischen von DDL und DML in einer Transaktion keine gute Idee ist. In der Vergangenheit gab es Fehler, bei denen dies zu einer falschen Protokollierung und einer nicht wiederherstellbaren Datenbank führte. Trotzdem machen es die Leute, besonders mit temporären Tischen. Dies kann zu einem ziemlich schwer zu verfolgenden Code führen.

17
Paul White 9

Ist es das, wonach du suchst?

BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
  EXEC sp_executesql N'UPDATE foo SET b = CAST( a AS varchar )';
  ALTER TABLE foo DROP COLUMN a;
COMMIT;
12
Joe Obbish

Auf die "im Allgemeinen Nein" -Statement zu Paul Whites Antwort bietet das Folgende hoffentlich eine direkte Antwort auf die Frage, dient aber auch dazu, die systemischen Einschränkungen eines solchen Prozesses aufzuzeigen und Sie von Methoden fernzuhalten, die sich nicht für eine einfache Verwaltung und Aufdeckung eignen Risiken.

Es kann oft erwähnt werden, um keine DDL-Änderungen vorzunehmen, während Sie DML erstellen. Eine gute Programmierung trennt diese Funktionen, um die Unterstützbarkeit aufrechtzuerhalten und Änderungen der Spaghetti-Saiten zu vermeiden.

Und wie Paul kurz und bündig hervorhob, arbeitet SQL Server in Stapeln .

Nun, für diejenigen, die bezweifeln, dass dies funktioniert, funktioniert es wahrscheinlich nicht auf Ihrer Instanz, aber einige Versionen wie 2017 können tatsächlich funktionieren! Hier ist der Beweis: enter image description here

[TESTCODE - Funktioniert möglicherweise nicht mit vielen Versionen von SQL Server]

USE master
GO
CREATE TABLE foo (a VARCHAR(11) )
GO
BEGIN TRANSACTION;
    INSERT INTO dbo.foo (a)
    VALUES ('entry')
/*****
[2] Check Values
*****/
    SELECT a FROM dbo.foo
/*****
[3] Add Column
*****/
    ALTER TABLE dbo.foo
        ADD b VARCHAR(11)
/*****
[3] Insert value into this new column in the same batch
-- Again, this is just an example. Please do not do this in production
*****/
    IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
            AND name = 'b')
        INSERT INTO dbo.foo (b)
        VALUES ('d')
COMMIT TRANSACTION;
/*****
[4] SELECT outside transaction
-- this will fail
*****/
    --IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
    --      AND name = 'b')
    --  SELECT b FROM dbo.foo
-- this will work...but a SELECT * ???
IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
            AND name = 'b')
        SELECT * FROM dbo.foo

DROP TABLE dbo.foo

[SCHLUSSFOLGERUNG]

Ja, Sie können DDL und DML für bestimmte Versionen oder Patches von SQL Server im selben Stapel ausführen wie @ AndriyM - dbfiddle unter SQL 2017 weist darauf hin, aber nicht alle DML werden unterstützt, und es gibt keine Garantie dafür wird immer der Fall sein. Wenn dies funktioniert, kann dies eine Abweichung von Ihrer SQL Server-Version sein. Dies kann zu dramatischen Problemen führen, wenn Sie patchen oder auf neue Versionen migrieren.

  • Außerdem sollte Ihr Design im Allgemeinen Änderungen vorwegnehmen. Ich verstehe die Bedenken, die das Ändern/Hinzufügen von Spalten in einer Tabelle haben kann, aber Sie können dies in Stapeln richtig gestalten.

[ZUSÄTZLICHES KREDIT]

Wie Paul bereits sagte, gibt es für die EXISTS-Anweisung viele andere Möglichkeiten, den Code zu validieren, bevor Sie mit dem nächsten Schritt in Ihrem Code fortfahren.

  • Mit der EXISTS-Anweisung können Sie Code erstellen, der auf allen Versionen von SQL Server funktioniert
  • Es ist eine boolesche Funktion, die komplexe Überprüfungen in einer Anweisung ermöglicht
2
clifton_h