2009-10-19 3 views
58

의 배열을 통해 반복은 여기 내 시나리오입니다 :T-SQL : 알려진 값

의가 나는 특정 ID의 집합에 다른 저장 프로 시저를 호출 할 필요가있는 저장 프로 시저가 있다고 가정 해 봅시다; 이것을 할 수있는 방법이 있습니까?

즉, 대신에이 작업을 수행 할 필요의이 같은 수행

exec p_MyInnerProcedure 4 
exec p_MyInnerProcedure 7 
exec p_MyInnerProcedure 12 
exec p_MyInnerProcedure 22 
exec p_MyInnerProcedure 19 

:

*magic where I specify my list contains 4,7,12,22,19* 

DECLARE my_cursor CURSOR FAST_FORWARD FOR 
*magic select* 

OPEN my_cursor 
FETCH NEXT FROM my_cursor INTO @MyId 
WHILE @@FETCH_STATUS = 0 
BEGIN 

exec p_MyInnerProcedure @MyId 

FETCH NEXT FROM my_cursor INTO @MyId 
END 

여기 내 주요 목표는 유지 보수가 간단을 (쉽게 제거하기/비즈니스 변화에 따라 아이디의 추가) , 모든 ID를 한 줄에 나열 할 수 있습니다 ... 성능이 큰 이슈가되어서는 안됩니다

+0

SQL 서버의 버전은 무엇? –

+0

과 관련하여 varchars와 같은 정수가 아닌 목록을 반복해야하는 경우 커서가있는 솔루션 : [iterate-through-a-lists-of-sql-server] (https://stackoverflow.com/questions)/38388144/iterate-through-a-lists-of-sql-server-in-sql-server) – Pac0

답변

74
declare @ids table(idx int identity(1,1), id int) 

insert into @ids (id) 
    select 4 union 
    select 7 union 
    select 12 union 
    select 22 union 
    select 19 

declare @i int 
declare @cnt int 

select @i = min(idx) - 1, @cnt = max(idx) from @ids 

while @i < @cnt 
begin 
    select @i = @i + 1 

    declare @id = select id from @ids where idx = @i 

    exec p_MyInnerProcedure @id 
end 
+0

나는 더 우아한 방법이 있기를 바랐다. 그러나 나는 이것이 얻을 수있는만큼 가깝다고 생각한다. 여기에서 선택/조합을 사용하는 하이브리드를 사용하고, 예. 감사! – John

+11

@john : 2008을 사용하는 경우 INSERT @ids VALUES (4), (7), (12), (22), (19) –

+2

과 같은 작업을 수행 할 수 있습니다. (비록 5 가지 값으로는 차이점을 거의 볼 수 없지만) 내가 좋아하는 가장 큰 이유는 커서가 애플리케이션 코드에서 찾을 수있는 것과 비슷한 문법을 ​​사용한다는 것입니다. 반면에 커서는 (나에게) 상대적으로 다르다. –

33

이 시나리오에서 Id를 보유 할 테이블 변수를 만듭니다.

Declare @Ids Table (id integer primary Key not null) 
    Insert @Ids(id) values (4),(7),(12),(22),(19) 

- (또는 표를 생성하는 다른 테이블을 반환 함수 호출)

이어서 표

Declare @Id Integer 
    While exists (Select * From @Ids) 
    Begin 
     Select @Id = Min(id) from @Ids 
     exec p_MyInnerProcedure @Id 
     Delete from @Ids Where id = @Id 
    End 

또는 행 ...에 기초하여 루프

Declare @Id Integer = 0 -- assuming all Ids are > 0 
    While exists (Select * From @Ids 
       where id > @Id) 
    Begin 
     Select @Id = Min(id) 
     from @Ids Where id > @Id 
     exec p_MyInnerProcedure @Id 
    End 

위의 방법 중 하나가 커서 (일반 사용자 표에 대해 선언 된 것)보다 훨씬 빠릅니다. 테이블 값 변수는 부적절하게 사용되면 (행 수가 많은 매우 넓은 테이블의 경우) 성능이 좋지 않기 때문에 불량 담당자가 있습니다. 그러나 인덱스를 사용하여 키 값 또는 4 바이트 정수 만 보유하는 경우 (이 경우) 매우 빠릅니다.

+0

위 접근 방식은 테이블 변수에 선언 된 커서와 동일하거나 느립니다. 확실히 빠르지는 않습니다. 그것은 일반 사용자 테이블에서 기본 옵션을 사용하여 선언 된 커서보다 빠릅니다. –

+0

@Peter, 네, 맞습니다. 커서를 사용하는 것이 테이블 변수가 아닌 일반 사용자 테이블을 사용한다고 잘못 가정합니다.나는 명확한 구별을하기 위해 편집했다. –

14

사용 정적 커서 변수와 split function :

declare @comma_delimited_list varchar(4000) 
set @comma_delimited_list = '4,7,12,22,19' 

declare @cursor cursor 
set @cursor = cursor static for 
    select convert(int, Value) as Id from dbo.Split(@comma_delimited_list) a 

declare @id int 
open @cursor 
while 1=1 begin 
    fetch next from @cursor into @id 
    if @@fetch_status <> 0 break 
    ....do something.... 
end 
-- not strictly necessary w/ cursor variables since they will go out of scope like a normal var 
close @cursor 
deallocate @cursor 

커서 많은 오버 헤드를 생성 할 수 있습니다 사용자 테이블에 대해 선언 된 기본 옵션부터 나쁜 담당자가 있습니다.

그러나이 경우 오버 헤드는 다른 어떤 방법보다 작습니다. STATIC은 SQL Server에 tempdb에서 결과를 구체화하고 그 결과를 반복하도록 지시합니다. 이와 같은 작은 목록의 경우 최적의 솔루션입니다.

4

나는 보통 당신은 다음과 같이 시도 할 수 있습니다 다음과 같은 방법을

DECLARE @calls TABLE (
    id INT IDENTITY(1,1) 
    ,parameter INT 
    ) 

INSERT INTO @calls 
select parameter from some_table where some_condition -- here you populate your parameters 

declare @i int 
declare @n int 
declare @myId int 
select @i = min(id), @n = max(id) from @calls 
while @i <= @n 
begin 
    select 
     @myId = parameter 
    from 
     @calls 
    where id = @i 

     EXECUTE p_MyInnerProcedure @myId 
    set @i = @i+1 
end 
2
CREATE TABLE #ListOfIDs (IDValue INT) 

DECLARE @IDs VARCHAR(50), @ID VARCHAR(5) 
SET @IDs = @OriginalListOfIDs + ',' 

WHILE LEN(@IDs) > 1 
BEGIN 
SET @ID = SUBSTRING(@IDs, 0, CHARINDEX(',', @IDs)); 
INSERT INTO #ListOfIDs (IDValue) VALUES(@ID); 
SET @IDs = REPLACE(',' + @IDs, ',' + @ID + ',', '') 
END 

SELECT * 
FROM #ListOfIDs 
1

를 사용

declare @list varchar(MAX), @i int 
select @i=0, @list ='4,7,12,22,19,' 

while(@i < LEN(@list)) 
begin 
    declare @item varchar(MAX) 
    SELECT @item = SUBSTRING(@list, @i,CHARINDEX(',',@list,@i)[email protected]) 
    select @item 

    --do your stuff here with @item 
    exec p_MyInnerProcedure @item 

    set @i = CHARINDEX(',',@list,@i)+1 
    if(@i = 0) set @i = LEN(@list) 
end 
+2

나는 다음과 같이리스트 선언을 만들 것이다 :'@list = '4,7,12,22,19'+ ',''-리스트가 쉼표로 끝나야한다는 것이 완전히 명확하다. (그것은 ' 그것없이 일하지 마라!). –