SSMS - 如何在对象资源管理器中进行不区分大小写的搜索

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)。我无法进行任何服务器端更改,并且我的本地开发环境应与目标服务器环境匹配以进行测试。

Sol*_*zky 8

技术上可以进行这种不区分大小写的过滤,尽管只是由于 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。

{掉话筒...走下舞台}?

补充说明:

  1. 请记住,此技术之所以有效,只是因为 SSMS 中存在错误。如果该错误得到修复,则此解决方法可能会停止工作。请参阅底部的“更新”部分,以获取指向我提交的关于此问题的错误报告的链接,以及我提交的要求每个可过滤属性的“不区分大小写”复选框的增强建议。

  2. 从兰迪的回答:

    除非您找到查询的存储位置并对其进行更改,否则...

    寻找查询是没有意义的。它们来自组成 SSMS 的 DLL。我认为C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\Microsoft.SqlServer.Management.Sdk.Sfc.dll可能是其中之一,但不确定。

  3. 从鲍勃对这个问题的评论:

    除非服务器整理区分大小写,否则 SSMS 不会强制区分大小写。

    这要看情况。有两个级别的排序规则——实例级别和数据库级别——重要的级别取决于某人试图过滤的元数据。实例级整理影响过滤实例级元数据(例如登录、链接服务器等)和系统数据库中的元数据。数据库级排序会影响过滤用户数据库元数据(例如用户、表等)。在包含/部分包含的数据库中,过滤数据库级元数据将始终使用排序规则:Latin1_General_100_CI_AS_WS_KS_SC

    这种级别的分离解释了 Randi 的回答中提到的以下发现:

    在 CI 服务器上的 CS DB 上似乎也不可能:

  4. 从 Elaskanator 对这个问题的评论:

    当前,它设置为区分大小写的二进制排序规则 (Latin1_General_BIN)。

    仅供参考:不幸的是,这是一个非常普遍的误解,但不,二进制排序规则不区分大小写

更新

我已向 Microsoft 提交了以下反馈项目:

  1. BUG:SSMS:对象资源管理器过滤允许 SQL 注入 (oops)
  2. 建议:SSMS:允许在对象资源管理器过滤器中强制不区分大小写匹配

  • @RandiVertongen 是的,你明白了。使用 0xYYYY 不是解析器正在寻找的东西。它只查找 `[`、`_` 和 `%` 的字面值。确切地说,我可以使用 `+ NCHAR(91) + N'Tt' + NCHAR(93) +` 来表示 `[Tt]`,但这似乎比连续的 `VARBINARY` 文字要庞大得多。但是如果你只是想要`%`,那么做`+ NCHAR(37) + N'` 就可以了。请记住使用 `NCHAR()` 并使用 `N` 作为前缀文字,因为这都是 NVARCHAR 数据。我会看到如何将这些信息添加到我的答案中。 (2认同)