TSQL 변수를 상수로 만드는 방법이 있습니까?


답변:


60

아니요,하지만 함수를 생성하고 거기에 하드 코딩하여 사용할 수 있습니다.

다음은 그 예입니다.

CREATE FUNCTION fnConstant()
RETURNS INT
AS
BEGIN
    RETURN 2
END
GO

SELECT dbo.fnConstant()

13
WITH SCHEMABINDING 이것을 '실제'상수로 바꿔야합니다 (UDF가 SQL에서 결정적으로 보이도록하기위한 요구 사항). 즉, 캐시 된 상태 여야합니다. 그래도 +1.
Jonathan Dickinson

이 대답은 좋습니다. sqlserver의 테이블 열이 함수를 기본값으로 참조 할 수 있습니다. 나는 일이를 가져올 수 없습니다
Ab의 베넷에게

1
@JonathanDickinson 명확하게 말하자면, 당신의 제안은 (함수를 호출 할 수있는 저장 프로 시저와는 반대로) 문 WITH SCHEMABINDING에서 사용 CREATE FUNCTION하는 것입니다. 맞습니까?
Holistic Developer

1
예, 기능에서. 이 때문에 - WITH SCHEMABINDING는 SQL은 "인라인 테이블 반환 함수"인라인 할 수 :이 양식에 있어야 gist.github.com/jcdickinson/61a38dedb84b35251da301b128535ceb을 . 쿼리 분석기는 SCHEMABINDING 또는 BEGIN 없이는 인라인하지 않습니다.
Jonathan Dickinson


28

Jared Ko가 제공하는 한 가지 해결책은 pseudo-constants 를 사용하는 것 입니다.

SQL Server에 설명 된대로 : 변수, 매개 변수 또는 리터럴? 아니면 ... 상수? :

의사 상수는 변수 나 매개 변수가 아닙니다. 대신 단순히 하나의 행과 상수를 지원하기에 충분한 열이있는보기입니다. 이러한 간단한 규칙을 사용하면 SQL 엔진은 뷰의 값을 완전히 무시하지만 여전히 해당 값에 따라 실행 계획을 작성합니다. 실행 계획은 뷰에 대한 조인도 표시하지 않습니다!

다음과 같이 작성하십시오.

CREATE SCHEMA ShipMethod
GO
-- Each view can only have one row.
-- Create one column for each desired constant.
-- Each column is restricted to a single value.
CREATE VIEW ShipMethod.ShipMethodID AS
SELECT CAST(1 AS INT) AS [XRQ - TRUCK GROUND]
      ,CAST(2 AS INT) AS [ZY - EXPRESS]
      ,CAST(3 AS INT) AS [OVERSEAS - DELUXE]
      ,CAST(4 AS INT) AS [OVERNIGHT J-FAST]
      ,CAST(5 AS INT) AS [CARGO TRANSPORT 5]

그런 다음 다음과 같이 사용하십시오.

SELECT h.*
FROM Sales.SalesOrderHeader h
JOIN ShipMethod.ShipMethodID const
    ON h.ShipMethodID = const.[OVERNIGHT J-FAST]

또는 다음과 같이 :

SELECT h.*
FROM Sales.SalesOrderHeader h
WHERE h.ShipMethodID = (SELECT TOP 1 [OVERNIGHT J-FAST] FROM ShipMethod.ShipMethodID)

1
이것은 허용되는 답변보다 훨씬 더 나은 솔루션입니다. 처음에 우리는 스칼라 함수 경로로 내려 갔고 성능이 끔찍했습니다. 이 답변과 Jared Ko의 기사에 대한 위의 링크가 훨씬 좋습니다.
David Coster

그러나 스칼라 함수에 WITH SCHEMABINDING을 추가하면 성능이 크게 향상되는 것으로 보입니다.
David Coster

이제 링크가 끊어졌습니다.
Matthieu Cormier

1
@MatthieuCormier : MSDN이 어쨌든 이전 URL에서 새 URL로 리디렉션을 추가 한 것 같지만 링크를 업데이트했습니다.
Ilmari Karonen

23

상수 누락에 대한 내 해결 방법은 옵티 마이저에 값에 대한 힌트를 제공하는 것입니다.

DECLARE @Constant INT = 123;

SELECT * 
FROM [some_relation] 
WHERE [some_attribute] = @Constant
OPTION( OPTIMIZE FOR (@Constant = 123))

이것은 실행 계획을 생성 할 때 변수를 상수 인 것처럼 처리하도록 쿼리 컴파일러에 지시합니다. 단점은 값을 두 번 정의해야한다는 것입니다.


3
그것은 도움이되지만 단일 정의의 목적을 무너 뜨립니다.
MikeJRamsey56

9

아니요,하지만 좋은 오래된 명명 규칙을 사용해야합니다.

declare @MY_VALUE as int

@VictorYarema 때로는 컨벤션이 필요한 모든 것이기 때문입니다. 때로는 다른 좋은 선택이 없기 때문입니다. 이제는 제쳐두고 SQLMenace의 답변이 더 좋아 보입니다. 동의합니다. 그럼에도 불구하고 함수 이름은 상수에 대한 규칙 인 IMO를 따라야합니다. 이름은이어야합니다 FN_CONSTANT(). 그렇게하면 그것이 무엇을하고 있는지 분명합니다.
tfrascaroli

이것만으로는 성능상의 이점을 원할 때 도움이되지 않습니다. 성능 향상에 대한 Michal D.와 John Nilsson의 답변도 시도해보십시오.
WonderWorker

8

T-SQL에는 상수에 대한 기본 제공 지원이 없습니다. SQLMenace의 접근 방식을 사용하여 시뮬레이션하거나 (다른 사람이 다른 것을 반환하기 위해 함수를 덮어 썼는지 여부를 확신 할 수는 없지만…) 여기에 제안 된대로 상수를 포함하는 테이블을 작성할 수 있습니다 . ConstantValue열에 대한 변경 사항을 롤백하는 트리거를 작성할 수 있습니까?


7

SQL 함수를 사용하기 전에 다음 스크립트를 실행하여 성능 차이를 확인하십시오.

IF OBJECT_ID('fnFalse') IS NOT NULL
DROP FUNCTION fnFalse
GO

IF OBJECT_ID('fnTrue') IS NOT NULL
DROP FUNCTION fnTrue
GO

CREATE FUNCTION fnTrue() RETURNS INT WITH SCHEMABINDING
AS
BEGIN
RETURN 1
END
GO

CREATE FUNCTION fnFalse() RETURNS INT WITH SCHEMABINDING
AS
BEGIN
RETURN ~ dbo.fnTrue()
END
GO

DECLARE @TimeStart DATETIME = GETDATE()
DECLARE @Count INT = 100000
WHILE @Count > 0 BEGIN
SET @Count -= 1

DECLARE @Value BIT
SELECT @Value = dbo.fnTrue()
IF @Value = 1
    SELECT @Value = dbo.fnFalse()
END
DECLARE @TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, @TimeStart, @TimeEnd) AS VARCHAR) + ' elapsed, using function'
GO

DECLARE @TimeStart DATETIME = GETDATE()
DECLARE @Count INT = 100000
DECLARE @FALSE AS BIT = 0
DECLARE @TRUE AS BIT = ~ @FALSE

WHILE @Count > 0 BEGIN
SET @Count -= 1

DECLARE @Value BIT
SELECT @Value = @TRUE
IF @Value = 1
    SELECT @Value = @FALSE
END
DECLARE @TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, @TimeStart, @TimeEnd) AS VARCHAR) + ' elapsed, using local variable'
GO

DECLARE @TimeStart DATETIME = GETDATE()
DECLARE @Count INT = 100000

WHILE @Count > 0 BEGIN
SET @Count -= 1

DECLARE @Value BIT
SELECT @Value = 1
IF @Value = 1
    SELECT @Value = 0
END
DECLARE @TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, @TimeStart, @TimeEnd) AS VARCHAR) + ' elapsed, using hard coded values'
GO

4
이것은 꽤 오래되었지만 참고로 내 서버에서 실행했을 때의 결과는 다음과 같습니다. | 2760ms elapsed, using function| 2300ms elapsed, using local variable| 2286ms elapsed, using hard coded values|
z00l

2
스키마 바인딩이없는 두 개의 추가 기능이있는 개발 랩톱에서. 5570 elapsed, using function | 406 elapsed, using local variable| 383 elapsed, using hard coded values| 3893 elapsed, using function without schemabinding
monkeyhouse

비교를 위해 간단한 select 문은 4110ms가 걸렸습니다. select 문이 번갈아 가며 code_values가 250 개의 변수가있는 사전 테이블 인 경우 select top 1 @m = cv_val from code_values where cv_id = 'C101' 동일 ... 'C201' 합니다. SQL-Server 2016에 모두있었습니다
monkeyhouse

6

변수 값에 대한 최적의 실행 계획을 얻으려면 동적 SQL 코드를 사용할 수 있습니다. 변수를 일정하게 만듭니다.

DECLARE @var varchar(100) = 'some text'
DECLARE @sql varchar(MAX)
SET @sql = 'SELECT * FROM table WHERE col = '''+@var+''''
EXEC (@sql)

1
이것이 내가하는 방법이며 상수를 포함하는 쿼리의 성능을 크게 향상시킵니다.
WonderWorker

5

열거 형 또는 단순 상수의 경우 단일 행이있는 뷰는 성능이 뛰어나고 컴파일 시간 검사 / 종속성 추적 (열 이름이 발생 함)이 있습니다.

Jared Ko의 블로그 게시물 https://blogs.msdn.microsoft.com/sql_server_appendix_z/2013/09/16/sql-server-variables-parameters-or-literals-or-constants/ 참조

보기 만들기

 CREATE VIEW ShipMethods AS
 SELECT CAST(1 AS INT) AS [XRQ - TRUCK GROUND]
   ,CAST(2 AS INT) AS [ZY - EXPRESS]
   ,CAST(3 AS INT) AS [OVERSEAS - DELUXE]
  , CAST(4 AS INT) AS [OVERNIGHT J-FAST]
   ,CAST(5 AS INT) AS [CARGO TRANSPORT 5]

보기 사용

SELECT h.*
FROM Sales.SalesOrderHeader 
WHERE ShipMethodID = ( select [OVERNIGHT J-FAST] from ShipMethods  )

3

좋아, 보자

상수는 컴파일 시간에 알려지고 프로그램 수명 동안 변경되지 않는 불변 값입니다.

즉, SQL Server에서 상수를 가질 수 없습니다.

declare @myvalue as int
set @myvalue = 5
set @myvalue = 10--oops we just changed it

방금 변경된 값


1

상수를 지원하는 빌드가 없기 때문에 내 솔루션은 매우 간단합니다.

이것은 지원되지 않기 때문에 :

Declare Constant @supplement int = 240
SELECT price + @supplement
FROM   what_does_it_cost

나는 단순히 그것을

SELECT price + 240/*CONSTANT:supplement*/
FROM   what_does_it_cost

분명히 이것은 모든 것 (후행 공백과 주석이없는 값)이 고유하다는 것에 의존합니다. 전역 검색 및 바꾸기로 변경할 수 있습니다.


한 가지 문제는 로컬에서만 사용할 수 있다는 것입니다
베르나르도 달 Corno

0

데이터베이스 문헌에는 "상수 생성"과 같은 것이 없습니다. 상수는있는 그대로 존재하며 종종 값이라고합니다. 변수를 선언하고 여기에 값 (상수)을 할당 할 수 있습니다. 학문적 관점에서 :

DECLARE @two INT
SET @two = 2

여기서 @two는 변수이고 2는 값 / 상수입니다.


성능 향상을 위해 Michal D.와 John Nilsson의 답변도 시도해보십시오.
WonderWorker

리터럴은 정의상 일정합니다. ASCII / 유니 코드 (편집기에 따라 다름) 문자 2는 "컴파일 시간"에 할당 될 때 이진 값으로 변환됩니다. 인코딩 된 실제 값은 할당되는 데이터 유형 (int, char, ...)에 따라 다릅니다.
samis

-1

가장 좋은 대답은 스크립트 내에서, 즉 여러 GO 문 / 배치에서 사용할 임시 상수를 만드는 경우 요구 사항에 따라 SQLMenace에서 오는 것입니다.

tempdb에서 프로 시저를 생성하기 만하면 대상 데이터베이스에 영향을주지 않습니다.

이에 대한 실용적인 예는 논리적 스키마 버전을 포함하는 스크립트 끝에 제어 값을 쓰는 데이터베이스 생성 스크립트입니다. 파일 상단에는 변경 내역 등이 포함 된 주석이 있습니다. 그러나 실제로 대부분의 개발자는 아래로 스크롤하여 파일 하단의 스키마 버전을 업데이트하는 것을 잊을 것입니다.

위의 코드를 사용하면 데이터베이스 스크립트 (SSMS의 스크립트 생성 기능에서 복사)가 데이터베이스를 생성하지만 마지막에 사용되기 전에 맨 위에 보이는 스키마 버전 상수를 정의 할 수 있습니다. 이는 변경 내역 및 기타 댓글 옆에있는 개발자의 얼굴에 있기 때문에 업데이트 할 가능성이 매우 높습니다.

예를 들면 :

use tempdb
go
create function dbo.MySchemaVersion()
returns int
as
begin
    return 123
end
go

use master
go

-- Big long database create script with multiple batches...
print 'Creating database schema version ' + CAST(tempdb.dbo.MySchemaVersion() as NVARCHAR) + '...'
go
-- ...
go
-- ...
go
use MyDatabase
go

-- Update schema version with constant at end (not normally possible as GO puts
-- local @variables out of scope)
insert MyConfigTable values ('SchemaVersion', tempdb.dbo.MySchemaVersion())
go

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