SQL Server 루프-레코드 세트를 반복하는 방법


151

선택에서 레코드 세트를 반복하는 방법은 무엇입니까?

예를 들어 몇 개의 레코드를 반복하여 각 레코드로 무언가를 수행한다고 가정하십시오. 내 선택의 기본 버전은 다음과 같습니다.

select top 1000 * from dbo.table
where StatusID = 7 

감사


5
각 기록에 대해 무엇을 하시겠습니까? SQL 조회에서 작업을 수행하는 것이 선호됩니다. 커서와 함께 T-SQL을 사용해야 할 필요는 없습니다.
Gordon Linoff

2
커서를 사용합니다.
FloChanz

5
그것은 매우 느릴 것입니다-저장된 proc을 다시 쓰거나 로직을 세트 기반 방식으로 작동시키는 것이 불가능합니까?
Bridge

2
@ 펑키 Sproc은 무엇을합니까? 종종 코드는 세트 기반 방식으로 다시 작성 될 수 있습니다 (즉, 루프 방지). RBAR 작업 ( simple-talk.com/sql/t-sql-programming/… ) 을 수행하려는 경우 커서는 조사하려는 것입니다.
gvee

1
아마도이 데이터로 무엇을할지 상세하게 설명 할 수있을 것입니다. 대부분의 경우 개별 레코드를 반복하는 대신 한 번의 작업으로 필요한 작업을 수행하는 단일 SQL 쿼리를 쉽게 작성할 수 있습니다.
Alan Barber

답변:


212

T-SQL과 다음과 같은 커서를 사용하여 :

DECLARE @MyCursor CURSOR;
DECLARE @MyField YourFieldDataType;
BEGIN
    SET @MyCursor = CURSOR FOR
    select top 1000 YourField from dbo.table
        where StatusID = 7      

    OPEN @MyCursor 
    FETCH NEXT FROM @MyCursor 
    INTO @MyField

    WHILE @@FETCH_STATUS = 0
    BEGIN
      /*
         YOUR ALGORITHM GOES HERE   
      */
      FETCH NEXT FROM @MyCursor 
      INTO @MyField 
    END; 

    CLOSE @MyCursor ;
    DEALLOCATE @MyCursor;
END;

5
올바른 것은 프로세스를 다시 작성하여 반복 할 필요가 없도록하는 것입니다. 루핑은 데이터베이스에서 매우 나쁜 선택입니다.
HLGEM

23
아마도 당신은 맞지만 질문에 주어진 정보로 사용자가 데이터 세트를 반복하고 싶을 때 답변을 썼습니다 ... 커서가 그것을 할 수있는 방법입니다.
FloChanz

16
커서는 도구 일뿐입니다. 일반적으로 커서에 대한 옳고 그른 것은 없습니다. 성능을 관찰하고 결정하십시오. 이 답변 (커서)은 하나의 가능한 선택입니다. WHILE LOOP, CTE 등을 사용할 수도 있습니다.
Chains

2
@FrenkyB 그렇습니다. 이런 식으로 봐 ... stackoverflow.com/questions/11035187/…
sam yi

2
축하합니다. 솔루션은 msdn : msdn.microsoft.com/en-us/library/ 에도 있습니다. 필드 데이터 형식을 사용하는 방법이 정말 마음에 듭니다.
Pete

111

반복적 인 작업을 수행 해야하는 경우 이것이 내가 한 일입니다 ...하지만 먼저 집합 연산을 찾는 것이 좋습니다.

select top 1000 TableID
into #ControlTable 
from dbo.table
where StatusID = 7

declare @TableID int

while exists (select * from #ControlTable)
begin

    select top 1 @TableID = TableID
    from #ControlTable
    order by TableID asc

    -- Do something with your TableID

    delete #ControlTable
    where TableID = @TableID

end

drop table #ControlTable

4
CURSOR (아래 답변 참조)를 사용하는 것이 훨씬 더 우아한 솔루션 인 것 같습니다.
Mikhail Glukhov

이 답변에 커서 솔루션보다 더 많은 투표가 필요한 이유는 무엇입니까?
ataravati 2016 년

29
@ataravati이 솔루션은 커서보다 많은 프로그래머에게보다 명확하게 읽습니다. 커서 구문은 다소 어색합니다.
Brian Webster

감사합니다! 위의 코드를 사용하여 논리별로 업데이트 및 그룹 화가있는 내 예제 : pastebin.com/GAjUNNi9 . 아마 누군가에게 유용 할 것입니다.
Nigrimmist

루프 내부의 update 문에서 변수를 열 이름으로 사용할 수 있습니까? "TableName SET @ ColumnName = 2 업데이트"와 같은 것
MH

28

더 나은 가독성 을 위해 sam yi의 답변약간 변경했습니다 .

select top 1000 TableID
into #ControlTable 
from dbo.table
where StatusID = 7

declare @TableID int

while exists (select * from #ControlTable)
begin

    select @TableID = (select top 1 TableID
                       from #ControlTable
                       order by TableID asc)

    -- Do something with your TableID

    delete #ControlTable
    where TableID = @TableID

end

drop table #ControlTable

1
@ bluish,이 답변은 sam yi의 답변을 수정합니다. 이 정정은 주로 select @TableID = (...)성명서 안에 있습니다.
Simple Sandman

이 답변은
반드시이

14

커서를 사용하면 레코드를 개별적으로 쉽게 반복하고 레코드를 개별적으로 또는 모든 레코드를 포함한 단일 메시지로 인쇄 할 수 있습니다.

DECLARE @CustomerID as INT;
declare @msg varchar(max)
DECLARE @BusinessCursor as CURSOR;

SET @BusinessCursor = CURSOR FOR
SELECT CustomerID FROM Customer WHERE CustomerID IN ('3908745','3911122','3911128','3911421')

OPEN @BusinessCursor;
    FETCH NEXT FROM @BusinessCursor INTO @CustomerID;
    WHILE @@FETCH_STATUS = 0
        BEGIN
            SET @msg = '{
              "CustomerID": "'+CONVERT(varchar(10), @CustomerID)+'",
              "Customer": {
                "LastName": "LastName-'+CONVERT(varchar(10), @CustomerID) +'",
                "FirstName": "FirstName-'+CONVERT(varchar(10), @CustomerID)+'",    
              }
            }|'
        print @msg
    FETCH NEXT FROM @BusinessCursor INTO @CustomerID;
END

1
이것은 흥미로워 보인다. @ 식별자가 무엇을 의미하는지 궁금합니다.
netskink

@는 변수로 구별하는 것입니다.
Agnel Amodia

9

임시 테이블을 사용하는 것이 좋은 경우 또 다른 방법입니다. 개인적으로 테스트 한 결과 임시 테이블에 데이터가없는 경우에도 예외가 발생하지 않습니다.

CREATE TABLE #TempTable
(
    ROWID int identity(1,1) primary key,
    HIERARCHY_ID_TO_UPDATE int,
)

--create some testing data
--INSERT INTO #TempTable VALUES(1)
--INSERT INTO #TempTable VALUES(2)
--INSERT INTO #TempTable VALUES(4)
--INSERT INTO #TempTable VALUES(6)
--INSERT INTO #TempTable VALUES(8)

DECLARE @MAXID INT, @Counter INT

SET @COUNTER = 1
SELECT @MAXID = COUNT(*) FROM #TempTable

WHILE (@COUNTER <= @MAXID)
BEGIN
    --DO THE PROCESSING HERE 
    SELECT @HIERARCHY_ID_TO_UPDATE = PT.HIERARCHY_ID_TO_UPDATE
    FROM #TempTable AS PT
    WHERE ROWID = @COUNTER

    SET @COUNTER = @COUNTER + 1
END


IF (OBJECT_ID('tempdb..#TempTable') IS NOT NULL)
BEGIN
    DROP TABLE #TempTable
END

정말 이상합니다. 여기에는 많은 오류가 포함되어 있으며 1에서 1로 COUNT(*), 두 번째 에서 1로가는 두 가지 변수를 사용하는 COUNT(*)것도 이상합니다.
David Ferenczy Rogožan

변수 MAXID는 루프 스루하는 데 사용됩니다. 변수 COUNTER은 테이블의 특정 레코드에서 작업을 수행하는 데 사용됩니다. 질문을 읽으면 "내가 반복하고 각 레코드로 무언가를 수행하고 싶은 몇 가지 레코드가 있습니다"라는 내용에 대해 이야기합니다. 나는 틀렸을지도 모르지만 @DAWID
Sandeep

2
코드에서 이러한 변수를 어떻게 사용하는지 분명합니다. 당신은 가질 수 있고 매 반복마다 WHILE (@COUTNER <= @ROWID)감소시킬 필요가 없습니다 @ROWID. BTW ROWID테이블의 s가 연속적이지 않은 경우 (일부 행이 이전에 삭제 된 경우) 어떻게됩니까 ?
David Ferenczy Rogožan

1
커서를 사용하여 임시 테이블을 사용하는 것이 언제 권장됩니까? 이것은 단순히 디자인 선택입니까, 아니면 성능이 더 좋은가요?
h0r53

4

데이터 세트를 반복하는 동안 데이터 순위를 정하고 ROW_NUMBER를 추가하고 0으로 카운트 다운 할 수 있습니다.

-- Get your dataset and rank your dataset by adding a new row_number
SELECT  TOP 1000 A.*, ROW_NUMBER() OVER(ORDER BY A.ID DESC) AS ROW
INTO #TEMPTABLE 
FROM DBO.TABLE AS A
WHERE STATUSID = 7;

--Find the highest number to start with
DECLARE @COUNTER INT = (SELECT MAX(ROW) FROM #TEMPTABLE);
DECLARE @ROW INT;

-- Loop true your data until you hit 0
WHILE (@COUNTER != 0)
BEGIN

    SELECT @ROW = ROW
    FROM #TEMPTABLE
    WHERE ROW = @COUNTER
    ORDER BY ROW DESC

    --DO SOMTHING COOL  

    -- SET your counter to -1
    SET @COUNTER = @ROW -1
END

DROP TABLE #TEMPTABLE

2

이런 식으로 테이블 데이터를 반복 할 수 있습니다.

DECLARE @_MinJobID INT
DECLARE @_MaxJobID INT
CREATE  TABLE #Temp (JobID INT)

INSERT INTO #Temp SELECT * FROM DBO.STRINGTOTABLE(@JobID,',')
SELECT @_MinJID = MIN(JobID),@_MaxJID = MAX(JobID)  FROM #Temp

    WHILE @_MinJID <= @_MaxJID
    BEGIN

        INSERT INTO Mytable        
        (        
            JobID,        
        )        

        VALUES        
        (        
            @_MinJobID,        
        ) 

        SET @_MinJID = @_MinJID + 1;
    END

DROP TABLE #Temp

STRINGTOTABLE 은 쉼표로 구분 된 데이터와 리턴 테이블을 구문 분석하는 사용자 정의 함수입니다. 감사


1

나는 이것이 항목을 반복하는 쉬운 방법이라고 생각합니다.

declare @cateid int
select CateID into [#TempTable] from Category where GroupID = 'STOCKLIST'

while (select count(*) from #TempTable) > 0
begin
    select top 1 @cateid = CateID from #TempTable
    print(@cateid)

    --DO SOMETHING HERE

    delete #TempTable where CateID = @cateid
end

drop table #TempTable
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.