มีวิธีทำให้ตัวแปร TSQL คงที่หรือไม่?
มีวิธีทำให้ตัวแปร TSQL คงที่หรือไม่?
คำตอบ:
ไม่ได้ แต่คุณสามารถสร้างฟังก์ชันและฮาร์ดโค้ดในนั้นและใช้งานได้
นี่คือตัวอย่าง:
CREATE FUNCTION fnConstant()
RETURNS INT
AS
BEGIN
RETURN 2
END
GO
SELECT dbo.fnConstant()
WITH SCHEMABINDING
ในCREATE FUNCTION
คำสั่ง (ตรงข้ามกับขั้นตอนการจัดเก็บที่อาจเรียกใช้ฟังก์ชัน) - ถูกต้องหรือไม่?
ทางออกหนึ่งที่นำเสนอโดย 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)
วิธีแก้ปัญหาของฉันสำหรับค่าคงที่หายไปคือการให้คำแนะนำเกี่ยวกับค่าให้กับเครื่องมือเพิ่มประสิทธิภาพ
DECLARE @Constant INT = 123;
SELECT *
FROM [some_relation]
WHERE [some_attribute] = @Constant
OPTION( OPTIMIZE FOR (@Constant = 123))
สิ่งนี้บอกให้คอมไพลเลอร์เคียวรีปฏิบัติต่อตัวแปรราวกับว่าเป็นค่าคงที่เมื่อสร้างแผนการดำเนินการ ข้อเสียคือคุณต้องกำหนดค่าสองครั้ง
ไม่ แต่ควรใช้หลักการตั้งชื่อแบบเก่าที่ดี
declare @MY_VALUE as int
FN_CONSTANT()
มันควรจะตั้งชื่อ วิธีนี้ชัดเจนว่ากำลังทำอะไร
ก่อนที่จะใช้ฟังก์ชัน 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
2760ms elapsed, using function
| 2300ms elapsed, using local variable
| 2286ms elapsed, using hard coded values
|
5570 elapsed, using function
| 406 elapsed, using local variable
| 383 elapsed, using hard coded values
| 3893 elapsed, using function without schemabinding
select top 1 @m = cv_val from code_values where cv_id = 'C101'
และเหมือนกัน... 'C201'
โดยที่ code_values เป็นตารางพจนานุกรมที่มี 250 vars มีทั้งหมดใน SQL-Server 2016
หากคุณสนใจที่จะรับแผนการดำเนินการที่เหมาะสมที่สุดสำหรับค่าในตัวแปรคุณสามารถใช้รหัส sql แบบไดนามิก มันทำให้ตัวแปรคงที่
DECLARE @var varchar(100) = 'some text'
DECLARE @sql varchar(MAX)
SET @sql = 'SELECT * FROM table WHERE col = '''+@var+''''
EXEC (@sql)
สำหรับ 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 )
เอาล่ะมาดูกัน
ค่าคงที่เป็นค่าที่ไม่เปลี่ยนรูปซึ่งเป็นที่ทราบกันในขณะคอมไพล์และไม่เปลี่ยนแปลงตลอดอายุของโปรแกรม
นั่นหมายความว่าคุณไม่มีค่าคงที่ใน SQL Server
declare @myvalue as int
set @myvalue = 5
set @myvalue = 10--oops we just changed it
ค่าเพิ่งเปลี่ยน
เนื่องจากไม่มีโครงสร้างที่รองรับค่าคงที่วิธีแก้ปัญหาของฉันจึงง่ายมาก
เนื่องจากไม่รองรับสิ่งนี้:
Declare Constant @supplement int = 240
SELECT price + @supplement
FROM what_does_it_cost
ฉันจะแปลงเป็นไฟล์
SELECT price + 240/*CONSTANT:supplement*/
FROM what_does_it_cost
เห็นได้ชัดว่าสิ่งนี้ขึ้นอยู่กับสิ่งทั้งหมด (ค่าที่ไม่มีช่องว่างต่อท้ายและความคิดเห็น) เป็นเอกลักษณ์ สามารถเปลี่ยนได้ด้วยการค้นหาทั่วโลกและแทนที่
ไม่มีสิ่งที่เรียกว่า "การสร้างค่าคงที่" ในเอกสารฐานข้อมูล ค่าคงที่มีอยู่และมักเรียกว่าค่า เราสามารถประกาศตัวแปรและกำหนดค่า (ค่าคงที่) ให้กับมันได้ จากมุมมองของนักวิชาการ:
DECLARE @two INT
SET @two = 2
ที่นี่ @two เป็นตัวแปรและ 2 คือค่า / ค่าคงที่
2
จะถูกแปลเป็นค่าไบนารีเมื่อกำหนดที่ "เวลาคอมไพล์" ค่าจริงที่เข้ารหัสขึ้นอยู่กับชนิดข้อมูลที่กำหนดให้ (int, char, ... )
คำตอบที่ดีที่สุดคือจาก 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
WITH SCHEMABINDING
ควรเปลี่ยนค่านี้ให้เป็นค่าคงที่ 'จริง' (ข้อกำหนดสำหรับ UDF ที่จะถูกมองว่าเป็นตัวกำหนดใน SQL) กล่าวคือควรขึ้นฝั่งที่ถูกแคช ยังคง +1