Ho fatto una procedura che esegue un FOREACH
con CURSOR
per qualsiasi tabella.
Esempio di utilizzo:
CREATE TABLE #A (I INT, J INT)
INSERT INTO #A VALUES (1, 2), (2, 3)
EXEC PRC_FOREACH
#A --Table we want to do the FOREACH
, 'SELECT @I, @J' --The execute command, each column becomes a variable in the same type, so DON'T USE SPACES IN NAMES
--The third variable is the database, it's optional because a table in TEMPB or the DB of the proc will be discovered in code
Il risultato è 2 selezioni per ogni riga. La sintassi di UPDATE
e spezzare il FOREACH
sono scritti nei suggerimenti.
Questo è il codice proc:
CREATE PROC [dbo].[PRC_FOREACH] (@TBL VARCHAR(100) = NULL, @EXECUTE NVARCHAR(MAX)=NULL, @DB VARCHAR(100) = NULL) AS BEGIN
--LOOP BETWEEN EACH TABLE LINE
IF @TBL + @EXECUTE IS NULL BEGIN
PRINT '@TBL: A TABLE TO MAKE OUT EACH LINE'
PRINT '@EXECUTE: COMMAND TO BE PERFORMED ON EACH FOREACH TRANSACTION'
PRINT '@DB: BANK WHERE THIS TABLE IS (IF NOT INFORMED IT WILL BE DB_NAME () OR TEMPDB)' + CHAR(13)
PRINT 'ROW COLUMNS WILL VARIABLE WITH THE SAME NAME (COL_A = @COL_A)'
PRINT 'THEREFORE THE COLUMNS CANT CONTAIN SPACES!' + CHAR(13)
PRINT 'SYNTAX UPDATE:
UPDATE TABLE
SET COL = NEW_VALUE
WHERE CURRENT OF MY_CURSOR
CLOSE CURSOR (BEFORE ALL LINES):
IF 1 = 1 GOTO FIM_CURSOR'
RETURN
END
SET @DB = ISNULL(@DB, CASE WHEN LEFT(@TBL, 1) = '#' THEN 'TEMPDB' ELSE DB_NAME() END)
--Identifies the columns for the variables (DECLARE and INTO (Next cursor line))
DECLARE @Q NVARCHAR(MAX)
SET @Q = '
WITH X AS (
SELECT
A = '', @'' + NAME
, B = '' '' + type_name(system_type_id)
, C = CASE
WHEN type_name(system_type_id) IN (''VARCHAR'', ''CHAR'', ''NCHAR'', ''NVARCHAR'') THEN ''('' + REPLACE(CONVERT(VARCHAR(10), max_length), ''-1'', ''MAX'') + '')''
WHEN type_name(system_type_id) IN (''DECIMAL'', ''NUMERIC'') THEN ''('' + CONVERT(VARCHAR(10), precision) + '', '' + CONVERT(VARCHAR(10), scale) + '')''
ELSE ''''
END
FROM [' + @DB + '].SYS.COLUMNS C WITH(NOLOCK)
WHERE OBJECT_ID = OBJECT_ID(''[' + @DB + '].DBO.[' + @TBL + ']'')
)
SELECT
@DECLARE = STUFF((SELECT A + B + C FROM X FOR XML PATH('''')), 1, 1, '''')
, @INTO = ''--Read the next line
FETCH NEXT FROM MY_CURSOR INTO '' + STUFF((SELECT A + '''' FROM X FOR XML PATH('''')), 1, 1, '''')'
DECLARE @DECLARE NVARCHAR(MAX), @INTO NVARCHAR(MAX)
EXEC SP_EXECUTESQL @Q, N'@DECLARE NVARCHAR(MAX) OUTPUT, @INTO NVARCHAR(MAX) OUTPUT', @DECLARE OUTPUT, @INTO OUTPUT
--PREPARE TO QUERY
SELECT
@Q = '
DECLARE ' + @DECLARE + '
-- Cursor to scroll through object names
DECLARE MY_CURSOR CURSOR FOR
SELECT *
FROM [' + @DB + '].DBO.[' + @TBL + ']
-- Opening Cursor for Reading
OPEN MY_CURSOR
' + @INTO + '
-- Traversing Cursor Lines (While There)
WHILE @@FETCH_STATUS = 0
BEGIN
' + @EXECUTE + '
-- Reading the next line
' + @INTO + '
END
FIM_CURSOR:
-- Closing Cursor for Reading
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR'
EXEC SP_EXECUTESQL @Q --MAGIA
END