繁体   English   中英

有没有办法使 TSQL 变量保持不变?

[英]Is there a way to make a TSQL variable constant?

有没有办法使 TSQL 变量保持不变?

不,但是您可以创建一个函数并在其中对其进行硬编码并使用它。

下面是一个例子:

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

SELECT dbo.fnConstant()

Jared Ko 提供的一种解决方案是使用伪常量

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)

我对缺少常量的解决方法是向优化器提供有关值的提示。

DECLARE @Constant INT = 123;

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

这告诉查询编译器在创建执行计划时将变量视为常量。 不利的一面是您必须两次定义该值。

不,但应该使用良好的旧命名约定。

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)

对于枚举或简单常量,具有单行的视图具有出色的性能和编译时检查/依赖关系跟踪(导致其列名)

请参阅 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 是一个值/常量。

如果要创建一个临时常量以在脚本中使用,即跨多个 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM