source

SQL Server에서 Forech를 쓰는 방법

lovecheck 2023. 4. 7. 21:32
반응형

SQL Server에서 Forech를 쓰는 방법

저는 각각을 위한 무언가를 달성하려고 합니다.반환된 선택문의 ID를 가져와 각각의 ID를 사용하고 싶습니다.

DECLARE @i int
DECLARE @PractitionerId int
DECLARE @numrows int
DECLARE @Practitioner TABLE (
    idx smallint Primary Key IDENTITY(1,1)
    , PractitionerId int
)

INSERT @Practitioner
SELECT distinct PractitionerId FROM Practitioner

SET @i = 1
SET @numrows = (SELECT COUNT(*) FROM Practitioner)
IF @numrows > 0
    WHILE (@i <= (SELECT MAX(idx) FROM Practitioner))
    BEGIN

        SET @PractitionerId = (SELECT PractitionerId FROM @Practitioner WHERE idx = @i)

        --Do something with Id here
        PRINT @PractitionerId

        SET @i = @i + 1
    END

현재 위와 같은 오류가 발생하고 있습니다.

열 이름 'idx'가 잘못되었습니다.

사용하시는 것 같습니다.CURSOR대부분의 경우 세트 기반 솔루션을 사용하는 것이 가장 좋지만, 경우에 따라서는CURSOR가장 좋은 해결책입니다.고객의 실제 문제에 대해 자세히 알지 못하면 그 이상의 도움을 드릴 수 없습니다.

DECLARE @PractitionerId int

DECLARE MY_CURSOR CURSOR 
  LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR 
SELECT DISTINCT PractitionerId 
FROM Practitioner

OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO @PractitionerId
WHILE @@FETCH_STATUS = 0
BEGIN 
    --Do something with Id here
    PRINT @PractitionerId
    FETCH NEXT FROM MY_CURSOR INTO @PractitionerId
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR

[ ActionerId ]컬럼이 고유하다고 가정하면 다음 루프를 사용할 수 있습니다.

DECLARE @PractitionerId int = 0
WHILE(1 = 1)
BEGIN
  SELECT @PractitionerId = MIN(PractitionerId)
  FROM dbo.Practitioner WHERE PractitionerId > @PractitionerId
  IF @PractitionerId IS NULL BREAK
  SELECT @PractitionerId
END

일반적으로 (거의 항상) 커서보다 성능이 뛰어나고 간단합니다.

DECLARE @PractitionerList TABLE(PracticionerID INT)
DECLARE @PracticionerID INT
    
INSERT @PractitionerList(PracticionerID)
SELECT PracticionerID
FROM Practitioner
    
WHILE(1 = 1)
BEGIN
            
    SET @PracticionerID = NULL
    SELECT TOP(1) @PracticionerID = PracticionerID
    FROM @PractitionerList
    
    IF @PracticionerID IS NULL
        BREAK
            
    PRINT 'DO STUFF'
    
    DELETE TOP(1) FROM @PractitionerList
    
END

선택 카운트와 최대 선택 값은 실제 테이블이 아닌 테이블 변수에서 지정해야 합니다.

DECLARE @i int
DECLARE @PractitionerId int
DECLARE @numrows int
DECLARE @Practitioner TABLE (
    idx smallint Primary Key IDENTITY(1,1)
    , PractitionerId int
)

INSERT @Practitioner
SELECT distinct PractitionerId FROM Practitioner

SET @i = 1
SET @numrows = (SELECT COUNT(*) FROM @Practitioner)
IF @numrows > 0
    WHILE (@i <= (SELECT MAX(idx) FROM @Practitioner))
    BEGIN

        SET @PractitionerId = (SELECT PractitionerId FROM @Practitioner WHERE idx = @i)

        --Do something with Id here
        PRINT @PractitionerId

        SET @i = @i + 1
    END

이 칼럼을 제외하고 모든 게 잘 될 것 같습니다.idx선택한 테이블에 실제로 존재하지 않습니다.아마도 당신은 그 중에서 선택하라는 뜻이었을 것이다.@Practitioner:

WHILE (@i <= (SELECT MAX(idx) FROM @Practitioner))

위의 코드에 다음과 같이 정의되어 있기 때문입니다.

DECLARE @Practitioner TABLE (
    idx smallint Primary Key IDENTITY(1,1)
    , PractitionerId int
)

사용하시는 버전에서는 다음 행이 올바르지 않습니다.

WHILE (@i <= (SELECT MAX(idx) FROM @Practitioner))

(@ 누락)

테이블이 더 달라지도록 명명 규칙을 변경하는 것이 좋습니다.

여기 더 나은 해결책 중 하나가 있습니다.

DECLARE @i int
            DECLARE @curren_val int
            DECLARE @numrows int
            create table #Practitioner (idx int IDENTITY(1,1), PractitionerId int)
            INSERT INTO #Practitioner (PractitionerId) values (10),(20),(30)
            SET @i = 1
            SET @numrows = (SELECT COUNT(*) FROM #Practitioner)
            IF @numrows > 0
            WHILE (@i <= (SELECT MAX(idx) FROM #Practitioner))
            BEGIN

                SET @curren_val = (SELECT PractitionerId FROM #Practitioner WHERE idx = @i)

                --Do something with Id here
                PRINT @curren_val
                SET @i = @i + 1
            END

여기 표에 몇 가지 값을 추가했습니다.처음에는 비어 있습니다.

루프 본체에 접속하거나 루프 본체에 있는 모든 작업을 수행할 수 있습니다.또, idx 를 테이블 정의내에서 정의해 액세스 할 수도 있습니다.

              BEGIN
                SET @curren_val = (SELECT PractitionerId FROM #Practitioner WHERE idx = @i)

                --Do something with Id here

                PRINT @curren_val
                SET @i = @i + 1
            END

제가 만든 절차는FOREACH와 함께CURSOR모든 테이블에서 사용할 수 있습니다.

사용 예:

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

결과는 각 행에 대해 2가 선택됩니다.의 구문UPDATE를 부수고FOREACH힌트에 써있네요.

프로시저 코드는 다음과 같습니다.

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

커서는 보통 끔찍한 악으로 간주되지만 FAST_FORWARD 커서의 경우라고 생각합니다.이것은 TSQL에서 FORACH에 가장 가까운 것입니다.

저는 이것을 하는 매우 효과적이고 읽기 쉬운 방법을 생각해 냈습니다.

  1. 임시 테이블을 만들고 반복할 레코드를 여기에 넣습니다.

  2. 사용하다WHILE @@ROWCOUNT <> 0반복하다

  3. 한 번에 한 줄씩 하려면SELECT TOP 1 <fieldnames>

    b. 해당 행의 고유 ID를 변수에 저장합니다.

  4. 작업을 수행한 후 스텝 3b에서 저장한 ID에 따라 임시 테이블에서 행을 삭제합니다.

여기 암호가 있습니다.죄송합니다. 질문의 변수 이름 대신 제 변수 이름을 사용하고 있습니다.

DECLARE @tempPFRunStops TABLE (
    ProformaRunStopsID int,
    ProformaRunMasterID int,
    CompanyLocationID int,
    StopSequence int
);

INSERT @tempPFRunStops (ProformaRunStopsID, ProformaRunMasterID, CompanyLocationID, StopSequence)
SELECT 
    ProformaRunStopsID, 
    ProformaRunMasterID, 
    CompanyLocationID, 
    StopSequence 
FROM ProformaRunStops
WHERE ProformaRunMasterID IN ( 
    SELECT ProformaRunMasterID 
    FROM ProformaRunMaster 
    WHERE ProformaId = 15 )

-- SELECT * FROM @tempPFRunStops

WHILE @@ROWCOUNT <> 0  -- << I dont know how this works
BEGIN
    SELECT TOP 1 * FROM @tempPFRunStops
    -- I could have put the unique ID into a variable here
    
    SELECT 'Ha'  -- Do Stuff
    
    DELETE @tempPFRunStops 
    WHERE ProformaRunStopsID = (SELECT TOP 1 ProformaRunStopsID FROM @tempPFRunStops)
END

언급URL : https://stackoverflow.com/questions/18513986/how-to-write-a-foreach-in-sql-server

반응형