Wie ordne ich einer Variablen ein ausgewähltes Ergebnis zu?

73

Wie speichere ich einen ausgewählten Feldwert in einer Variablen aus einer Abfrage und verwende ihn in einer Aktualisierungsanweisung?

Hier ist meine Vorgehensweise:

Ich schreibe eine gespeicherte SQL Server 2005 T-SQL-Prozedur, die Folgendes ausführt:

  1. Ruft eine Liste der Rechnungs-IDs aus der Rechnungstabelle ab und speichert sie im Cursor
  2. Abrufen der Rechnungs-ID vom Cursor -> Variable tmp_key
  3. foreach tmp_key findet die primäre Kontakt-ID des Rechnungsclients aus der Kundentabelle
  4. Aktualisiert den Client-Kontaktschlüssel mit der primären Kontakt-ID
  5. Cursor schließen

Hier ist mein Code:

DECLARE @tmp_key int
DECLARE @get_invckey cursor 

set @get_invckey = CURSOR FOR 
    select invckey from tarinvoice where confirmtocntctkey is null and tranno like '%115876'

OPEN @get_invckey 

FETCH NEXT FROM @get_invckey into @tmp_key

WHILE (@@FETCH_STATUS = 0) 
BEGIN 
    SELECT c.PrimaryCntctKey as PrimaryContactKey
    from tarcustomer c, tarinvoice i
    where i.custkey = c.custkey and i.invckey = @tmp_key

    UPDATE tarinvoice set confirmtocntctkey = PrimaryContactKey where invckey = @tmp_key
    FETCH NEXT FROM @get_invckey INTO @tmp_key
END 

CLOSE @get_invckey
DEALLOCATE @get_invckey

Wie speichere ich den PrimaryContactKey und verwende ihn erneut in der set-Klausel der folgenden Update-Anweisung? Erstelle ich eine Cursor-Variable oder nur eine andere lokale Variable mit einem int-Typ?

phill
quelle
2
Wie @GilaMonster unten antwortet, kann diese gesamte Operation eine einzelne UPDATE-Anweisung sein (eine " satzbasierte Operation", nicht zu verwechseln mit einer t-sql-SET-Anweisung ). Dies ist ein viel besserer Ansatz (schnellere Ausführung, weniger Overhead und deutlich weniger Code). Ich möchte nur darauf hinweisen, weil es bei der Frage und allen aktuellen Top-Antworten darum geht, wie man eine SET-Anweisung schreibt, aber es ist wirklich nicht der beste Ansatz, um damit zu beginnen.
Gregmac

Antworten:

47
DECLARE @tmp_key int
DECLARE @get_invckey cursor 

SET @get_invckey = CURSOR FOR 
    SELECT invckey FROM tarinvoice WHERE confirmtocntctkey IS NULL AND tranno LIKE '%115876'

OPEN @get_invckey 

FETCH NEXT FROM @get_invckey INTO @tmp_key

DECLARE @PrimaryContactKey int --or whatever datatype it is

WHILE (@@FETCH_STATUS = 0) 
BEGIN 
    SELECT @PrimaryContactKey=c.PrimaryCntctKey
    FROM tarcustomer c, tarinvoice i
    WHERE i.custkey = c.custkey AND i.invckey = @tmp_key

    UPDATE tarinvoice SET confirmtocntctkey = @PrimaryContactKey WHERE invckey = @tmp_key
    FETCH NEXT FROM @get_invckey INTO @tmp_key
END 

CLOSE @get_invckey
DEALLOCATE @get_invckey

EDIT:
Diese Frage hat viel mehr Zugkraft bekommen, als ich erwartet hätte. Beachten Sie, dass ich in meiner Antwort nicht die Verwendung des Cursors befürworte, sondern zeige, wie der Wert basierend auf der Frage zugewiesen wird.

Squillman
quelle
102

Ich hatte gerade das gleiche Problem und ...

declare @userId uniqueidentifier
set @userId = (select top 1 UserId from aspnet_Users)

oder noch kürzer:

declare @userId uniqueidentifier
SELECT TOP 1 @userId = UserId FROM aspnet_Users
Pawel G.
quelle
1
Haha ich mag das. sollte für die Zuweisung eines Skalarwerts sehr einfach sein. Hass Cursor bla3 .. Zum Glück fand Googeln diese kleine Antwort.
CallMeLaNN
3
Ich weiß es nicht, denn set @userId = (select top 1 UserId from aspnet_Users) ohne Klammer führt dies zu "falscher Syntax in der Nähe von select"!
CallMeLaNN
Dieses Forum zeigt den richtigen Ansatz für top: sqlservercentral.com/Forums/Topic496124-169-1.aspx
Chip McCormick
3
Beachten Sie, dass die Verwendung des obigen Cursors nicht mit der Frage zusammenhängt, wie eine Variable zugewiesen werden soll. Leider ist dies eine verwirrende Frage.
Chip McCormick
1
Achten Sie auf die Verwendung von TOP, da SQL Server möglicherweise Zeilen neu anordnet, um die Leistung zu optimieren, wenn Sie mit vielen Abfragen belastet sind. Die erste Zeile ist möglicherweise nicht immer dieselbe.
Erk
19

Versuche dies

SELECT @PrimaryContactKey = c.PrimaryCntctKey
FROM tarcustomer c, tarinvoice i
WHERE i.custkey = c.custkey 
    AND i.invckey = @tmp_key

UPDATE tarinvoice SET confirmtocntctkey = @PrimaryContactKey 
WHERE invckey = @tmp_key
FETCH NEXT FROM @get_invckey INTO @tmp_key

Sie würden diese Variable außerhalb Ihrer Schleife nur als Standard-TSQL-Variable deklarieren.

Ich sollte auch beachten, dass Sie dies für jede Art der Auswahl in einer Variablen tun würden, nicht nur, wenn Sie sich mit Cursorn befassen.

TheTXI
quelle
14

Warum brauchst du überhaupt einen Cursor? Ihr gesamtes Codesegment kann durch dieses ersetzt werden, das in einer großen Anzahl von Zeilen viel schneller ausgeführt wird.

UPDATE tarinvoice set confirmtocntctkey = PrimaryCntctKey 
FROM tarinvoice INNER JOIN tarcustomer ON tarinvoice.custkey = tarcustomer.custkey
WHERE confirmtocntctkey is null and tranno like '%115876'
GilaMonster
quelle
Sind Cursor wirklich verpönt?
Phill
4
Sie sind langsam. SQL Server ist für satzbasierte Abfragen optimiert. Es ist schneller, eine Million Zeilen in einer Abfrage zu bearbeiten, als eine Zeile millionenfach zu bearbeiten. Fügen Sie dazu den Overhead hinzu, den Cursor haben, und Sie fragen nach größeren Leistungsproblemen, indem Sie Cursor anstelle von satzbasierten Operationen verwenden. Testen Sie Ihre Cursorlösung und meine Abfrage, und sehen Sie, wie die Ausführungszeiten der beiden sind.
GilaMonster
Danke, Mann!!!! Dies war für mich auch einfacher, als herauszufinden, wie der Cursor für mein spezifisches Szenario geändert werden kann. Du bist ein Gewinner!
Alan Ball
14

Um eine Variable sicher zuzuweisen, müssen Sie die Anweisung SET-SELECT verwenden:

SET @PrimaryContactKey = (SELECT c.PrimaryCntctKey
    FROM tarcustomer c, tarinvoice i
    WHERE i.custkey = c.custkey 
    AND i.invckey = @tmp_key)

Stellen Sie sicher, dass Sie sowohl eine Start- als auch eine Endklammer haben!

Es gibt zwei Gründe, warum die SET-SELECT-Version die sicherste Methode zum Festlegen einer Variablen ist.

1. Das SELECT gibt mehrere Beiträge zurück

Was passiert, wenn die folgende Auswahl zu mehreren Posts führt?

SELECT @PrimaryContactKey = c.PrimaryCntctKey
FROM tarcustomer c, tarinvoice i
WHERE i.custkey = c.custkey 
    AND i.invckey = @tmp_key

@PrimaryContactKeywird der Wert aus dem letzten Beitrag im Ergebnis zugewiesen .

Tatsächlich @PrimaryContactKeywird im Ergebnis ein Wert pro Beitrag zugewiesen, sodass er folglich den Wert des letzten Beitrags enthält, den der SELECT-Befehl verarbeitet hat.

Welcher Beitrag "letzter" ist, wird durch Clustered-Indizes bestimmt. Wenn kein Clustered-Index verwendet wird oder der Primärschlüssel geclustert wird, ist der "letzte" Beitrag der zuletzt hinzugefügte Beitrag. Dieses Verhalten kann im schlimmsten Fall jedes Mal geändert werden, wenn die Indizierung der Tabelle geändert wird.

Mit einer SET-SELECT-Anweisung wird Ihre Variable auf gesetzt null.

2. Das SELECT gibt keine Beiträge zurück

Was passiert bei Verwendung der zweiten Version des Codes, wenn Ihre Auswahl überhaupt kein Ergebnis zurückgibt?

Im Gegensatz zu dem, was Sie vielleicht glauben, wird der Wert der Variablen nicht null sein - er behält seinen vorherigen Wert bei!

Dies liegt daran, dass SQL, wie oben angegeben, der Variablen einmal pro Beitrag einen Wert zuweist. Dies bedeutet, dass mit der Variablen nichts unternommen wird, wenn das Ergebnis keine Beiträge enthält. Die Variable hat also immer noch den Wert, den sie hatte, bevor Sie die Anweisung ausgeführt haben.

Mit der Anweisung SET-SELECT wird der Wert sein null.

Siehe auch: SET versus SELECT beim Zuweisen von Variablen?

Erk
quelle
1
Erk ist korrekt und dies sollte als Antwort markiert worden sein. Der zweite Punkt hat mich kürzlich erwischt ...
Wexman
Ich habe gerade einen Fehler mit Code nach Fall 1 oben behoben. Dem ursprünglichen Codierer schien es egal zu sein, dass mehrere Zeilen ausgewählt wurden, aber leider gibt ein MS SQL Server die Zeilen manchmal in einer anderen Reihenfolge zurück, als sie aus keinem anderen offensichtlichen Grund als der Optimierung eingefügt wurden ... das Ergebnis war zufällig und verwirrend ...
Erk