使用 context_info 在 SQL Server Management Studio 与 SQLCMD 中返回不同的输出

问题描述 投票:0回答:1

我有这个 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 为什么要这样做?我可以改变什么?

sql-server sqlcmd sql-server-2022 context-info
1个回答
2
投票

问题是您使用了错误的数据类型。

首先,

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)
,以避免尽可能多的截断。

db<>小提琴

然而,

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';
    
© www.soinside.com 2019 - 2024. All rights reserved.