none
Hochzählen per gespeicherter Prozedur RRS feed

  • Frage

  • Hi,

    ich möchte, dass beim ausführen einer gespeicherten Prozedur ein Tabellenwert um eins (1) erhöht wird.

    Hier mein Ansatz:

    ALTER procedure [dbo].[web_Autopilotaktiv]
    @kundenId int,
    @neuesDatum date,
    @Text  nvarchar(max)
    
    as
    declare @Zaehler int = (SELECT AutopilotStatus From Kundenliste Where ID_Kunde = @kundenId)
    
    
    UPDATE Kundenliste
    SET Nachverfolgung = @neuesDatum, AutopilotStatus = convert(int,@Zaehler) + 1	
    Where ID_Kunde = @kundenId

    Die Tabellenspalte AutopilotStatus ist eine INT Spalte. Das Problem ist, dass maximal bis zur Nummer zwei hochgezählt wird. Ab einem Wert von zwei bleibt es bei zwei. Besitzt AutopilotStatus den Wert 3, wird der Wert bei Ausführung sogar auf zwei herabgestuft...

    Im Vorfeld hatte ich es auch schon probiert mit:

    SET Nachverfolgung = @neuesDatum, AutopilotStatus = AutopilotStatus + 1	

    Kann mir jemand dieses Verhalten erklären und Abhilfe schaffen?


    .::datekk::.

    Freitag, 7. Juni 2019 06:53

Antworten

  • Hi,

    ich kann das nicht reproduzieren. Hier mal ein funktionierendes Beispiel mit einer Tabellenvariablen.

    DECLARE @Kunden TABLE
    (
        ID_Kunde int,
        AutopilotStatus int,
        Nachverfolgung date
    )
    
    INSERT INTO @Kunden
    (
        ID_Kunde,
        AutopilotStatus
    )
    VALUES
    ( 1, 3 ),
    ( 2, 49 ),
    ( 3, 101 )
    
    DECLARE @KundenId int = 3;
    DECLARE @Zaehler  int = ( SELECT AutopilotStatus From @Kunden Where ID_Kunde = @KundenId );
    
    UPDATE @Kunden
    SET    Nachverfolgung  = GETDATE(),
           AutopilotStatus = CONVERT( int, @Zaehler ) + 1	
    WHERE  ID_Kunde = @kundenId
    
    SELECT *
    FROM   @Kunden

    Auch die andere Variante klappt problemlos:

    DECLARE @Kunden TABLE
    (
        ID_Kunde int,
        AutopilotStatus int,
        Nachverfolgung date
    )
    
    INSERT INTO @Kunden
    (
        ID_Kunde,
        AutopilotStatus
    )
    VALUES
    ( 1, 3 ),
    ( 2, 49 ),
    ( 3, 101 )
    
    DECLARE @KundenId int = 1;
    
    UPDATE @Kunden
    SET    Nachverfolgung  = GETDATE(),
           AutopilotStatus = AutopilotStatus + 1
    WHERE  ID_Kunde = @kundenId
    
    UPDATE @Kunden
    SET    Nachverfolgung  = GETDATE(),
           AutopilotStatus = AutopilotStatus + 1
    WHERE  ID_Kunde = @kundenId
    
    UPDATE @Kunden
    SET    Nachverfolgung  = GETDATE(),
           AutopilotStatus = AutopilotStatus + 1
    WHERE  ID_Kunde = @kundenId
    
    SELECT *
    FROM   @Kunden

    Poste bitte mal deine Tabellendefinition als CREATE TABLE Statement und schreib bitte die genaue Versionsnummer deiner SQL Server Instanz (SELECT @@VERSION) dabei.

    Ich tippe auf einen Trigger, der den Wert wieder runtersetzt, wenn größer 2 oder evtl. einen Check Constraint, der Werte > 2 verhindert.

     


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
    https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport



    Freitag, 7. Juni 2019 08:10
    Moderator
  • Hallo Datekk,

    warum so kompliziert und nicht in einem Statement. Deine Lösung kann bei hoher Concurrency zu falschen Ergebnissen führen. Wenn Prozess 1 die Prozedur ausführt und die ersten Anweisung gelesen hat (aber noch nicht geschrieben hat!), kommt Prozess 2 und liest genau den gleichen Wert. Somit hast Du eigentlich 2 Updates - es wird aber nur ein Update gezählt.

    Entweder Du serialisierst den Prozess (würde ich aber nicht machen) oder aber Du kombinierst das Update in einem Statement:

    CREATE TABLE dbo.Customers
    (
    	Id	INT	NOT NULL IDENTITY (1, 1) PRIMARY KEY CLUSTERED,
    	Name	VARCHAR(255) NOT NULL,
    	AutoPilot INT NOT NULL DEFAULT (1)
    );
    GO
    
    -- Ein paar Daten eintragen ...
    
    -- UPDATE-Statement
    UPDATE	dbo.Customers
    SET	Name = 'Test',
    	AutoPilot = AutoPilot + 1
    WHERE	Id = 10;
    GO


    Uwe Ricken (Blog | Twitter)
    Microsoft Certiied Master - SQL Server 2008
    Microsoft Certified Solution Master - CHARTER Data Platform
    Microsoft Certified Solution Expert - Data Platform
    db Berater GmbH
    Microsoft SQL Server Blog (german only)

    Montag, 17. Juni 2019 09:24
  • Ich habe den Fehler tatsächlich in meiner Anwendung gefunden. Die Proc wurde vom Programm aufgerufen, davor wurde aber AutopilotStatus auf 1 gesetzt.... 

    .::datekk::.

    Freitag, 7. Juni 2019 12:37

Alle Antworten

  • Hi,

    ich kann das nicht reproduzieren. Hier mal ein funktionierendes Beispiel mit einer Tabellenvariablen.

    DECLARE @Kunden TABLE
    (
        ID_Kunde int,
        AutopilotStatus int,
        Nachverfolgung date
    )
    
    INSERT INTO @Kunden
    (
        ID_Kunde,
        AutopilotStatus
    )
    VALUES
    ( 1, 3 ),
    ( 2, 49 ),
    ( 3, 101 )
    
    DECLARE @KundenId int = 3;
    DECLARE @Zaehler  int = ( SELECT AutopilotStatus From @Kunden Where ID_Kunde = @KundenId );
    
    UPDATE @Kunden
    SET    Nachverfolgung  = GETDATE(),
           AutopilotStatus = CONVERT( int, @Zaehler ) + 1	
    WHERE  ID_Kunde = @kundenId
    
    SELECT *
    FROM   @Kunden

    Auch die andere Variante klappt problemlos:

    DECLARE @Kunden TABLE
    (
        ID_Kunde int,
        AutopilotStatus int,
        Nachverfolgung date
    )
    
    INSERT INTO @Kunden
    (
        ID_Kunde,
        AutopilotStatus
    )
    VALUES
    ( 1, 3 ),
    ( 2, 49 ),
    ( 3, 101 )
    
    DECLARE @KundenId int = 1;
    
    UPDATE @Kunden
    SET    Nachverfolgung  = GETDATE(),
           AutopilotStatus = AutopilotStatus + 1
    WHERE  ID_Kunde = @kundenId
    
    UPDATE @Kunden
    SET    Nachverfolgung  = GETDATE(),
           AutopilotStatus = AutopilotStatus + 1
    WHERE  ID_Kunde = @kundenId
    
    UPDATE @Kunden
    SET    Nachverfolgung  = GETDATE(),
           AutopilotStatus = AutopilotStatus + 1
    WHERE  ID_Kunde = @kundenId
    
    SELECT *
    FROM   @Kunden

    Poste bitte mal deine Tabellendefinition als CREATE TABLE Statement und schreib bitte die genaue Versionsnummer deiner SQL Server Instanz (SELECT @@VERSION) dabei.

    Ich tippe auf einen Trigger, der den Wert wieder runtersetzt, wenn größer 2 oder evtl. einen Check Constraint, der Werte > 2 verhindert.

     


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
    https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport



    Freitag, 7. Juni 2019 08:10
    Moderator
  • Ich habe den Fehler tatsächlich in meiner Anwendung gefunden. Die Proc wurde vom Programm aufgerufen, davor wurde aber AutopilotStatus auf 1 gesetzt.... 

    .::datekk::.

    Freitag, 7. Juni 2019 12:37
  • Hallo Datekk,

    warum so kompliziert und nicht in einem Statement. Deine Lösung kann bei hoher Concurrency zu falschen Ergebnissen führen. Wenn Prozess 1 die Prozedur ausführt und die ersten Anweisung gelesen hat (aber noch nicht geschrieben hat!), kommt Prozess 2 und liest genau den gleichen Wert. Somit hast Du eigentlich 2 Updates - es wird aber nur ein Update gezählt.

    Entweder Du serialisierst den Prozess (würde ich aber nicht machen) oder aber Du kombinierst das Update in einem Statement:

    CREATE TABLE dbo.Customers
    (
    	Id	INT	NOT NULL IDENTITY (1, 1) PRIMARY KEY CLUSTERED,
    	Name	VARCHAR(255) NOT NULL,
    	AutoPilot INT NOT NULL DEFAULT (1)
    );
    GO
    
    -- Ein paar Daten eintragen ...
    
    -- UPDATE-Statement
    UPDATE	dbo.Customers
    SET	Name = 'Test',
    	AutoPilot = AutoPilot + 1
    WHERE	Id = 10;
    GO


    Uwe Ricken (Blog | Twitter)
    Microsoft Certiied Master - SQL Server 2008
    Microsoft Certified Solution Master - CHARTER Data Platform
    Microsoft Certified Solution Expert - Data Platform
    db Berater GmbH
    Microsoft SQL Server Blog (german only)

    Montag, 17. Juni 2019 09:24