มีวิธีทำให้ตัวแปร 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 Bennett

1
@JonathanDickinson เพื่อความชัดเจนคำแนะนำของคุณคือการใช้WITH SCHEMABINDINGในCREATE FUNCTIONคำสั่ง (ตรงข้ามกับขั้นตอนการจัดเก็บที่อาจเรียกใช้ฟังก์ชัน) - ถูกต้องหรือไม่?
Holistic Developer

1
ใช่ในฟังก์ชัน กับ SCHEMABINDING ช่วยให้ SQL เพื่ออินไลน์ "ฟังก์ชั่นตารางมูลค่า inlined" - ดังนั้นจึงยังต้องอยู่ในแบบฟอร์มนี้: gist.github.com/jcdickinson/61a38dedb84b35251da301b128535ceb ตัววิเคราะห์คำค้นหาจะไม่แทรกในบรรทัดใด ๆ หากไม่มีการกำหนดตารางเวลาหรืออะไรก็ตามด้วย BEGIN
Jonathan Dickinson

ผลกระทบของการใช้ UDF ที่ไม่ได้กำหนดปัจจัย: docs.microsoft.com/es-es/archive/blogs/sqlprogrammability/…
Ochoto

28

ทางออกหนึ่งที่นำเสนอโดย Jared Ko คือการใช้หลอกคงที่

ตามที่อธิบายไว้ในSQL Server: Variables, Parameters or Literals? หรือ…ค่าคงที่? :

Pseudo-Constants ไม่ใช่ตัวแปรหรือพารามิเตอร์ แต่เป็นเพียงมุมมองที่มีแถวเดียวและมีคอลัมน์เพียงพอที่จะรองรับค่าคงที่ของคุณ ด้วยกฎง่ายๆเหล่านี้ SQL Engine จะละเว้นค่าของมุมมองโดยสิ้นเชิง แต่ยังคงสร้างแผนการดำเนินการตามค่าของมัน แผนการดำเนินการไม่ได้แสดงการเข้าร่วมในมุมมอง!

สร้างแบบนี้:

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

อย่างไรก็ตามการเพิ่มด้วยการกำหนดตารางเวลาในฟังก์ชันสเกลาร์ดูเหมือนจะช่วยเพิ่มประสิทธิภาพได้อย่างมาก
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

10

ไม่ แต่ควรใช้หลักการตั้งชื่อแบบเก่าที่ดี

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
บนแล็ปท็อป dev ที่มีฟังก์ชั่นเพิ่มเติมอีกสองฟังก์ชันโดยไม่มีการผูกสคีมา 5570 elapsed, using function | 406 elapsed, using local variable| 383 elapsed, using hard coded values| 3893 elapsed, using function without schemabinding
บ้านลิง

สำหรับการเปรียบเทียบคำสั่ง select อย่างง่ายใช้เวลา 4110ms โดยที่คำสั่ง select สลับกันระหว่างselect top 1 @m = cv_val from code_values where cv_id = 'C101' และเหมือนกัน... 'C201' โดยที่ code_values ​​เป็นตารางพจนานุกรมที่มี 250 vars มีทั้งหมดใน 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

สำหรับ enums หรือค่าคงที่ธรรมดามุมมองที่มีแถวเดียวจะมีประสิทธิภาพที่ยอดเยี่ยมและรวบรวมการตรวจสอบเวลา / การติดตามการอ้างอิง (ทำให้เป็นชื่อคอลัมน์)

ดูบล็อกโพสต์ของ 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

เห็นได้ชัดว่าสิ่งนี้ขึ้นอยู่กับสิ่งทั้งหมด (ค่าที่ไม่มีช่องว่างต่อท้ายและความคิดเห็น) เป็นเอกลักษณ์ สามารถเปลี่ยนได้ด้วยการค้นหาทั่วโลกและแทนที่


ปัญหาหนึ่งคือมีเฉพาะในพื้นที่เท่านั้น
Bernardo Dal Corno

0

ไม่มีสิ่งที่เรียกว่า "การสร้างค่าคงที่" ในเอกสารฐานข้อมูล ค่าคงที่มีอยู่และมักเรียกว่าค่า เราสามารถประกาศตัวแปรและกำหนดค่า (ค่าคงที่) ให้กับมันได้ จากมุมมองของนักวิชาการ:

DECLARE @two INT
SET @two = 2

ที่นี่ @two เป็นตัวแปรและ 2 คือค่า / ค่าคงที่


ลองใช้คำตอบของ Michal D. และ John Nilsson เพื่อเพิ่มประสิทธิภาพเช่นกัน
WonderWorker

ตัวอักษรมีค่าคงที่ตามคำจำกัดความ อักขระ ascii / unicode (ขึ้นอยู่กับตัวแก้ไข) 2จะถูกแปลเป็นค่าไบนารีเมื่อกำหนดที่ "เวลาคอมไพล์" ค่าจริงที่เข้ารหัสขึ้นอยู่กับชนิดข้อมูลที่กำหนดให้ (int, char, ... )
samis

-1

คำตอบที่ดีที่สุดคือจาก SQLMenace ตามข้อกำหนดหากนั่นคือการสร้างค่าคงที่ชั่วคราวสำหรับใช้ภายในสคริปต์เช่นในคำสั่ง GO หลายคำสั่ง / แบทช์

เพียงสร้างโพรซีเดอร์ใน 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.