سؤال

هل هناك طريقة لجعل متغير 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top