내가 상상하는 것처럼 T-SQL에서 변수를 사용할 수없는 이유는 무엇입니까?


18

용서하십시오, 저는 SQL의 세계로 옮긴 개발자입니다. 변수를 추가하여 SQL을 향상시킬 수 있다고 생각했지만 예상대로 작동하지 않았습니다. 왜 이것이 작동하지 않는지 말해 줄 수 있습니까? 나는 해결책을 원하지 않고, 나는 그것이 좋은 이유가 있다고 확신 할 때 상상 해야하는 것처럼 작동하지 않는 이유를 알고 싶다. 그러나 현재 나에게 튀어 오르지 않는다.

DECLARE @DatabaseName varchar(150)
SET @DatabaseName = 'MyAmazingDatabaseName'

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO

이를 위해서는 동적 SQL을 사용해야합니다. mssqltips.com/sqlservertip/1160/…
Stijn Wynants가

3
의견을 보내 주셔서 감사합니다.하지만 내 질문에 따르면 이것은 내가 찾고있는 대답이 아닙니다. 내가 보여준 것처럼 왜 내가 할 수 없는지 아는 사람이 있는지 알고 싶습니다.
gareth

2
USE매개 변수와 함께 명령 을 사용할 수 없기 때문입니다 .
TT.

2
이미 주어진 답변 외에 자체 답변에는 충분하지 않은 변수는 현재 배치의 최대 범위에 속합니다. (SQL Server보다 변수 범위를 더 좁힐 수 있는지 여부는 알 수 없습니다.) 따라서 GO이전에 선언 된 변수가 사라집니다. 시나리오에 적용되거나 적용되지 않을 수있는 SQLCMD 변수를 조사 할 수 있습니다.
CVn

1
우리는 데이터베이스 이름과 열 이름을 문자열 값으로 생각하는 경향이 있지만 SQL의 맥락에서 이들은 식별자입니다. 당신이 시도하는 것은 x = 7을 기대하는 것과 같습니다. 'x'= 7과 동일해야합니다. 다른 언어로. x = 7과 동일한 'x'= 7을 처리하는 컴퓨터 언어를 만들 수있는 것처럼 Create Table X를 Create Table 'X'와 동일하게 처리하는 RDBMS를 만들 수 있습니다. 그러나 그것은 SQL이 아닙니다.
user1008646

답변:


20

변수에 대한 온라인 설명서 페이지

변수는 객체 이름이나 키워드 대신 표현식에서만 사용할 수 있습니다. 동적 SQL 문을 구성하려면 EXECUTE를 사용하십시오.

예를 들어 where 절에서 변수를 사용한 경우 예상했던대로 작동합니다. 이유에 관해서는 변수가 변수를 평가할 수 없어 파서와 관련이 있다고 생각합니다. 실행시 구문과 객체에 대해 쿼리를 먼저 구문 분석 한 다음 구문 분석에 성공하면 변수가 설정된 시점에서 쿼리가 실행됩니다.

DECLARE @name varchar(20);
SET @name = 'test';

CREATE TABLE [#tmp]([val] varchar(10));

insert into #tmp
values('test')

SELECT *
FROM [#tmp]
WHERE [val] = @name;

3
가능할 때마다 동적 SQL을 피해야합니다. evalJavaScript 및 Python과 같은 절차 적 언어에서 함수 를 사용 하는 것과 유사합니다. 보안 허점을 만드는 빠른 방법입니다.
jpmc26

1
@ jpmc26 : 동적 SQL을 포함하지 않는 더 안전한 방법은 무엇입니까?
Robert Harvey

1
@RobertHarvey 피하는 것이 가장 좋다고해서 반드시 동일한 기능을 가진 대안이 항상 있다는 것을 의미하지는 않습니다. ;) 종종 대답의 일부는 "문제에 대해 완전히 다른 해결책을 사용하십시오"입니다. 때로는 최선의 선택이지만, 많은 노력을 기울이지 않고 대안을 무시하지 않았는지 확인하지 않아도 건강에주의를 기울여야합니다.
jpmc26

2
@ jpmc26 : OP의 예는 "Code-First"ORM이 데이터베이스에서 테이블을 설정하기 위해 수행 할 수있는 것과 같습니다. 동적 SQL은 원칙적으로 안전하지 않지만 최종 사용자는 특정 코드를 건드리지 않습니다.
Robert Harvey

@RobertHarvey "최종 사용자"를 고려할 사람에 따라 다릅니다. DB를 배포하는 스크립트의 경우 개발자와 일부 sys 관리자가 "최종 사용자"라고 생각합니다. 나는 사고를 피하는 것 외에 다른 이유가 없다면 그 경우 안전하지 않은 입력을 거부하도록 시스템을 설계하려고합니다. 또한 "never touch"에 관해서는 OP가이 코드를
건드리고 있습니다

17

SQL 문에서 변수 사용에 대한 제한 사항은 SQL 아키텍처에서 발생합니다.

SQL 문 처리에는 세 단계가 있습니다.

  1. 준비-명령문을 구문 분석하고 실행 계획 을 컴파일하여 액세스 할 데이터베이스 오브젝트, 액세스 방법 및 관련 방법을 지정합니다. 실행 계획은 계획 캐시에 저장 됩니다 .
  2. 바인딩-명령문의 모든 변수가 실제 값으로 바뀝니다.
  3. 실행-캐시 된 계획이 바인딩 된 값으로 실행됩니다.

SQL Server는 프로그래머로부터 준비 단계를 숨기고 Oracle 및 DB2와 같은 기존의 데이터베이스보다 훨씬 빠르게 실행합니다. 성능상의 이유로 SQL이 잠재적으로 최적의 실행 계획을 결정하는 데 많은 시간을 소비하지만 재시작 후 명령문이 처음 발생할 때만 수행합니다.

따라서 정적 SQL 에서는 변수가 실행 계획을 무효화하지 않는 위치에서만 사용할 수 있으므로 테이블 이름, 열 이름 (WHERE 조건의 열 이름 포함) 등이 아닙니다.

동적 SQL 은 제한 사항을 처리 할 수없는 경우에 존재하며 프로그래머는 실행하는 데 약간 더 오래 걸릴 것임을 알고 있습니다. 동적 SQL은 악성 코드 삽입에 취약 할 수 있으므로주의하십시오!


7

보시다시피 "이유"질문에는 역사적 근거와 언어에 대한 기본 가정을 포함하여 다른 종류의 답변이 필요합니다.

SQL MVP Erland Sommarskog의이 포괄적 인 기사는 메커니즘과 함께 몇 가지 이론적 근거를 제공하려고 시도합니다.

동적 SQL의 저주와 축복 :

쿼리 계획 캐싱

SQL Server에서 실행하는 모든 쿼리에는 쿼리 계획이 필요합니다. 쿼리를 처음 실행할 때 SQL Server는 쿼리 계획을 작성하거나 용어에 따라 쿼리를 컴파일합니다. SQL Server는 계획을 캐시에 저장하고 다음에 쿼리를 실행할 때 계획이 재사용됩니다.

이것이 (그리고 보안, 아래 참조) 아마도 가장 큰 이유 일 것입니다.

SQL은 쿼리가 일회성 작업이 아니라 계속 사용된다는 전제 하에서 작동합니다. 테이블 (또는 데이터베이스!)이 실제로 쿼리에 지정되어 있지 않으면 나중에 사용할 수 있도록 실행 계획을 생성하고 저장할 수 없습니다.

예, 우리가 실행하는 모든 쿼리가 재사용되는 것은 아니지만 이것이 SQL의 기본 운영 전제 이므로 "예외"는 예외적입니다.

Erland가 나열하는 몇 가지 다른 이유 ( 저장 프로 시저 사용의 장점을 명시 적으로 나열 하고 있지만 매개 변수화 된 (비 동적) 쿼리의 장점이기도합니다)

  • 권한 시스템 : SQL 엔진은 조작 할 테이블 (또는 데이터베이스)을 모르는 경우 쿼리를 실행할 권한이 있는지 여부를 예측할 수 없습니다. 동적 SQL을 사용하는 "권한 체인"은 문제가 많습니다.
  • 네트워크 트래픽 줄이기 : 저장된 proc의 이름과 몇 개의 매개 변수 값을 네트워크를 통해 전달하는 것은 긴 쿼리 문보다 짧습니다.
  • 캡슐화 로직 : 다른 프로그래밍 환경에서 로직을 캡슐화하는 이점에 익숙해야합니다.
  • 사용 된 내용 추적 : 열 정의를 변경해야하는 경우이를 호출하는 모든 코드를 어떻게 찾을 수 있습니까? 시스템 프로시 저는 SQL 데이터베이스 내에서 종속성을 찾기 위해 존재하지만 코드가 저장 프로 시저에있는 경우에만 존재합니다.
  • SQL 코드 작성 용이성 : 저장 프로 시저를 만들거나 수정할 때 구문 검사가 발생하므로 오류가 거의 발생하지 않습니다.
  • 버그 및 문제 해결 : DBA는 변화하는 동적 SQL보다 훨씬 쉽게 개별 저장 프로 시저의 성능을 추적하고 측정 할 수 있습니다.

다시, 이것들 각각은 내가 여기에 들어 가지 않을 백 개의 뉘앙스를 가지고 있습니다.


2

동적 SQL을 사용해야합니다

DECLARE @DatabaseName varchar(150) = 'dbamaint'
declare @sqltext nvarchar(max) = N''

set @sqltext = N'CREATE DATABASE '+quotename(@DatabaseName)+ ';'

print @sqltext 

-- once you are happy .. uncomment below
--exec sp_executesql @sqltext
set @sqltext = ''
set @sqltext = N'use '+quotename(@DatabaseName)+ ';'
print @sqltext 
-- once you are happy .. uncomment below
--exec sp_executesql @sqltext

아래는 print.의 출력입니다. 주석을 해제 exec sp_executesql @sqltext하면 명령문이 실제로 실행됩니다 ...

CREATE DATABASE [dbamaint];
use [dbamaint];

1
예, 감사합니다. 그러나 변수를 사용할 수없는 이유를 아는 사람이 있는지 알고 싶습니다.
gareth

T-SQL 파서는 구문 오류를 발생시킵니다. 파서가 인식하는 유효한 T-SQL이 아닙니다.
Kin Shah

고마워 킨, 나는 그것에 대한 좋은 이유가 있어야 확신합니다. 데이터베이스 이름에 '@'이 포함될 수 있고 다른 복잡한 이유가있을 수 있습니다.
gareth

1
예, @을 포함 할 수 있으며 주된 이유라고 생각합니다. msdn.microsoft.com/ko-kr/library/ms175874.aspx
Paweł Tajs 2016

1
@gazeranco 저를 믿으십시오. SQL Server를 사용하는 사람은 더 많은 명령이 상수 식별자 대신 변수를 허용하기를 바랍니다.
db2
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.