SQL中的全局变量

时间:2010-01-26 22:09:13

标签: sql sql-server

假设我想创建一个sql脚本并执行以下操作:

DECLARE @SomeVariable int
SET @SomeVariable = 'VALUE'
  FROM someTable
--do stuff with @SomeVariable
GO

CREATE PROCEDURE myProcedure
(
  @MyParameter
)
AS
SET NOCOUNT ON

--Do something
--Do something using @SomeVariable
SET NOCOUNT OFF
RETURN 0
GO

我不能,因为@SomeVariable死于他所属的批次,而myProcedure需要自己的批次。显然我可以创建一个#temp表并填充我需要的任何值,但是我必须从中选择 - 添加代码,尽管是微不足道的,但是当我需要的只是一个全局变量时,它会损害可读性并且看起来很愚蠢。还有更好的方法吗?

痛苦地清楚。我知道SQL Server有“全局变量”称为“表” - 我在上面的段落中提到使用#table是一种可能的解决方案,就像使用实际的永久表一样。我在这里寻找的可能更多的是一个全局常量,我可以在给定的脚本中使用任何地方,而不是全局变量 - 所以我们都可以停止润湿我们的裤子关于全局变量的邪恶。

7 个答案:

答案 0 :(得分:5)

GO语句不是SQL语言规范的一部分,是批处理分隔符。您的局部变量作用于批处理。因此,它们在GO声明中超出了范围。我认为你唯一的选择就是你描述的内容。

答案 1 :(得分:4)

目前尚不清楚为什么存储过程在两个批次的示例集中依赖于全局。我看到两种主要可能性:SP在创建时依赖于全局(即代码生成 - 案例1),或者SP对全局具有运行时依赖性(即必须在参数化之间进行选择 - 案例2 - 或者自我配置 - 案例3)。

在运行时依赖性的情况下,无论是从SP外部的某个位置获取并作为参数传递还是直接在SP内部传递,都是基本的设计决策。选择何时将数据作为参数传递以及何时从表中提取数据并不完全是一门科学,它完全取决于系统中所有的实际使用情况。

案例1 - 代码生成:

DECLARE @SomeVariable int 
SET @SomeVariable = 'VALUE' 
  FROM someTable 
--do stuff with @SomeVariable 
GO 

DECLARE @sp as varchar(MAX)

SET @sp = '
CREATE PROCEDURE myProcedure -- I would actually name this myProcedure_ + CONVERT(varchar, @SomeVariable), since each proc generated might function differently
( 
  @MyParameter 
) 
AS 
SET NOCOUNT ON 
DECLARE @SomeVariable AS int -- This is going to be an initialized local copy of the global at time of SP creation
SET @SomeVariable = ' + CONVERT(varchar, @SomeVariable) + '

--Do something 
--Do something using @SomeVariable 
SET NOCOUNT OFF 
RETURN 0 
'
EXEC(@sp) -- create the procedure dynamically

Executing the producedure normally as EXEC myProcedure or EXEC myProcedure_1, etc.

案例2 - 参数化:

DECLARE @SomeVariable int 
SET @SomeVariable = 'VALUE' 
  FROM someTable 
--do stuff with @SomeVariable 
GO 

CREATE PROCEDURE myProcedure 
( 
  @MyParameter 
  ,@SomeVariable int
) 
AS 
SET NOCOUNT ON 

--Do something 
--Do something using @SomeVariable 
SET NOCOUNT OFF 
RETURN 0 
GO 

现在每当调用myProcedure时,必须始终传递参数@SomeVariable。当您使用不同的参数化定期调用相同的SP时,建议使用此方法

案例3 - 配置:

DECLARE @SomeVariable int 
SET @SomeVariable = 'VALUE' 
  FROM someTable 
--do stuff with @SomeVariable 
GO 

CREATE PROCEDURE myProcedure 
( 
  @MyParameter 
) 
AS 
SET NOCOUNT ON 

--Do something 
DECLARE @SomeVariable int 
SET @SomeVariable = 'VALUE' 
  FROM someTable 

SET NOCOUNT OFF 
RETURN 0 
GO 

现在,每当您执行myProcedure时,您需要确保已在表中设置配置。建议将此方案用于缓慢更改的配置案例。在这种情况下,您可以将@SomeVariable初始化包装在标量值UDF中,这样在不同SP中使用相同配置的任何时候,它们都将通过相同的UDF调用,从而使您可以更改配置表约定(你不会给你的用户SELECT权限,无论如何,对吧?)如果UDF需要根据用户或类似的方式开始变化,你现在有一个控制点来强制执行一致性,权限和接口调用约定:

DECLARE @SomeVariable int 
SET @SomeVariable = dbo.udf_Global(username, session, etc.)
--do stuff with @SomeVariable 
GO 

CREATE PROCEDURE myProcedure 
( 
  @MyParameter 
) 
AS 
SET NOCOUNT ON 

--Do something 
DECLARE @SomeVariable int 
SET @SomeVariable = dbo.udf_Global(username, session, etc.)

SET NOCOUNT OFF 
RETURN 0 
GO 

答案 2 :(得分:1)

如果您正在使用多个不同的过程可以使用的某些全局持久值,那么将其存储在表中是我能想到的最佳值。

如果您只想要在单个过程中多次使用的变量,那么将其包含在过程定义中。

CREATE PROCEDURE myProcedure 
( 
  @MyParameter 
) 
AS 
SET NOCOUNT ON 

DECLARE @SomeVariable int 
SET @SomeVariable = 'VALUE' 
  FROM someTable 
--do stuff with @SomeVariable 

--Do something 
--Do something using @SomeVariable 
SET NOCOUNT OFF 
RETURN 0 
GO 

如果你想要一个很好的逻辑封装,你可以方便地引用,那么标量用户定义函数(UDF)可能就是你所追求的。

答案 3 :(得分:1)

我想我们需要更多关于此的背景,因为我在这里没有看到这一点,但我可以根据我所知道的情况给你一些见解。

这里有两个不同的批次,一个只是一个常规代码块,另一个实际上并没有做任何事情,只是创建了一个存储过程。

如果您需要存储过程具有要使用的值,请将其设为输入。

如果需要跨存储过程共享上述值,可能可以使用标量函数返回所需的值。

答案 4 :(得分:0)

全局变量在任何编程语言中都是不好的做法。为什么不直接将变量作为参数传递给存储过程。

CREATE PROCEDURE myProcedure
(
  @MyParameter,
  @SomeVariable  -- the global variable
)
AS
...

答案 5 :(得分:0)

如何创建一个名为dbo.Configuration的表格,您可以存储一堆值,在需要时将它们拉出来?它最多只有一到两页,访问速度很快,可能一直留在RAM中,你可以从任何地方进入它。

答案 6 :(得分:0)

虽然这是一个老话题,但问题仍然有效。 “为什么”不是问题(即盖茨说“[为什么] 会有人需要更多 64k?” :))但我会提供示例:

假设客户端使用 SQL 2012(因此,除了有限的单个二进制 (128) 值与命名变量之外,快乐会话上下文不可用):

  1. 客户对支持 RLS 不感兴趣
  2. 客户正在利用多租户方法
  3. 视图用于租户 1 跨表联接,联接的一部分是限制集合的租户值列表(显然,这将是不同的值,具体取决于访问视图的租户)
  4. 租户在此加入中使用链接服务器

连接到另一个表是一个临时链接服务器的死亡。将限制值“传递”到链接服务器端会减少返回的结果集。一个函数会做同样的事情。显然,对于 2016 年,这可以通过会话上下文来完成,但同样 - 2012 年的限制是不够的。

什么是“好”(并且在视图中可读)将是诸如 选择 * 从服务器 1 a 内连接(select * from abc.db.dbo.tbl where somevalue in (@@RestrictedList))b 在 a.field = b.field

只是想... :)

如果这是一个过程 - okilies。没问题。但是...因为它们是视图,所以大便哈哈。

回答,如果您没有使用链接服务器,并且在 2012 年及之前,一种方法是 UDF(小心...根据您的使用方式评估性能)。 如果你使用的东西 > 2012 而不是跨链接服务器,那么会话上下文将起作用(然后你必须在该上下文中设置它)