我有这个 T-SQL 代码:
declare @DBNAAM varbinary(128) = cast((select DB_NAME() as [Current Database]) + '_SINGLES-' as varbinary(128));
set context_info @DBNAAM;
go
print cast(context_info() as varchar(128)) + 'XTRA_NAME'
go
如果我在 SSMS 中运行它,我会得到输出:
TEST_T24TSV_SINGLES-XTRA_NAME
这正是我所期望的,数据库名称是
TEST_T24TSV_SINGLES
。
但是如果我使用 SQLCMD 运行相同的脚本,输出是:
T
所以
T
之后的所有内容都消失了!
(如果我将 SSMS 的第一个输出复制到 f.e 记事本,我也只得到 T。)
(我想使用context_info的构造,因为我想打印很多这样的内容(在无数次GO之后),并且我想为我所做的每次打印都有一行语句。不可能使用子查询作为打印语句的一部分。)
SQL Server 为什么要这样做?我可以改变什么?
问题是您使用了错误的数据类型。
首先,
DB_NAME
返回一个sysname
,它是nvarchar(128) NOT NULL
的同义词。您将其转换为 varbinary(128)
(因此可能会被截断),然后将该值转换为 varchar
,因此第二个字节现在将被视为 null
字符。
其次,
CONTEXT_INFO
存储binary
值,不是varbinary
,这意味着如果您使用null
,您会在字符串末尾得到很多(n)varchar
字符。将 binary
转换为 varbinary
不会从值末尾删除 0x00
字节。
因此您需要转换为
nchar(128)
,然后转换为 binary(128)
(因为 CONTEXT_INFO
允许 128 个字节),然后转换为 SET
值;这意味着末尾的字节是 whitespace not null
字符。然后,您可以将该值 back 转换为 nchar(128)
、RTRIM
(隐式将其转换为 nvarchar
),然后连接:
DECLARE @DBNAAM binary(128) = CONVERT(binary(128),CONVERT(nchar(128),DB_NAME() +N'_SINGLES-'));
SET context_info @DBNAAM;
GO
PRINT RTRIM(CONVERT(nchar(128),CONTEXT_INFO()))+N'XTRA_NAME';
如果您知道您的数据库只能包含 ASCII 字符,那么我建议转换为
char(128)
而不是 nchar(128)
,以避免尽可能多的截断。
然而,Damien_The_Un believer确实提出了一个关于使用SESSION_CONTEXT的好观点。这避免了“麻烦”,并且该值使用 sql_variant
存储,因此最多支持 8,000 字节的数据。使用此方法的解决方案如下所示:
DECLARE @DBNAAM sysname = DB_NAME() + N'_SINGLES-';
EXEC sys.sp_set_session_context N'DBNAAM', @DBNAAM;
GO
PRINT CONVERT(nvarchar(128),SESSION_CONTEXT(N'DBNAAM')) + N'XTRA_NAME';