هل هناك طريقة لجعل متغير TSQL ثابتًا؟
-
09-06-2019 - |
سؤال
هل هناك طريقة لجعل متغير TSQL ثابتًا؟
المحلول
لا، ولكن يمكنك إنشاء وظيفة وترميزها هناك واستخدامها.
هنا مثال:
CREATE FUNCTION fnConstant()
RETURNS INT
AS
BEGIN
RETURN 2
END
GO
SELECT dbo.fnConstant()
نصائح أخرى
الحل البديل للثوابت المفقودة هو تقديم تلميحات حول القيمة للمُحسِّن.
DECLARE @Constant INT = 123;
SELECT *
FROM [some_relation]
WHERE [some_attribute] = @Constant
OPTION( OPTIMIZE FOR (@Constant = 123))
هذا يخبر مترجم الاستعلام بمعاملة المتغير كما لو كان ثابتًا عند إنشاء خطة التنفيذ.الجانب السلبي هو أنه يجب عليك تحديد القيمة مرتين.
استخدام الثوابت الزائفة: http://blogs.msdn.com/b/sql_server_appendix_z/archive/2013/09/16/sql-server-variables-parameters-or-literals-or-constants.aspx
الثوابت الزائفة ليست متغيرات أو معلمات.بدلاً من ذلك ، إنها ببساطة طرق عرض بصف واحد ، وأعمدة كافية لدعم ثوابتك.مع هذه القواعد البسيطة ، يتجاهل محرك SQL تمامًا قيمة العرض ولكنه لا يزال يبني خطة تنفيذ بناءً على قيمته.خطة التنفيذ لا تظهر حتى صلة بالعرض!
لا، ولكن يجب استخدام اصطلاحات التسمية القديمة الجيدة.
declare @MY_VALUE as int
لا يوجد دعم مضمن للثوابت في T-SQL.يمكنك استخدام أسلوب SQLMenace لمحاكاته (على الرغم من أنك لا تستطيع أبدًا التأكد مما إذا كان شخص آخر قد قام بالكتابة فوق الوظيفة لإرجاع شيء آخر...)، أو ربما كتابة جدول يحتوي على ثوابت، كما هو مقترح هنا.ربما تكتب مشغلًا يلغي أي تغييرات تم إجراؤها على ConstantValue
عمود؟
قبل استخدام دالة 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
إذا كنت مهتمًا بالحصول على خطة التنفيذ المثالية لقيمة في المتغير، فيمكنك استخدام كود SQL الديناميكي.يجعل المتغير ثابتا .
DECLARE @var varchar(100) = 'some text'
DECLARE @sql varchar(MAX)
SET @sql = 'SELECT * FROM table WHERE col = '''+@var+''''
EXEC (@sql)
بالنسبة للتعدادات أو الثوابت البسيطة، فإن العرض الذي يحتوي على صف واحد يتمتع بأداء رائع ويقوم بتجميع التحقق من الوقت/تتبع التبعية (يسبب اسم العمود)
راجع منشور مدونة جاريد كو 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 قيمة/ثابت.
أفضل إجابة هي من 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