CiAgICA8IS0tIExpbmtlZEluIC0tPgogICAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgogICAgICAgIF9saW5rZWRpbl9wYXJ0bmVyX2lkID0gIjEyMzUwNzMiOwogICAgICAgIHdpbmRvdy5fbGlua2VkaW5fZGF0YV9wYXJ0bmVyX2lkcyA9IHdpbmRvdy5fbGlua2VkaW5fZGF0YV9wYXJ0bmVyX2lkcyB8fCBbXTsKICAgICAgICB3aW5kb3cuX2xpbmtlZGluX2RhdGFfcGFydG5lcl9pZHMucHVzaChfbGlua2VkaW5fcGFydG5lcl9pZCk7CiAgICA8L3NjcmlwdD48c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+CiAgICAgICAgKGZ1bmN0aW9uKCl7dmFyIHMgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgic2NyaXB0IilbMF07CiAgICAgICAgICAgIHZhciBiID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7CiAgICAgICAgICAgIGIudHlwZSA9ICJ0ZXh0L2phdmFzY3JpcHQiO2IuYXN5bmMgPSB0cnVlOwogICAgICAgICAgICBiLnNyYyA9ICJodHRwczovL3NuYXAubGljZG4uY29tL2xpLmxtcy1hbmFseXRpY3MvaW5zaWdodC5taW4uanMiOwogICAgICAgICAgICBzLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGIsIHMpO30pKCk7CiAgICA8L3NjcmlwdD4KICAgIDxub3NjcmlwdD4KICAgICAgICA8aW1nIGhlaWdodD0iMSIgd2lkdGg9IjEiIHN0eWxlPSJkaXNwbGF5Om5vbmU7IiBhbHQ9IiIgc3JjPSJodHRwczovL3B4LmFkcy5saW5rZWRpbi5jb20vY29sbGVjdC8/cGlkPTEyMzUwNzMmZm10PWdpZiIgLz4KICAgIDwvbm9zY3JpcHQ+CiAgICA8IS0tIEVuZCBMaW5rZWRJbiAtLT4KICAgIA==
Generic filters
Exact matches only
Search in title
Search in excerpt
Search in content

Dynamisches SQL mit Ausgabeparameter

Dynamisches SQL erlaubt die Definition und Ausführung von SQL-Anweisungen zur Laufzeit. Teile der Anweisungen befinden sich in Variablen, die ihre Werte zur Laufzeit ändern können. So kann ein dynamisch erstelltes SQL-Statement bei Veränderung der Variablen immer wieder verwendet werden ohne neu erstellt werden zu müssen.

Jeder von uns hat sicher bereits dynamisches SQL entwickelt und verwendet. Seltener wird dabei ein Ausgabewert benötigt. Aber genau um den soll es in diesem Blogbeitrag gehen, da die Programmierung eines Ausgabewertes innerhalb eines dynamischen SQL-Statements bestimmten Regeln unterliegt.

Im Beispiel wird eine Prozedur zum Kopieren von Werten der Wertart 1 zu Wertart 2 bei definiertem Zeitpunkt und Kunden in der Datenbank Chair erstellt. Es wird dafür dynamisches SQL eingesetzt, weil sich die zu kopierenden Werte in mehreren Tabellen befinden. Dabei soll zuvor ermittelt werden, ob zu diesem Zeitpunkt und bei diesem Kunden bei der Wertart 1, die kopiert werden soll, überhaupt Daten vorliegen. Diese Anzahl Datensätze ist also der Ausgabewert, der für jede Tabelle ermittelt wird.

Nun wird die Prozedur erstellt. Sie soll als Übergabeparameter den gewünschten Monat, den Kunden, die Wertart 1, welche kopiert werden soll, und die Wertart 2, in die kopiert werden soll, enthalten. Außerdem werden zu Beginn alle erforderlichen Variablen deklariert.


CREATE PROCEDURE [dbo].[ P_Kopieren_Wertart1_zu_Wertart2] 
@Period_ID varchar(10),
@ValueType1 varchar(50),
@ValueType2 varchar(50), 
@Customer varchar(10)

AS
BEGIN

DECLARE @Tabname varchar(100)
DECLARE @ValueTypeID1 varchar(1)
DECLARE @ValueTypeID2 varchar(1)
DECLARE @SQL varchar(500)
DECLARE @SQL_Anz nvarchar(500)
DECLARE @ParamDef nvarchar(500)
DECLARE @Anz int

Zunächst kann man in der Prozedur die Übergabeparameter auf Gültigkeit überprüfen. Das soll hier nicht weiter thematisiert werden und ist in dem vollständigen Skript der Prozedur enthalten, welches sich im Anhang dieses Blogs befindet.

Da das Kopieren für mehrere Tabellen durchgeführt werden soll, müssen diese zunächst ermittelt und eine Cursor-Variable deklariert werden:


DECLARE tab_cursor CURSOR FOR
SELECT distinct sysobjects.name 
FROM syscolumns 
inner join sysobjects ON syscolumns.id = sysobjects.id
WHERE sysobjects.type = 'U'
and syscolumns.name = 'Revenues'
and sysobjects.name like 'T_Import_%'
and sysobjects.name not like '%_Log'
and sysobjects.name not like '%_Rollback'

Es werden alle Tabellen, die eine Spalte “Revenues” enthalten, mit “T_Import_” beginnen und nicht auf “_Log” oder “_Rollback” enden, selektiert. In der Datenbank Chair sind dies die Tabellen T_IMPORT_FACT und T_Import_Deckungsbeitragsrechnung.

Nun soll ermittelt werden, ob diese Tabellen zu dem in den Parametern angegebenem Zeitpunkt und Kunden aktuelle Werte vorliegen.

Dafür öffnen wir den zuvor definierten Cursor und selektieren den Namen der ersten Tabelle in eine Variable @Tabname.


OPEN tab_cursor
FETCH NEXT FROM tab_cursor INTO @Tabname
WHILE @@fetch_status = 0
BEGIN

Um die Anzahl der Datensätze zu ermitteln, wird folgendes dynamisches SQL erstellt:


SET @SQL_Anz = N'select @AnzOut = COUNT(*) from ' + @Tabname
SET @SQL = @SQL + ' WHERE ValueType = ' + @ValueTypeID1 + ' AND Month = ' + @Period_ID
IF (@Customer_Flag = 1) SET @SQL = @SQL + ' AND Customer in (select CustomerID from T_S_Kunden_Stammdaten_Geo where DE_L4 = ''' + @Customer + ''')'

Die Variable @AnzOut soll die Anzahl der Datensätze enthalten. Wurde ein spezieller Kunde als Paramater an die Prozedur übergeben, wird der Kunde in die WHERE-Bedingung aufgenommen, sonst werden alle Kunden in die Abfrage einbezogen.

Mit der Anweisung:


SET @ParamDef = N'@AnzOut int OUTPUT'

wird die @AnzOut als Ausgabeparameter definiert.

Ausgeführt wird das SQL-Statement über:


EXECUTE sp_executesql @SQL_Anz, @ParamDef, @AnzOUT=@Anz OUTPUT

Ein einfaches Ausführen mit EXEC @SQL_Anz ist nicht möglich. Wichtig ist auch, dass sowohl die Variable für das SQL-Statement (@SQL_Anz) als auch die Variable für die Definition der Parameter (@ParamDef) vom Typ nvarchar sind.

Der eigentlich mit der Anzahl befüllte Parameter @AnzOut muss zudem in eine Variable (hier @Anz) kopiert werden, da es sich bei @AnzOut tatsächlich nur um einen Ausgabeparameter handelt, der nicht als Variable verwendet werden kann.

Mit der Variable @Anz kann nun die Entscheidung getroffen werden, ob Daten kopiert werden oder nicht.


IF (@Anz > 0)
BEGIN

-- Kopierprozess starten

END


 
Anhang
----------------------------------------------------------------------------- Prozedur zum Kopieren von Wertart1 -> Wertart2
-- erforderliche Parameter: Periode, Wertart, Customer
-- Periode: YYYYMM Bsp.: 201412
-- Customer: All für alle REUs
-- Customer für einen ausgewählte Kunden, Bsp.: Dobersohn 
-- Wertart: Ist, Plan,...
-- Mehrfachnennungen sind bei Periode und Customer nicht möglich. Die 
-- Prozedur kann nur für jeweils eine Angabe ausgeführt werden.
-- Prüfungen bei der Ausführung: Periode, Wertart1, Wertart2 und Kunde 
   (All, Customer)
---------------------------------------------------------------------------

CREATE PROCEDURE [dbo].[P_Kopieren_Wertart1_zu_Wertart2] 
@Period_ID varchar(10),
@ValueType1 varchar(50),
@ValueType2 varchar(50),
@Customer varchar(10)

AS
BEGIN

DECLARE @ValueTypeID1 varchar(1)
DECLARE @ValueTypeID2 varchar(1)
DECLARE @Customer_Flag tinyint
DECLARE @Tabname varchar(100)
DECLARE @SQL varchar(500)
DECLARE @SQL_Anz nvarchar(500)
DECLARE @ParamDef nvarchar(500)
DECLARE @Anz int
DECLARE @message varchar(100)

----------------------------------------------------------------------------- Parameter @Customer überprüfen
---------------------------------------------------------------------------
SET @Customer_Flag = 10

IF (@Customer = 'All' or @Customer = '') SET @Customer_Flag = 0
ELSE
	BEGIN
IF ((SELECT COUNT(*) FROM T_S_Kunden_Stammdaten_Geo reg WHERE reg.DE_L4 = @Customer) > 0) SET @Customer_Flag = 1
	END
		
IF (@Customer_Flag = 10) 
BEGIN
	PRINT 'Kunde ist nicht gültig.'
	RETURN (-1)
END

----------------------------------------------------------------------------- Parameter @ValueType1 überprüfen
---------------------------------------------------------------------------SELECT @ValuetypeID1 = isnull(ValueTypeID,'') FROM T_IMPORT_Wertart we WHERE we.DE_Wertart = + @ValueType1

IF (LEN(@ValuetypeID1) = 0)
BEGIN
	PRINT 'Wertart1 ist nicht gültig.'
	RETURN(-1)
END

----------------------------------------------------------------------------- Parameter @ValueType2 überprüfen
---------------------------------------------------------------------------SELECT @ValuetypeID2 = isnull(ValueTypeID,'') FROM T_IMPORT_Wertart we WHERE we.DE_Wertart = + @ValueType2

IF (LEN(@ValuetypeID2) = 0)
BEGIN
	PRINT 'Wertart1 ist nicht gültig.'
	RETURN(-1)
END
	
----------------------------------------------------------------------------- Parameter @Periode überprüfen
---------------------------------------------------------------------------IF ((SELECT COUNT(*) FROM T_DIM_01_03_Monat WHERE MonatID = @Period_ID) = 0)
BEGIN
	PRINT 'Monat ist nicht gültig'
	RETURN (-1)
END

----------------------------------------------------------------------------- Cursor für Tabellen
---------------------------------------------------------------------------DECLARE tab_cursor CURSOR FOR
SELECT distinct sysobjects.name 
FROM syscolumns 
inner join sysobjects ON syscolumns.id = sysobjects.id
WHERE sysobjects.type = 'U'
and syscolumns.name = 'Revenues'
and sysobjects.name like 'T_Import_%'
and sysobjects.name not like '%_Log'
and sysobjects.name not like '%_Rollback'

OPEN tab_cursor
FETCH NEXT FROM tab_cursor INTO @Tabname
WHILE @@fetch_status = 0
BEGIN

	---------------------------------------------------------------------
-- Ermitteln, ob in der Tabelle Werte für Wertart1 für diese   
-- Periode und diese -- Kunden vorliegen
	---------------------------------------------------------------------
	SET @SQL_Anz = N'select @AnzOut = COUNT(*) from ' + @Tabname
SET @SQL_Anz = @SQL_Anz + ' WHERE ValueType = ''' + @ValueTypeID1 + ''' AND 		Month = ' + @Period_ID
IF (@Customer_Flag = 1) SET @SQL_Anz = @SQL_Anz + ' AND Customer in (select CustomerID from T_S_Kunden_Stammdaten_Geo
	where DE_L4 = ''' + @Customer + ''')'
		
	SET @ParamDef = N'@AnzOut int OUTPUT'
		
	EXECUTE sp_executesql @SQL_Anz, @ParamDef, @AnzOUT=@Anz OUTPUT
		
 
	---------------------------------------------------------------------
	-- Datensätze vorhanden
	---------------------------------------------------------------------	IF (@Anz > 0)
	BEGIN
	
		---------------------------------------------------------------
		-- Kopierprozess starten
		---------------------------------------------------------------
		BEGIN TRY
		BEGIN TRANSACTION
					
		---------------------------------------------------------------
		-- vorhandene Datensätze mit Wertart = @ValueType2 löschen
		---------------------------------------------------------------
SET @SQL = 'DELETE FROM ' + @Tabname + ' WHERE ValueType = ''' + @ValueTypeID2 + ''' AND Month = ' + @Period_ID 
IF (@Customer_Flag = 1) SET @SQL = @SQL + ' AND Customer in (select CustomerID from T_S_Kunden_Stammdaten_Geo
		where DE_L4 = ''' + @Customer + ''')'
			
		EXEC (@SQL)
			
		---------------------------------------------------------------
-- Datensätze mit Wertart = @ValueType1 in Hilfstabelle 
-- schreiben
		---------------------------------------------------------------
		SET @SQL = 'SELECT * INTO ' + @Tabname + '_tmp'  
SET @SQL = @SQL + ' FROM ' + @Tabname + ' where ValueType = ''' + @ValueTypeID1 + ''' AND Month = ' + @Period_ID  
IF (@Customer_Flag = 1) SET @SQL = @SQL + ' AND Customer in (select CustomerID from T_S_Kunden_Stammdaten_Geo
		where DE_L4 = ''' + @Customer + ''')'
			
		EXEC (@SQL)
			
		---------------------------------------------------------------
		-- ValueType, ChangeDate und User aktualisieren
		---------------------------------------------------------------
		SET @SQL = 'UPDATE ' + @Tabname + '_tmp'
		SET @SQL = @SQL + ' SET ValueType = ''' + @ValueTypeID2 + ''''
		
		EXEC (@SQL)
			
		---------------------------------------------------------------
		-- Datensätze aus Hilfstabelle einfügen
		---------------------------------------------------------------
		SET @SQL = 'INSERT INTO ' + @Tabname
		SET @SQL = @SQL + ' SELECT * FROM ' + @Tabname + '_tmp'
			
		EXEC (@SQL)
			
		---------------------------------------------------------------
		-- Hilfstabelle löschen
		---------------------------------------------------------------
		SET @SQL = 'DROP TABLE ' + @Tabname + '_tmp' 
		
		EXEC (@SQL)
			
		COMMIT TRANSACTION
		END TRY
		BEGIN CATCH
			-- Transaction zurücksetzen
			ROLLBACK TRANSACTION
			
SET @message = 'Kopieren von ' + @ValueType1 + ' zu ' + @ValueType2 + ' ist fehlgeschlagen.' 
					+ char(13)
					+ error_message()
			PRINT @message
		END CATCH		
	END
		
	FETCH NEXT FROM tab_cursor INTO @Tabname	
END
CLOSE tab_cursor
DEALLOCATE tab_cursor

END