Edm*_*und 9 sql-server enum sql-server-2016
我有几个 SQL 对象,它们需要根据请求的所需状态采取备用操作。有没有办法创建可以传递给存储过程、表值函数并在查询中使用(不使用 CLR)的数据库级常量(枚举)?
CREATE PROCEDURE dbo.DoSomeWork(@param1 INTEGER, ..., @EnumValue myEnumType) AS ...;
Run Code Online (Sandbox Code Playgroud)
然后使用它:
EXEC doSomeWork 85, ..., (myEnumType.EnumValue1 + myEnumType.EnumValue2);
Run Code Online (Sandbox Code Playgroud)
哪里myEnumType会保存一些枚举值。
在该过程中,我将能够使用@EnumValue它并针对其中的值对其进行测试myEnumType以完成所需的工作。我会myEnumType为我正在考虑的情况设置位掩码的值。
举一个简单的例子,考虑一个昂贵的过程,它需要一个巨大的数据集,并将其缩减为一个较小但仍然非常大的数据集。在这个过程中,您需要在该过程中进行一些会影响结果的调整。假设这是根据减少中的中间计算的某些状态过滤(或反对)某些类型的记录。该@EnumValue类型的myEnumType可以用来测试此
SELECT ...
FROM ...
WHERE (@EnumValue & myEnumType.EnumValue1 = myEnumType.EnumValue1 AND ...)
OR (@EnumValue & myEnumType.EnumValue2 = myEnumType.EnumValue2 AND ...)
OR ...
Run Code Online (Sandbox Code Playgroud)
在不使用 CLR 的情况下,SQL Server 中是否可以使用这些类型的数据库级常量?
我正在寻找可以作为参数传递给存储过程、函数等的数据库级枚举或常量集。
您可以使用 XML 架构在 SQL Server 中创建枚举类型。
例如颜色。
create xml schema collection ColorsEnum as '
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Color">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Red"/>
<xs:enumeration value="Green"/>
<xs:enumeration value="Blue"/>
<xs:enumeration value="Yellow"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>';
Run Code Online (Sandbox Code Playgroud)
这允许您使用类型的变量或参数xml(dbo.ColorsEnum)。
declare @Colors xml(dbo.ColorsEnum);
set @Colors = '<Color>Red</Color><Color>Green</Color>'
Run Code Online (Sandbox Code Playgroud)
如果你尝试添加一些不是颜色的东西
set @Colors = '<Color>Red</Color><Color>Ferrari</Color>';
Run Code Online (Sandbox Code Playgroud)
你得到一个错误。
create xml schema collection ColorsEnum as '
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Color">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Red"/>
<xs:enumeration value="Green"/>
<xs:enumeration value="Blue"/>
<xs:enumeration value="Yellow"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>';
Run Code Online (Sandbox Code Playgroud)
像这样构造 XML 可能有点乏味,因此您可以例如创建一个也包含允许值的辅助视图。
create view dbo.ColorsConst as
select cast('<Color>Red</Color>' as varchar(100)) as Red,
cast('<Color>Green</Color>' as varchar(100)) as Green,
cast('<Color>Blue</Color>' as varchar(100)) as Blue,
cast('<Color>Yellow</Color>' as varchar(100)) as Yellow;
Run Code Online (Sandbox Code Playgroud)
并像这样使用它来创建枚举。
set @Colors = (select Red+Blue+Green from dbo.ColorsConst);
Run Code Online (Sandbox Code Playgroud)
如果您想从 XML 架构动态创建视图,您可以使用此查询提取颜色。
select C.Name
from (select xml_schema_namespace('dbo','ColorsEnum')) as T(X)
cross apply T.X.nodes('//*:enumeration') as E(X)
cross apply (select E.X.value('@value', 'varchar(100)')) as C(Name);
Run Code Online (Sandbox Code Playgroud)
枚举当然也可以用作函数和过程的参数。
declare @Colors xml(dbo.ColorsEnum);
set @Colors = '<Color>Red</Color><Color>Green</Color>'
Run Code Online (Sandbox Code Playgroud)
set @Colors = '<Color>Red</Color><Color>Ferrari</Color>';
Run Code Online (Sandbox Code Playgroud)
Msg 6926, Level 16, State 1, Line 43
XML Validation: Invalid simple type value: 'Ferrari'. Location: /*:Color[2]
Run Code Online (Sandbox Code Playgroud)
由于您显然使用的是 SQL Server 2016,我想放弃另一个“可能”选项 - SESSION_CONTEXT。
Leonard Lobel 的文章“在 SQL Server 2016 中共享状态”提供了有关 SQL Server 2016 中SESSION_CONTEXT这一新功能的一些非常好的信息。
总结一些关键点:
如果您曾经想在数据库连接的整个生命周期内在所有存储过程和批处理之间共享会话状态,那么您一定会喜欢
SESSION_CONTEXT. 当你连接到 SQL Server 2016 时,你会得到一个有状态的字典,或者通常被称为状态包,在某个地方你可以存储值,比如字符串和数字,然后通过你分配的键检索它。在 的情况下SESSION_CONTEXT,键是任何字符串,值是一个 sql_variant,这意味着它可以容纳多种类型。一旦你在 中存储了一些东西
SESSION_CONTEXT,它就会一直留在那里,直到连接关闭。它不存储在数据库的任何表中,只要连接保持活动状态,它就存在于内存中。并且在存储过程、触发器、函数或其他任何东西中运行的任何和所有 T-SQL 代码都可以共享您输入的任何内容SESSION_CONTEXT。到目前为止
CONTEXT_INFO,我们拥有的最接近的东西是,它允许您存储和共享长达 128 个字节的单个二进制值,这远不如您使用的字典灵活SESSION_CONTEXT,后者支持不同数据的多个值类型。
SESSION_CONTEXT易于使用,只需调用 sp_set_session_context 即可按所需键存储值。当你这样做时,你当然提供键和值,但你也可以将 read_only 参数设置为 true。这会锁定会话上下文中的值,以便在连接的剩余生命周期内无法更改它。因此,例如,客户端应用程序很容易在建立数据库连接后立即调用此存储过程来设置一些会话上下文值。如果应用程序在执行此操作时设置了 read_only 参数,则随后在服务器上执行的存储过程和其他 T-SQL 代码只能读取该值,它们无法更改客户端上运行的应用程序设置的内容。
作为测试,我创建了一个服务器登录触发器,它设置了一些CONTEXT_SESSION信息 - 其中一个SESSION_CONTEXT设置为@read_only.
DROP TRIGGER IF EXISTS [InitializeSessionContext] ON ALL SERVER
GO
CREATE TRIGGER InitializeSessionContext ON ALL SERVER
FOR LOGON AS
BEGIN
--Initialize context information that can be altered in the session
EXEC sp_set_session_context @key = N'UsRegion'
,@value = N'Southeast'
--Initialize context information that cannot be altered in the session
EXEC sp_set_session_context @key = N'CannotChange'
,@value = N'CannotChangeThisValue'
,@read_only = 1
END;
Run Code Online (Sandbox Code Playgroud)
我以全新用户身份登录并能够提取SESSION_CONTEXT信息:
DECLARE @UsRegion varchar(20)
SET @UsRegion = CONVERT(varchar(20), SESSION_CONTEXT(N'UsRegion'))
SELECT DoThat = @UsRegion
DECLARE @CannotChange varchar(20)
SET @CannotChange = CONVERT(varchar(20), SESSION_CONTEXT(N'CannotChange'))
SELECT DoThat = @CannotChange
Run Code Online (Sandbox Code Playgroud)
我什至试图更改“只读”上下文信息:
EXEC sp_set_session_context @key = N'CannotChange'
,@value = N'CannotChangeThisValue'
Run Code Online (Sandbox Code Playgroud)
并收到一个错误:
消息 15664,级别 16,状态 1,过程 sp_set_session_context,第 1 行 [批处理开始第 8 行] 无法在会话上下文中设置键“CannotChange”。此会话的密钥已设置为只读。
关于登录触发器的重要说明(来自这篇文章)!
登录触发器可以有效地阻止所有用户(包括 sysadmin 固定服务器角色的成员)成功连接到数据库引擎。当登录触发器阻止连接时,sysadmin 固定服务器角色的成员可以通过使用专用管理员连接或通过以最小配置模式 (-f) 启动数据库引擎来进行连接
一个潜在的缺点是这会填充会话上下文实例范围内(而不是每个数据库)。在这一点上,我能想到的唯一选择是:
Session_Context与数据库名称前缀它们,以免引起对在另一个数据库中同一类型名称冲突名称-值对。这并不能解决Session_Context为所有用户预定义所有名称值的问题。EventData(xml),您可以使用它来提取登录用户,并在此基础上创建特定的Session_Context名称-值对。在 SQL Server 中,没有(尽管我记得早在 1998 年就在 Oracle 包中创建了常量,并且有点怀念在 SQL Server 中使用它们的日子)。
而且,我刚刚测试并发现您甚至无法使用 SQLCLR 执行此操作,至少不能在所有情况下都有效。阻碍是对存储过程参数的限制。参数名称中似乎不能有 a.或。::我试过:
EXEC MyStoredProc @ParamName = SchemaName.UdtName::[StaticField];
-- and:
DECLARE @Var = SchemaName.UdtName = 'something';
EXEC MyStoredProc @ParamName = @Var.[InstanceProperty];
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,它甚至没有通过解析阶段(使用验证SET PARSEONLY ON;),因为:
消息 102,级别 15,状态 1,第 xxxxx
行“.”附近的语法不正确。
另一方面,这两种方法都适用于用户定义函数参数:
SELECT MyUDF(SchemaName.UdtName::[StaticField]);
-- and:
DECLARE @Var = SchemaName.UdtName = N'something';
SELECT MyUDF(@Var.[InstanceProperty]);
Run Code Online (Sandbox Code Playgroud)
因此,您真正能做的最好的事情就是使用 SQLCLR 来获得直接与 UDF、TVF、UDA(我假设)和查询一起使用的东西,然后在需要与存储过程一起使用时分配给局部变量:
DECLARE @VarInt = SchemaName.UdtName::[StaticField];
EXEC MyStoredProc @ParamName = @VarInt;
Run Code Online (Sandbox Code Playgroud)
这是我在有机会获得实际枚举值时所采取的方法(而不是查找值,查找值应该位于特定于其用法/含义的查找表中)。
关于尝试使用用户定义函数(UDF)吐出“常量”/“枚举”值,我无法将其作为存储过程参数传递:
EXEC MyStoredProc @ParamName = FunctionName(N'something');
Run Code Online (Sandbox Code Playgroud)
返回“语法不正确”错误,SSMS 会突出显示括号内的所有内容,即使我用数字替换字符串,或者如果没有要传入的参数,则用右括号替换字符串。