Ela*_*tor 3 sql-server collation ssms case-sensitive
SSMS 中有许多允许过滤的地方,例如对象资源管理器和分析器,但这些都将过滤器视为区分大小写,否则没有可见选项,因此如果您搜索,contains 'ASDF'
则包括“ASDF_MyEntity”之类的值,但“asdf_MyEntity” "省略。
例如,我们在大型服务器上有很多SQL 代理作业,我正在尝试使用对象资源管理器按项目名称过滤它们,我们总是在作业名称前加上前缀。但是,这些显示为大写和小写变体。
另一个用例是在数千个条目中搜索与模块相关的存储过程。如果命名不一致(例如 PascalCase 与 camelCase),过滤搜索将忽略它。这对于调试来说似乎是一个不必要的幻象危险。
此外,对象资源管理器中的排序将每种类型的实体(例如表名、存储过程、作业名称等)的大写变体放在小写之前(因此大写Z
在小写之前a
),所以我要么必须滚动很多,或检查两个不同的过滤器(如果不是全部大写或全部小写字母,则检查更多过滤器 -准确地说是2 len(name)次)。
我意识到我只能手动查询作业、表和其他实体,但鉴于 SSMS 存在,这样做是荒谬的,因为它的名称是“SQL Server Management Studio”。
我可以做些什么来使 SSMS(和 SQL Server Profiler)像其他所有 Windows 应用程序一样忽略大小写?也许我可以在本地更改排序规则设置?
另外,为什么微软在实施 SSMS 时做出这个决定?我发现它只是有害的。
此外,SSMS 的加载初始屏幕显示“基于 Visual Studio 构建”,这会忽略解决方案资源管理器中的大小写。
PS 使用 SSMS 2014(版本 12.0.5214.6)。我无法进行任何服务器端更改,并且我的本地开发环境应与目标服务器环境匹配以进行测试。
技术上可以进行这种不区分大小写的过滤,尽管只是由于 SSMS 中的一个错误。我没有 SSMS 2014(版本 12.x),但我确实让它在 SSMS 17.9.1 和 18.0 Preview 6 中工作,连接到一个默认排序规则为Hebrew_100_BIN2
.
尝试 Uno(否)
看到查询使用LIKE
,我想我会尝试使用单字符范围通配符(即[
和]
)。[Tt][Ee][Ss][Tt]
但是,由于某些字符被转义,传递模式不起作用。看来[
,%
和_
所有获得包裹在[
和]
把他们变成文字(即不再有特殊含义)。
尝试 Dos(是)
看到与LIKE
操作符一起使用的值(即输入到 UI 中的值)被连接到查询中而不是作为变量传入(即tbl.name like N''%Test%''
而不是tbl.name like N''%'' + @_msparam_XX + N''%''
),我开始思考,“难道没有理由为什么这样做很危险吗?用用户输入来做这个?嗯,我想知道......”所以我传入了 a'
并转义到了''
. 通常这正是单引号应该发生的情况。但是,这在动态 SQL 中使用,因此它比''
正确转义的where 嵌套更深一层。字符串中的字符串需要用单引号转义为''''
或双转义,因为''
仅在字符串中结束字符串。
例如,在过滤“表格”时,生成的代码(重要的一行)是:
CAST(tbl.is_external AS bit)=@_msparam_5 and tbl.name like N''%Test%'')
Run Code Online (Sandbox Code Playgroud)
如果我传入了“Test”,生成的代码应该是:
CAST(tbl.is_external AS bit)=@_msparam_5 and tbl.name like N''%Te''st%'')
Run Code Online (Sandbox Code Playgroud)
是的,它确实收到了“未关闭的引号”错误。
我尝试添加一个COLLATE
子句,但所有排序规则都有下划线,所以Latin1_General_100_CI_AS
变成了Latin1[_]General[_]100[_]CI[_]AS
. 哦!
我们能不能通过在任一[
或%
没有这些被包裹在[
和]
,但是,我们可以连接东西到N'%
字符串,如函数的输出。并且,我们可以通过将[
和%
字符作为十六进制 /VARBINARY
值传入,然后将CONVERT
它们放回NVARCHAR
一次来绕过转义字符。
因此,在对存储过程进行过滤时,我们从正常生成的代码开始:
AS bit)=@_msparam_3 and ISNULL(sm.uses_native_compilation,0)=@_msparam_4 and sp.name like N''%Test%'')',N'@_msparam_0 nvarchar(4000),@_msparam_1
Run Code Online (Sandbox Code Playgroud)
为了完成这项工作,我们需要传入一个具有以下结构的字符串(代替“Test”):
' + CONVERT(NVARCHAR(MAX), 0x.....) AND 'x' <> '
Run Code Online (Sandbox Code Playgroud)
我们可以--
在末尾使用代替AND 'x' <> '
,但该AND
结构确保如果查询的其他部分跟在这部分之后并且在同一行上,则它会继续按预期运行。
要生成我们的“转义”过滤器,我们可以使用以下内容:
SELECT CONVERT(VARBINARY(MAX), N'[Tt][Ee][Ss][Tt]%'); -- must end with "%"
-- 0x5B00540074005D005B00450065005D005B00530073005D005B00540074005D002500
Run Code Online (Sandbox Code Playgroud)
将该十六进制值粘贴到我们的 SQL 注入解决方法中,我们得到:
' + CONVERT(NVARCHAR(MAX), 0x5B00540074005D005B00450065005D005B00530073005D005B00540074005D002500) AND 'x' <> '
Run Code Online (Sandbox Code Playgroud)
如果我们直接将过滤器设置为上面的值,生成的代码将是:
AS bit)=@_msparam_3 and ISNULL(sm.uses_native_compilation,0)=@_msparam_4 and sp.name like N''%'' + CONVERT(NVARCHAR(MAX), 0x5B00540074005D005B00450065005D005B00530073005D005B00540074005D002500) AND ''x'' <> ''%'')',N'@_msparam_0 nvarchar(4000),@_msparam_1 nvarchar(4000),@_msparam_2 nvarchar(4000),@_msparam_3 nvarchar(4000),@_msparam_4 nvarchar(4000)',@_msparam_0=N'P',@_msparam_1=N'RF',@_msparam_2=N'PC',@_msparam_3=N'0',@_msparam_4=N'1'
Run Code Online (Sandbox Code Playgroud)
并将生成的代码传递到sp_executesql
. 执行的语句在 Profiler 中显示为:
AS bit)=@_msparam_3 and ISNULL(sm.uses_native_compilation,0)=@_msparam_4 and sp.name like N'%' + CONVERT(NVARCHAR(MAX), 0x5B00540074005D005B00450065005D005B00530073005D005B00540074005D002500) AND 'x' <> '%')
Run Code Online (Sandbox Code Playgroud)
这种技术之所以有效,0xYYYY....
是因为它不是解析器正在寻找的东西。这只是在寻找的文字值[
,_
和%
,和我们没有经过那些,我们只是传递的东西,将被翻译成那些一旦动态SQL执行。
但它不需要VARBINARY
是传入的字符串。任何传回字符串的函数都可以工作。您只需要将 的结尾N'%
与某些内容连接起来,然后是之前的内容%')
,因此:' + {something_ending_with_%'} AND 'x' <> '
。我本可以用来+ NCHAR(91) + N'Tt' + NCHAR(93) +
表示[Tt]
,但这似乎比连续VARBINARY
文字要庞大得多。但是,如果您只想要一个%
,那么做+ NCHAR(37) + N'
就可以了。请记住使用NCHAR()
和前缀文字,N
因为这是所有NVARCHAR
数据。
尝试 Tres (Mucho Yes)
因此,您不想[Tt][Ee][Ss][Tt]
每次都创建字符串,然后将其转换为VARBINARY
. 我明白了。哦,您无权在 SQL Server 中添加/更改对象。好的。但是您确实可以访问tempdb
,对吗?如果您可以在 中创建函数tempdb
,那么这会更容易。只需执行以下操作:
USE [tempdb];
IF (OBJECT_ID(N'dbo.ObjectFilter') IS NOT NULL)
BEGIN
DROP FUNCTION dbo.ObjectFilter;
END;
GO
CREATE FUNCTION dbo.ObjectFilter(@Name [sysname], @Filter [sysname])
RETURNS BIT
AS
BEGIN
IF (@Name LIKE N'%' + @Filter + N'%' COLLATE Latin1_General_100_CI_AS_SC)
BEGIN
RETURN 1;
END;
RETURN 0;
END;
GO
Run Code Online (Sandbox Code Playgroud)
然后将过滤器设置为:
' AND tempdb.dbo.ObjectFilter(sp.name, N'Test') = 1 AND 'x' <> '
生成的代码将是:
CAST(tbl.is_external AS bit)=@_msparam_5 and tbl.name like N'%' AND tempdb.dbo.ObjectFilter(sp.name, N'Test') = 1 AND 'x' <> '%')
Run Code Online (Sandbox Code Playgroud)
您将不得不调整列别名,因为表使用tbl.name
而不是sp.name
. 但是现在您可以传入任何字符串,它将使用不区分大小写的排序规则进行比较!!
而且,此功能将一直存在,直到实例重新启动。因此,您只需要每隔一段时间执行一次上述 T-SQL。
{掉话筒...走下舞台}?
补充说明:
请记住,此技术之所以有效,只是因为 SSMS 中存在错误。如果该错误得到修复,则此解决方法可能会停止工作。请参阅底部的“更新”部分,以获取指向我提交的关于此问题的错误报告的链接,以及我提交的要求每个可过滤属性的“不区分大小写”复选框的增强建议。
从兰迪的回答:
除非您找到查询的存储位置并对其进行更改,否则...
寻找查询是没有意义的。它们来自组成 SSMS 的 DLL。我认为C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\Microsoft.SqlServer.Management.Sdk.Sfc.dll可能是其中之一,但不确定。
从鲍勃对这个问题的评论:
除非服务器整理区分大小写,否则 SSMS 不会强制区分大小写。
这要看情况。有两个级别的排序规则——实例级别和数据库级别——重要的级别取决于某人试图过滤的元数据。实例级整理影响过滤实例级元数据(例如登录、链接服务器等)和系统数据库中的元数据。数据库级排序会影响过滤用户数据库元数据(例如用户、表等)。在包含/部分包含的数据库中,过滤数据库级元数据将始终使用排序规则:Latin1_General_100_CI_AS_WS_KS_SC
。
这种级别的分离解释了 Randi 的回答中提到的以下发现:
在 CI 服务器上的 CS DB 上似乎也不可能:
从 Elaskanator 对这个问题的评论:
当前,它设置为区分大小写的二进制排序规则 (Latin1_General_BIN)。
仅供参考:不幸的是,这是一个非常普遍的误解,但不,二进制排序规则不区分大小写
我已向 Microsoft 提交了以下反馈项目: