[sql-server] 오류 : “INSERT EXEC 문은 중첩 될 수 없습니다.” 및 “INSERT-EXEC 문 내에서 ROLLBACK 문을 사용할 수 없습니다.” 이것을 해결하는 방법?

나는 세 가지 저장 프로 시저를 가지고 Sp1, Sp2하고 Sp3.

첫 번째 항목 ( Sp1)은 두 번째 항목 ( )을 실행하고 Sp2반환 된 데이터를에 저장 @tempTB1하고 두 번째 항목은 세 번째 항목 ( Sp3)을 실행하고 데이터를에 저장합니다 @tempTB2.

실행 Sp2하면 작동하고에서 내 모든 데이터를 반환 Sp3하지만 문제는에 있습니다. Sp1실행하면 다음 오류가 표시됩니다.

INSERT EXEC 문은 중첩 될 수 없습니다.

장소를 변경하려고했는데 execute Sp2또 다른 오류가 표시됩니다.

INSERT-EXEC 문 내에서 ROLLBACK 문을 사용할 수 없습니다.



답변

이것은 저장 프로 시저 체인에서 데이터를 ‘버블 링’하려고 할 때 흔히 발생하는 문제입니다. SQL Server의 제한 사항은 한 번에 하나의 INSERT-EXEC 만 활성화 할 수 있다는 것입니다. 이러한 유형의 문제를 해결하기 위해 패턴에 대한 매우 철저한 문서 인 저장 프로 시저간에 데이터를 공유하는 방법을 살펴 보는 것이 좋습니다 .

예를 들어 해결 방법은 Sp3를 테이블 반환 함수로 바꾸는 것입니다.


답변

이것은 거대한 복잡한 생성 함수 나 실행 된 SQL 문자열 호출없이 SQL Server에서이 작업을 수행하는 유일한 “간단한”방법입니다. 둘 다 끔찍한 솔루션입니다.

  1. 임시 테이블 생성
  2. 저장 프로 시저 데이터를 저장하십시오.

예:

INSERT INTO #YOUR_TEMP_TABLE
SELECT * FROM OPENROWSET ('SQLOLEDB','Server=(local);TRUSTED_CONNECTION=YES;','set fmtonly off EXEC [ServerName].dbo.[StoredProcedureName] 1,2,3')

참고 : 반드시 ‘set fmtonly off’를 사용해야하며, 저장 프로 시저 매개 변수가 포함 된 문자열 또는 테이블 이름에 대해 openrowset 호출 내에서 동적 SQL을 추가 할 수 없습니다. 그렇기 때문에 대부분의 경우 임시 테이블을 수행하므로 테이블 변수 대신 임시 테이블을 사용해야합니다.


답변

좋습니다. jimhark의 권장 사항은 이전 단일 해시 테이블 접근 방식의 예입니다.-

CREATE PROCEDURE SP3 as

BEGIN

    SELECT 1, 'Data1'
    UNION ALL
    SELECT 2, 'Data2'

END
go


CREATE PROCEDURE SP2 as

BEGIN

    if exists (select  * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
        INSERT INTO #tmp1
        EXEC SP3
    else
        EXEC SP3

END
go

CREATE PROCEDURE SP1 as

BEGIN

    EXEC SP2

END
GO


/*
--I want some data back from SP3

-- Just run the SP1

EXEC SP1
*/


/*
--I want some data back from SP3 into a table to do something useful
--Try run this - get an error - can't nest Execs

if exists (select  * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
    DROP TABLE #tmp1

CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20))

INSERT INTO #tmp1
EXEC SP1


*/

/*
--I want some data back from SP3 into a table to do something useful
--However, if we run this single hash temp table it is in scope anyway so
--no need for the exec insert

if exists (select  * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
    DROP TABLE #tmp1

CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20))

EXEC SP1

SELECT * FROM #tmp1

*/


답변

이 문제에 대한 나의 해결 방법은 항상 단일 해시 임시 테이블이 호출 된 모든 procs의 범위 내에 있다는 원칙을 사용하는 것입니다. 따라서 proc 매개 변수에 옵션 스위치가 있습니다 (기본값은 off로 설정 됨). 이것이 켜져 있으면 호출 된 proc은 호출하는 proc에서 생성 된 임시 테이블에 결과를 삽입합니다. 과거에는 한 단계 더 나아가 호출 된 proc에 코드를 넣어 단일 해시 테이블이 범위에 있는지 확인하고 코드를 삽입하고 그렇지 않으면 결과 집합을 반환한다고 생각합니다. 잘 작동하는 것 같습니다-procs간에 대용량 데이터 세트를 전달하는 가장 좋은 방법입니다.


답변

이 트릭은 저에게 효과적입니다.

원격 서버에서는이 문제가 없습니다. 원격 서버에서는 마지막 삽입 명령이 이전 명령의 결과가 실행되기를 기다리기 때문입니다. 동일한 서버에서는 그렇지 않습니다.

해결 방법을 위해 해당 상황을 활용하십시오.

연결된 서버를 생성 할 수있는 권한이 있다면 그렇게하십시오. 연결된 서버와 동일한 서버를 만듭니다.

  • SSMS에서 서버에 로그인하십시오.
  • “서버 개체
  • “연결된 서버”를 마우스 오른쪽 버튼으로 클릭 한 다음 “새 연결 서버”를 클릭합니다.
  • 대화 상자에서 연결된 서버의 이름을 지정하십시오. 예 : THISSERVER
  • 서버 유형은 “기타 데이터 소스”입니다.
  • 공급자 : SQL 서버용 Microsoft OLE DB 공급자
  • 데이터 소스 : IP, 점 (.) 일 수도 있습니다. 로컬 호스트이기 때문입니다.
  • “보안”탭으로 이동하여 세 번째 “로그인의 현재 보안 컨텍스트를 사용하여 작성”을 선택하십시오.
  • 원하는 경우 서버 옵션 (세 번째 탭)을 편집 할 수 있습니다.
  • 확인을 누르면 연결된 서버가 생성됩니다.

이제 SP1의 Sql 명령은

insert into @myTempTable
exec THISSERVER.MY_DATABASE_NAME.MY_SCHEMA.SP2

저를 믿으십시오. SP2에 동적 삽입이 있어도 작동합니다.


답변

해결 방법은 제품 중 하나를 테이블 값 함수로 변환하는 것입니다. 나는 그것이 항상 가능한 것은 아니라는 것을 깨닫고 그 자체의 한계를 도입합니다. 그러나 나는 항상 적어도 하나의 절차가 이에 적합한 후보임을 찾을 수있었습니다. 저는이 솔루션을 좋아합니다. 솔루션에 “핵”을 도입하지 않기 때문입니다.


답변

Stored Proc의 결과를 임시 테이블로 가져 오려고 할 때이 문제가 발생했으며 해당 Stored Proc가 자체 작업의 일부로 임시 테이블에 삽입되었습니다. 문제는 SQL Server가 동일한 프로세스가 동시에 두 개의 다른 임시 테이블에 쓰는 것을 허용하지 않는다는 것입니다.

허용되는 OPENROWSET 답변은 제대로 작동하지만 프로세스에서 동적 SQL 또는 외부 OLE 공급자를 사용하지 않아야했기 때문에 다른 경로를 사용했습니다.

내가 찾은 쉬운 해결 방법 중 하나는 저장 프로 시저의 임시 테이블을 테이블 변수로 변경하는 것입니다. 임시 테이블과 똑같이 작동하지만 더 이상 다른 임시 테이블 삽입과 충돌하지 않습니다.

코멘트를 끝내기 위해 저는 여러분 중 일부가 성능 킬러로서 테이블 변수를 경고하면서 저에게 경고를 작성하려고한다는 것을 알고 있습니다. 제가 말할 수있는 것은 2020 년 에 테이블 변수를 두려워 하지 않도록 배당금을 지불 한다는 것입니다. 이것이 2008 년이고 내 데이터베이스가 16GB RAM과 5400RPM HDD로 실행되는 서버에서 호스팅 되었다면 동의 할 수 있습니다. 하지만 2020 년이고 SSD 어레이를 기본 스토리지로 사용하고 수백 기가 RAM을 사용합니다. 회사 전체의 데이터베이스를 테이블 변수에로드 할 수 있지만 여전히 충분한 RAM을 확보 할 수 있습니다.

테이블 변수가 메뉴로 돌아 왔습니다!