none
Cursorproblem ohne Cursor lösen RRS feed

  • Frage

  • Hi Community,

    hier mal ein ganz theoretisches Problem.

    Ziel ist, in einer Tabelle eine laufende Summe mitzuspeichern, die Bereits bei der Eingabe der Daten geschrieben wird. (siehe Codebeispiel)

    Das klappt auch soweit wunderbar, solange jedesmal lediglich ein Datensatz importiert wird.

    Ich wurde im Rahmen einer Schulung darauf angesprochen, konnte natürlich auch Lösungen präsentieren.

    1. Das ganze über einen Cursor erledigen

    2. Im Nachlauf nochmal über die Tabelle gehen.

    Ich suche aber gerne nach eleganteren Lösungen - hat vielleicht jemand eine Idee, wie ich auch mehrere Datensätze direkt beim Import richtig einfügen kann.

    Den Code habe ich auf das Wesentliche reduziert, danke für Eure Antworten

    Roland

    CREATE TABLE [dbo].[tblStamm](
    	[st_ID] [int] IDENTITY(1,1) NOT NULL,
    	[st_Einzahlung] [float] NOT NULL,
    	[st_Bestand] [float] NOT NULL,
     CONSTRAINT [PK_tblSta] PRIMARY KEY CLUSTERED 
    (
    	[st_ID] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    
    
    
    CREATE TABLE [dbo].[tblImport](
    	[imp_ID] [int] IDENTITY(1,1) NOT NULL,
    	[imp_Einzahlung] [float] NOT NULL,
     CONSTRAINT [PK_tblImport] PRIMARY KEY CLUSTERED 
    (
    	[imp_ID] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    
    INSERT INTO tblImport
    	(
    	imp_Einzahlung
    	)
    VALUES
    	(10),(10),(20)
    	
    
    
    
    
    INSERT INTO [tblStamm]
    	(
    	[st_Einzahlung],
    	[st_Bestand]
    	)
    SELECT
    	imp_Einzahlung,
    	imp_Einzahlung + (SELECT ISNULL(Sum((st_Einzahlung)),0) FROM tblStamm)
    FROM
    	tblImport
    ORDER BY imp_ID
    
    
    Update tblStamm 
    SET st_Bestand = (SELECT SUM(st_Einzahlung) FROM tblStamm s WHERE s.st_ID <= tblStamm.st_ID)
    
    
    SELECT * FROM tblImport
    SELECT * FROM tblStamm
    
    /*
    TRUNCATE TABLE tblStamm
    TRUNCATE TABLE tblImport
    */
    
    /*
    DROP TABLE tblStamm
    DROP TABLE tblImport
    */
    


    It's no problem, it's just the syntax

    Donnerstag, 26. Juni 2014 14:51

Antworten

  • Hallo Roland,

    Du kannst die Lösungsansätze nach dem Import einfach auf die tblStamm Tabelle anwenden, oder Du ermittelst vorab einmal den letzten Bestandswert, wendest die Lösung auf die tblImport an, wobei Du den letzten Wert hinzuaddierst.


    Olaf Helper

    [ Blog] [ Xing] [ MVP]

    Freitag, 27. Juni 2014 11:16

Alle Antworten

  • Hallo Roland,

    Welche Version des Sql Server verwendest Du? Wenn es 2012 oder 2014 ist, dann kannst Du die laufende Summe "on the fly" mit der neuen SUM OVER Funktion abfragen, statt es in der Tabelle zu speichern; liefert natürlich nur dann richtige Ergebnisse, wenn man die Daten nicht filtert.

    SELECT *,
          SUM(st_Einzahlung) OVER (ORDER BY st_id) AS RunningSum
    FROM tblStamm
    ORDER BY st_id


    Olaf Helper

    [ Blog] [ Xing] [ MVP]

    Donnerstag, 26. Juni 2014 17:41
  • Ab 2005 kann man es wenigstens über eine CTE realisieren, nicht so elegant, wie ab 2012, aber immerhin.

    WITH NumberedRows
    AS (SELECT     st_ID, st_Einzahlung,
                            st_ID AS rn
         FROM         dbo.tblStamm)
    SELECT             d1.st_ID, d1.st_Einzahlung,
                            SUM(d2.st_Einzahlung) AS RunningSum
    FROM                 NumberedRows AS d1
    INNER JOIN     NumberedRows AS d2
             ON         d2.rn < = d1.rn
    GROUP BY         d1.st_ID, d1.st_Einzahlung
    ORDER BY         d1.st_ID;

    Wenn Du dann noch eine Spalte (Part) zur Partitionierung hast dann geht es mit einer Window Function:

    WITH NumberedRows
    AS (SELECT     Part, st_ID, st_Einzahlung,
                            ROW_NUMBER() OVER (PARTITION BY Part
                                                                 ORDER BY st_ID) AS rn
         FROM         dbo.tblStamm)
    SELECT             d1.Part, d1.st_ID, d1.st_Einzahlung,
                            SUM(d2.st_Einzahlung) AS MovSUM
    FROM                 NumberedRows AS d1
    INNER JOIN     NumberedRows AS d2
             ON         d2.Part = d1.Part
             AND        d2.rn < = d1.rn
    GROUP BY         d1.Part, d1.st_ID, d1.st_Einzahlung
    ORDER BY         d1.Part, d1.st_ID;

    Die Lösung von Olaf (ab 2012) ist aber auch für die Partitionierung viel einfacher.

    SUM(st_Einzahlung) OVER (PARTITION BY Part ORDER BY st_id) AS RunningSum

    Einen schönen Tag noch,
    Christoph
    --
    Microsoft SQL Server MVP - http://www.insidesql.org/blogs/cmu

    Freitag, 27. Juni 2014 08:56
  • Hallo Olaf, Hallo Christoph,

    erst mal Danke für Eure schnellen Antworten.

    Allerdings gingen die etwas am Thema vorbei. In keiner der beiden Antworten erscheint die Importtabelle. Das mit der Running Sum ist ja eigentlich schon mit dem UPDATE abgehakt.

    Mir ging es darum, ob ich schon beim Import die Datensätze fertig speichern kann, indem diese quasi nacheinander (Wie beim Cursor) geschrieben werden. Daher sollte die Lösung auf jeden Fall ein INSERT enthalten.

    Gruß und schönes Wochenende

    Roland


    It's no problem, it's just the syntax

    Freitag, 27. Juni 2014 09:16
  • Hallo Roland,

    Du kannst die Lösungsansätze nach dem Import einfach auf die tblStamm Tabelle anwenden, oder Du ermittelst vorab einmal den letzten Bestandswert, wendest die Lösung auf die tblImport an, wobei Du den letzten Wert hinzuaddierst.


    Olaf Helper

    [ Blog] [ Xing] [ MVP]

    Freitag, 27. Juni 2014 11:16