2009-10-19 30 views
58

Đây là kịch bản của tôi:T-SQL: Lặp qua một mảng các giá trị đã biết

Giả sử tôi có một thủ tục được lưu trữ khác trên một tập hợp các id cụ thể; Có cách nào để làm việc này không?

tức là thay vì cần phải làm điều này:

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

Làm một cái gì đó như thế này:

*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 

Mục tiêu chính của tôi ở đây chỉ đơn giản là khả năng bảo trì (dễ dàng để loại bỏ/thêm id như những thay đổi kinh doanh) , có thể liệt kê tất cả các Id trên một dòng ... Hiệu suất không được lớn như một vấn đề

+0

gì phiên bản của SQL Server? –

+0

liên quan, nếu bạn cần lặp lại trên danh sách không phải số nguyên như varchars, giải pháp với con trỏ: [iterate-through-a-list-of-strings-in-sql-server] (https://stackoverflow.com/questions)/38388144/iterate-through-a-list-of-strings-in-sql-server) – Pac0

Trả lời

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

Tôi đã hy vọng sẽ có một cách thanh lịch hơn, nhưng tôi nghĩ điều này sẽ gần như tôi có thể nhận được: Kết thúc bằng cách sử dụng lai giữa việc sử dụng các công đoàn/lựa chọn ở đây và con trỏ từ thí dụ. Cảm ơn! – John

+11

@john: nếu bạn đang sử dụng năm 2008, bạn có thể thực hiện một số việc như INSERT @ids VALUES (4), (7), (12), (22), (19) –

+2

. hơn là con trỏ (mặc dù với 5 giá trị tôi khó có thể thấy được sự khác biệt), nhưng lý do lớn nhất tôi thích chúng là tôi tìm cú pháp tương tự với những gì bạn tìm thấy trong mã ứng dụng, trong khi con trỏ xuất hiện (với tôi) tương đối khác. –

33

Điều tôi làm trong trường hợp này là tạo một biến bảng để giữ các Id.

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

- (hoặc gọi bảng khác chức năng có giá trị để tạo ra bảng này)

Sau đó, vòng lặp dựa trên các hàng trong bảng này

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 

hoặc ...

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 

Một trong các cách tiếp cận trên là nhanh hơn nhiều so với con trỏ (được tuyên bố đối với Bảng Người dùng thông thường). Các biến có giá trị bảng có một đại diện xấu bởi vì khi được sử dụng không đúng cách, (đối với các bảng rất rộng với số lượng lớn các hàng) chúng không thực hiện được. Nhưng nếu bạn đang sử dụng chúng chỉ để giữ một giá trị quan trọng hoặc một số nguyên 4 byte, với một chỉ mục (như trong trường hợp này) chúng cực kỳ nhanh.

+0

Cách tiếp cận trên tương đương hoặc chậm hơn con trỏ được khai báo trên một biến bảng. Nó chắc chắn không nhanh hơn. Nó sẽ nhanh hơn con trỏ được khai báo với các tùy chọn mặc định trên các bảng người dùng thông thường. –

+0

@Peter, ahhh, có bạn là chính xác, tôi không chính xác giả định rằng bằng cách sử dụng một con trỏ ngụ ý một bảng người dùng thường xuyên, không phải là một biến bảng ..Ihave chỉnh sửa để làm rõ sự khác biệt –

14

sử dụng một biến con trỏ tĩnh và một 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 

Cursors có một đại diện xấu từ các tùy chọn mặc định khi công bố chống lại các bảng sử dụng có thể tạo ra rất nhiều chi phí.

Nhưng trong trường hợp này, phí trên là khá nhỏ, ít hơn bất kỳ phương pháp nào khác ở đây. STATIC nói với SQL Server để thực hiện các kết quả trong tempdb và sau đó lặp lại điều đó. Đối với các danh sách nhỏ như thế này, đó là giải pháp tối ưu.

4

Tôi thường sử dụng các phương pháp sau đây

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

Bạn có thể thử như sau:

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

Tôi muốn khai báo danh sách như sau: '@list = '4,7,12,22,19' + ','' - vì vậy hoàn toàn rõ ràng rằng danh sách phải kết thúc bằng dấu phẩy (nó không ' t làm việc mà không có nó!). –

Các vấn đề liên quan