SqlCommand.Prepare () 사용의 의미와 이점은 무엇입니까?


14

SQL 쿼리 실행 전에 SqlCommand.Prepare () (MSDN 참조) 메서드가 광범위하게 사용되는 개발자 코드를 발견했습니다 . 그리고 이것의 이점이 무엇인지 궁금합니다.

견본:

command.Prepare();
command.ExecuteNonQuery();
//...
command.Parameters[0].Value = 20;
command.ExecuteNonQuery();

나는 조금 놀았고 추적했다. Prepare()메서드를 호출 한 후 Command를 실행 하면 Sql Server가 다음 문을 실행합니다.

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@id int,@desc text',N'INSERT INTO dbo.testtable (id) VALUES (@id)',@id=20'
select @p1

그런 다음 Parameter가 값을 가져오고 SqlCommand.ExecuteNonQuery()호출하면 Sql-Server에서 다음이 실행됩니다.

exec sp_execute 1,@id=20

나에게 이것은 명령문 Prepare()이 실행 되는 즉시 명령문이 컴파일되는 것처럼 보입니다 . 이것의 이점이 무엇인지 궁금합니다. 이것은 계획 캐시에 저장되어 원하는 매개 변수 값으로 최종 쿼리가 실행되는 즉시 재사용 할 수 있다는 의미입니까?

SqlParameters로 실행되는 SqlCommands가 항상 래핑된다는 것을 알았습니다 ( 또 다른 질문에 문서화했습니다 ).sp_executesql 프로 시저 호출. 따라서 Sql Server는 매개 변수 값에 관계없이 계획을 저장하고 재사용 할 수 있습니다.

이것과 관련하여 나는 그 prepare()방법이 쓸모 없거나 쓸모없는 것인지 또는 여기에 뭔가 빠졌 는지 궁금해 합니까?


나는 항상 SQL 주입을 방지하는 것과 관련이 있다고 생각했기 때문에 특정 유형의 특정 변수를 허용하도록 쿼리를 준비했습니다. 이런 식으로 변수를 전달하지 않으면 쿼리가 실패합니다
Mark Sinkinson


"예, 운전사, 이사를 준비하십시오." "무엇을 준비하고 있습니까?! 항상 준비하고 있습니다! 그냥 가십시오!"
모든 거래의 존

답변:


19

준비된 SQL 배치를 실행하는 것과 별도로 SQL 배치를 준비하는 것은 효과적으로 **실행 계획이 캐시되는 방식을 감안할 때 SQL Server에는 쓸모가 없습니다. 준비 단계 (파싱, 매개 변수 바인딩 및 컴파일)를 분리하고 실행하는 것은 캐싱이없는 경우에만 의미가 있습니다. 기존 계획을 재사용하여 구문 분석 및 컴파일에 소요되는 시간을 절약하는 것이 목적이며, 이는 내부 계획 캐시의 기능입니다. 그러나 모든 RDBMS가 이러한 수준의 캐싱을 수행하는 것은 아니며 (이러한 기능의 존재로 인해) 계획을 "캐싱"하도록 요청하고 캐시 ID를 저장 한 다음 재사용하는 것은 클라이언트 코드에 달려 있습니다. 그것. 이는 불필요한 클라이언트 코드 (및 프로그래머가 기억하고 올바르게 기억해야 함)에 대한 추가 부담입니다. 실제로 IDbCommand.Prepare () 메서드에 대한 MSDN 페이지는 다음 과 같습니다.

서버는 필요에 따라 재사용 계획을 자동으로 캐시합니다. 따라서 클라이언트 애플리케이션에서이 메소드를 직접 호출 할 필요가 없습니다.

SqlCommand.Prepare ()를 호출하는 내 테스트 쇼 (질문에 표시된 것과 일치) 가 "준비"전용 작업을 수행 하지 않는 경우 SQL 을 준비 하고 실행하는 sp_prepexec 를 호출합니다. ; 그것은 호출하지 않습니다 sp_prepare 구문 분석하고 컴파일 만 (및 매개 변수 값, 그들의 이름과 데이터 유형에 고려하지 않습니다). 따라서 즉시 실행을 수행하기 때문에 호출의 "사전 컴파일"이점이 없습니다 (목표는 실행을 지연시키는 것으로 가정). 그러나 이라고 전화하더라도 호출의 흥미로운 잠재적 이점이SqlCommand.PrepareSqlCommand.Prepare()sp_prepexec 대신 하는 이 sp_prepare있습니다. 참고 사항을 참조하십시오 (** 자세한 내용은 하단에 있습니다.

내 테스트에 따르면 SqlCommand.Prepare()호출 된 경우에도 (이 호출은 SQL Server에서 명령을 실행 하지 않음 ), ExecuteNonQuery또는 ExecuteReader다음에 나오는 것이 완전히 실행되며 ExecuteReader 인 경우 행을 반환합니다. 여전히 SQL Server 프로파일 러는 SqlCommand.Execute______()호출 후 첫 번째 호출 SqlCommand.Prepare()이 "Prepare SQL"이벤트로 .Execute___()등록 되고 후속 호출이 "Exec Prepared SQL"이벤트로 등록됨을 보여줍니다.


테스트 코드

PasteBin ( http://pastebin.com/Yc2Tfvup) 에 업로드되었습니다 . 이 코드는 SQL Server 프로파일 러 추적이 실행되는 동안 (또는 확장 이벤트 세션) 실행되는 .NET / C # 콘솔 앱을 만듭니다. 각 단계 후에 일시 정지되므로 특정 명령문이 어떤 영향을 미치는지 명확하게됩니다.


최신 정보

더 많은 정보를 찾았으며을 (를) 호출하지 않는 약간의 잠재적 이유가 SqlCommand.Prepare()있습니다. 테스트에서 주목 한 점은 해당 테스트 콘솔 앱을 실행하는 모든 사람에게 유의해야 할 사항입니다 sp_unprepare. 명시적인 호출은 없습니다 . 나는 파고 들었고 MSDN 포럼에서 다음 게시물을 발견했습니다.

SqlCommand-준비 또는 준비하지 않습니까?

허용되는 답변에는 많은 정보가 포함되어 있지만 두드러진 점은 다음과 같습니다.

  • ** 실제로 준비하는 것은 주로 쿼리 문자열을 유선으로 전송하는 데 걸리는 시간입니다.
  • 준비된 쿼리는 캐시 된 계획의 저장을 보장하지 않으며 서버에 도달하면 임시 쿼리보다 빠르지 않습니다.
  • RPC (CommandType.StoredProcedure)는 준비에서 아무것도 얻지 못합니다.
  • 서버에서 준비된 핸들은 기본적으로 실행할 TSQL을 포함하는 메모리 내 맵에 대한 인덱스입니다.
  • 맵은 연결별로 저장되며 교차 연결을 사용할 수 없습니다.
  • 클라이언트가 풀에서 연결을 재사용 할 때 전송 된 재설정은 맵을 지 웁니다.

또한 SQLCAT 팀에서 다음과 같은 게시물을 발견했습니다. 관련 게시물은 ODBC 관련 문제 일 수 있지만 SqlClient는 SqlConnection을 폐기하면 올바르게 정리됩니다. SQLCAT 게시물에는 연결 풀 지우기 등과 같이 원인을 증명하는 데 도움이되는 추가 테스트가 언급되어 있지 않으므로 말하기가 어렵습니다.

준비된 SQL 문을 조심하십시오


결론

을 고려하면:

  • 호출의 유일한 장점은 SqlCommand.Prepare()네트워크를 통해 쿼리 텍스트를 다시 제출할 필요가없고,
  • 연결 메모리 (예 : SQL Server 메모리)의 일부로 쿼리 텍스트 호출 sp_preparesp_prepexec저장

나는 추천 에 대해 호출 SqlCommand.Prepare()단점이 더 많은 서버 메모리를 복용하는 동안 유일한 잠재적 인 이점은 네트워크 패킷을 저장하기 때문이다. 대부분의 경우 소비되는 메모리의 양이 매우 적을 수 있지만 대부분의 DB 서버가 10 메가 비트 최소 (요즘 100 메가 비트 또는 기가비트) 연결을 통해 앱 서버에 직접 연결되므로 네트워크 대역폭은 거의 문제가되지 않습니다. 그리고 일부는 같은 상자에 있습니다 ;-). 그와 메모리는 거의 항상 네트워크 대역폭보다 더 적은 리소스입니다.

여러 데이터베이스에 연결할 수있는 소프트웨어를 작성하는 사람이라면 누구나 표준 인터페이스의 편리 성으로 인해이 비용 / 혜택 분석이 변경 될 수 있습니다. 그러나 그 경우에도 기본 객체를 제공한다는 점 에서 서로 다른 공급자 (따라서 호출 할 필요 가 없는 것과 같은 공급자 간)를 허용하는 "표준"DB 인터페이스를 추상화하는 것이 여전히 쉽다는 것을 믿어야합니다. Prepare()앱 코드에서 이미 수행하고있는 지향적 인 프로그래밍 ;-).


이 광범위한 답변에 감사드립니다. 나는 당신의 단계를 테스트했고 당신의 기대 / 결과로부터 두 가지 수차를 가졌습니다 : 4 단계에서 나는 추가적인 결과를 얻었습니다. 10 단계에서 2 단계보다 다른 plan_handle을 얻었습니다.
Magier

1
@ 마법사 4 단계에 대해 잘 모르겠습니다 ... 다른 SET 옵션이 있거나 어쩌면 둘 사이의 쿼리 텍스트가 다를 수 있습니까? 단일 문자 (보이거나 보이지 않는) 차이도 다른 계획이 될 것입니다. 이 웹 페이지에서 복사하여 붙여 넣는 것이 도움이되지 않을 수 있습니다. 따라서 작은 따옴표 사이의 쿼리 sp_prepare를에서 호출로 복사 sp_executesql하십시오. 10 단계의 경우, 어제 더 많은 테스트를 수행했으며에 대한 핸들이 다른 경우를 sp_prepare보았지만 여전히 동일하지는 않습니다 sp_executesql. 한 가지 더 테스트하고 답변을 업데이트하겠습니다.
Solomon Rutzky

1
@Magier 실제로, 이전에 실행 한 테스트에서 다루었으며 특히 MSDN 포럼 게시물에 비추어 SQL을 캐시한다고 말하는 계획을 실제로 재사용하는 것은 아닙니다. 나는 여전히 plan_handle을 어떻게 재사용 할 수 있는지 궁금하지만 여전히 할 수 sp_executesql없습니다. 그러나 계획에 상관없이 sp_prepare캐시에서 계획이 제거 된 경우 구문 분석 시간이 여전히 남아 있으므로 답변의 해당 부분을 제거 할 것입니다 (관심이 없지만 관련이 없음). 그 부분은 당신의 직접적인 질문을 다루지 않았기 때문에 어쨌든 흥미로운 부수적 인 내용이었습니다. 다른 모든 것은 여전히 ​​적용됩니다.
Solomon Rutzky

1
이것은 내가 찾고있는 종류의 설명이었습니다.
CSharpie

5

이것에 관해서는, 나는 prepare () 메소드가 쓸모 없거나 쓸모없는 것인지 또는 여기에 뭔가 빠졌는지 궁금합니다.

Prepare 메서드는 SqlCommand 세계의 가치가 제한적이라고 말하지만 완전히 쓸모는 없습니다. 한 가지 이점은 Prepare가 SqlCommand가 구현하는 IDbCommand 인터페이스의 일부라는 것입니다. 따라서 조건부 논리없이 다른 DBMS 제공자 (준비가 필요할 수 있음)에 대해 동일한 코드를 실행하여 Prepare를 호출해야하는지 여부를 판별 할 수 있습니다.

이 사용 사례 (AFAIK) 외에 SQL Server 용 .NET Provider를 사용하면 Prepare는 가치가 없습니다.

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