쿼리에서 반환 된 각 행에 대해 저장 프로 시저를 어떻게 한 번 실행합니까?


206

특정 방식으로 사용자 데이터를 변경하는 저장 프로 시저가 있습니다. user_id를 전달하면됩니다. 테이블에서 쿼리를 실행하고 각 user_id마다 해당 user_id에서 저장 프로 시저를 한 번 실행합니다.

이것에 대한 쿼리를 어떻게 작성합니까?


5
대답은 등 SQL 서버, 오라클, MySQL은, 서로 다른 것입니다 - 당신은 RDBMS 지정할 필요
Gary.Ray

5
저장 프로 시저가 전혀 필요하지 않을 수 있습니다. 저장 프로 시저가 정확히 "무엇"을 설명 할 수 있습니까? 전체 프로세스가 단일 업데이트 명령문으로 표현 될 수 있습니다. 가능하면 "각 레코드마다 한 번 수행"패턴을 피해야합니다.
Tomalak

어떤 데이터베이스를 사용하고 있습니까?
SO User

1
이 기사를 읽어야합니다 ... 항목 2는 커서를 사용하지 마십시오 codeproject.com/KB/database/sqldodont.aspx ... 나는 조기 최적화에 대해서도 반대합니다.
Michael Prewecki

7
@MichaelPrewecki : 저조한 기사를 더 읽으면 항목 10이 "서버 쪽 커서를 사용하지 마십시오. 나는 이것이 "내가하는 일을 알고있다"고 생각합니다.
Gabe

답변:


246

커서를 사용하십시오

부록 : [MS SQL 커서 예]

declare @field1 int
declare @field2 int
declare cur CURSOR LOCAL for
    select field1, field2 from sometable where someotherfield is null

open cur

fetch next from cur into @field1, @field2

while @@FETCH_STATUS = 0 BEGIN

    --execute your sproc on each row
    exec uspYourSproc @field1, @field2

    fetch next from cur into @field1, @field2
END

close cur
deallocate cur

MS SQL에서는 다음 기사가 있습니다.

커서는 설정 기반 작업보다 느리지 만 수동 while 루프보다 빠릅니다. 이 SO 질문에 대한 자세한 내용

부록 2 : 몇 개 이상의 레코드를 처리 할 경우 먼저 임시 테이블로 가져 와서 임시 테이블 위로 커서를 이동하십시오. 이렇게하면 SQL이 테이블 잠금으로 에스컬레이션되지 않고 작업 속도가 빨라집니다.

부록 3 : 물론 각 사용자 ID에 대해 스토어드 프로 시저가 수행하는 작업을 인라인하고 전체를 단일 SQL 업데이트 명령문으로 실행할 수 있다면 최적입니다.


21
선언 후 'open cur'를 놓쳤습니다. 이것은 '커서가 열리지 않습니다'오류를 발생시킵니다. 편집을 할 담당자가 없습니다.
Fiona-myaccessible.website

5
의견을 투표하여 사람들에게 감사 할 수 있습니다. 누가 그런 식으로 다음 번에 편집 할 담당자가 있을지 알고 있습니다. :-)
Robino 2016 년

스토어드 프로 시저에 사용 된 필드의 JOINS 및 WHERE 절에서 인덱스를 확인하십시오. 적절한 인덱스를 추가 한 후 SP를 반복적으로 호출하는 속도를 크게 높였습니다.
Matthew

1
장시간 실행으로 인한 잠재적 인 잠금 문제를 피하기 위해 임시 테이블을 사용하도록 상기시켜 주셔서 감사합니다.
Tony

때로는 저장 프로 시저가 버그를 유발할 위험없이 인라인하기에 너무 크거나 복잡 할 수 있습니다. 성능이 최우선 순위가 아닌 경우 커서 루프에서 SP를 실행하는 것이 종종 가장 실용적인 선택입니다.
Suncat2000

55

루프가 필요한 경우 메소드를 변경하십시오!

상위 스토어드 프로 시저에서 처리해야하는 데이터가 포함 된 #temp 테이블을 작성하십시오. 자식 저장 프로 시저를 호출하면 #temp 테이블이 표시되고 커서 또는 루프없이 전체 데이터 세트를 사용하여 처리 할 수 ​​있습니다.

이것은 실제로이 하위 저장 프로 시저가 수행하는 작업에 따라 다릅니다. UPDATE-ing하는 경우 #temp 테이블에서 "update from"조인을 수행하고 루프없이 하나의 명령문으로 모든 작업을 수행 할 수 있습니다. INSERT 및 DELETE에 대해서도 동일하게 수행 할 수 있습니다. IF로 여러 번 업데이트해야하는 경우 UPDATE FROM#temp 테이블을 사용 하여 여러 번 업데이트하고 CASE 문 또는 WHERE 조건을 사용할 수 있습니다.

데이터베이스에서 작업 할 때 루핑의 사고 방식을 잃어 버리려고하면 실제 성능이 저하되고 잠금 / 차단이 발생하고 처리 속도가 느려집니다. 어디에서나 반복하면 시스템이 제대로 확장되지 않으며 사용자가 느린 새로 고침에 대해 불평하기 시작할 때 속도를 높이기가 매우 어렵습니다.

이 절차의 내용을 루프에 호출하려면 10 번 중 9 번을 베팅하고 일련의 행에서 작동하도록 작성할 수 있습니다.


3
자식 sproc을 제어한다고 가정하면 매우 좋은 해결 방법으로 +1
Steven A. Lowe

약간의 생각으로,이 해결책은 훨씬 우수합니다!
encc

7
세트 기반 작업이 항상 선호됩니다. 그러나 SP를 수정하는 것이 항상 옵션은 아니라는 점에 유의하십시오. 공급 업체가 솔루션을 제공했다고 생각하십시오. 커서 나 루프 옵션 만 남겨두고 일부 사용자에게 가시성이 없을 수도 있습니다. 내 상점에서 우리 개발자들은 모든 것을 볼 수 있지만 트리거, 중첩 된 프로 시저, 조작되는 레코드 수 등으로 인해 솔루션이 공급 업체의 응용 프로그램 외부에 구축되어 있는지 확인해야 할 장애물이 많이 있습니다. 응용 프로그램의 복잡성은 단순히 레코드를 커서로 이동하는 것입니다.
Steve Mangiameli

11

이 대체와 같은 것이 테이블 및 필드 이름에 필요합니다.

Declare @TableUsers Table (User_ID, MyRowCount Int Identity(1,1)
Declare @i Int, @MaxI Int, @UserID nVarchar(50)

Insert into @TableUser
Select User_ID
From Users 
Where (My Criteria)
Select @MaxI = @@RowCount, @i = 1

While @i <= @MaxI
Begin
Select @UserID = UserID from @TableUsers Where MyRowCount = @i
Exec prMyStoredProc @UserID
Select

 @i = @i + 1, @UserID = null
End

2
while 루프는 커서보다 느리다
Steven A. Lowe

선언 커서 SQL 구문 또는 명령문은 지원되지 않습니다 (??)
MetaGuru

9

동적 쿼리로 수행 할 수 있습니다.

declare @cadena varchar(max) = ''
select @cadena = @cadena + 'exec spAPI ' + ltrim(id) + ';'
from sysobjects;
exec(@cadena);

6

저장 프로 시저가 수행하는 작업을 복제하기 위해 사용자 정의 함수를 사용하여이를 수행 할 수 없습니까?

SELECT udfMyFunction(user_id), someOtherField, etc FROM MyTable WHERE WhateverCondition

여기서 udfMyFunction은 사용자 ID를 가져 와서 필요한 모든 기능을 수행하는 함수입니다.

http://www.sqlteam.com/article/user-defined-functions를 참조하십시오 .조금 더 많은 배경을 를

가능한 경우 커서를 피해야한다는 데 동의합니다. 그리고 보통 가능합니다!

(물론, 내 대답은 SP에서 출력을 얻는 데 관심이 있고 실제 데이터를 변경하지 않을 것을 전제로합니다. 원래 질문과는 달리 "특정 방식으로 사용자 데이터 변경"을 발견했습니다. 가능한 해결책으로 이것을 제공하겠다고 생각했습니다. 당신의 행동에 달려 있습니다.)


1
OP : "특정 방법으로 사용자 데이터를 변경하는 저장 프로 시저" MSDN : 사용자 정의 함수를 사용하여 데이터베이스 상태를 수정하는 작업을 수행 할 수 없습니다. 그러나 SQLSVR 2014에는 문제가없는 것 같습니다.
Johnny 5

6

테이블 변수 또는 임시 테이블을 사용하십시오.

앞에서 언급했듯이 커서는 최후의 수단입니다. 주로 많은 리소스를 사용하기 때문에 잠금 문제가 발생하며 SQL을 올바르게 사용하는 방법을 이해하지 못하고 있다는 신호일 수 있습니다.

참고 사항 : 커서를 사용하여 테이블의 행을 업데이트하는 솔루션을 발견했습니다. 약간의 조사 끝에 모든 것이 단일 UPDATE 명령으로 대체 될 수 있음이 밝혀졌습니다. 그러나이 경우 저장 프로 시저를 실행해야 할 경우 단일 SQL 명령이 작동하지 않습니다.

다음과 같이 테이블 변수를 작성하십시오 (많은 데이터로 작업 중이거나 메모리가 부족한 경우 임시 테이블을 대신 사용하십시오).

DECLARE @menus AS TABLE (
    id INT IDENTITY(1,1),
    parent NVARCHAR(128),
    child NVARCHAR(128));

그만큼 id 중요하다.

교체 parent하고child 좋은 데이터, 예를 들어 관련 식별자 또는 데이터의 전체 집합에 작동합니다.

테이블에 데이터를 삽입하십시오. 예 :

INSERT INTO @menus (parent, child) 
  VALUES ('Some name',  'Child name');
...
INSERT INTO @menus (parent,child) 
  VALUES ('Some other name', 'Some other child name');

일부 변수를 선언하십시오.

DECLARE @id INT = 1;
DECLARE @parentName NVARCHAR(128);
DECLARE @childName NVARCHAR(128);

마지막으로 테이블의 데이터에 while 루프를 만듭니다.

WHILE @id IS NOT NULL
BEGIN
    SELECT @parentName = parent,
           @childName = child 
        FROM @menus WHERE id = @id;

    EXEC myProcedure @parent=@parentName, @child=@childName;

    SELECT @id = MIN(id) FROM @menus WHERE id > @id;
END

첫 번째 선택은 임시 테이블에서 데이터를 가져옵니다. 두 번째 선택은 @id를 업데이트합니다.MIN선택된 행이 없으면 null을 반환합니다.

다른 방법은 테이블에 행이있는 동안 루프 SELECT TOP 1하고 임시 테이블에서 선택한 행을 제거하는 것입니다.

WHILE EXISTS(SELECT 1 FROM @menuIDs) 
BEGIN
    SELECT TOP 1 @menuID = menuID FROM @menuIDs;

    EXEC myProcedure @menuID=@menuID;

    DELETE FROM @menuIDs WHERE menuID = @menuID;
END;

3

Dave Rincon의 동적 쿼리 방식은 커서를 사용하지 않으며 작고 쉽습니다. 공유해 주셔서 감사합니다.

그러나 Azure SQL에 대한 요구와 쿼리에 "고유성"이 필요한 경우 다음과 같이 코드를 수정해야했습니다.

Declare @SQL nvarchar(max);
-- Set SQL Variable
-- Prepare exec command for each distinctive tenantid found in Machines 
SELECT @SQL = (Select distinct 'exec dbo.sp_S2_Laser_to_cache ' + 
              convert(varchar(8),tenantid) + ';' 
              from Dim_Machine
              where iscurrent = 1
              FOR XML PATH(''))

--for debugging print the sql 
print @SQL;

--execute the generated sql script
exec sp_executesql @SQL;

나는 이것이 누군가를 돕기를 바랍니다 ...

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