排序规则不一定由数据库默认值决定:它们也可以按字符串字段设置。
不,除了使用动态 SQL 编写之外,我从未见过(而且我也看过)进行动态排序的方法COLLATE
子句放入查询中。或者,如果您需要考虑的选项数量相当少,您could也许尝试类似以下的方法:
SELECT ...
FROM ...
WHERE (@CaseSensitive = 1 AND [Field] LIKE N'%' + @Name + N'%' COLLATE Something_CS_AS)
OR (@CaseSensitive = 0 AND [Field] LIKE N'%' + @Name + N'%')
另外,没有direct大小写(甚至重音、假名或宽度)敏感和不敏感之间的等效性。虽然大多数情况下,不区分大小写的排序规则都有对应的区分大小写的排序规则,但有 15 种排序规则是仅不区分大小写的:
;WITH CaseS AS
(
SELECT [name]
FROM sys.fn_helpcollations()
WHERE [name] LIKE N'%[_]cs[_]%'
)
SELECT CaseI.*
FROM sys.fn_helpcollations() CaseI
LEFT JOIN CaseS
ON CaseI.name = REPLACE(CaseS.[name], N'_CS_', N'_CI_')
WHERE CaseI.[name] LIKE N'%[_]ci[_]%'
AND CaseS.[name] IS NULL;
Returns:
name description
SQL_1xCompat_CP850_CI_AS ...
SQL_AltDiction_CP850_CI_AI ...
SQL_AltDiction_Pref_CP850_CI_AS ...
SQL_Danish_Pref_CP1_CI_AS ...
SQL_Icelandic_Pref_CP1_CI_AS ...
SQL_Latin1_General_CP1_CI_AI ...
SQL_Latin1_General_CP1253_CI_AI ...
SQL_Latin1_General_CP437_CI_AI ...
SQL_Latin1_General_CP850_CI_AI ...
SQL_Latin1_General_Pref_CP1_CI_AS ...
SQL_Latin1_General_Pref_CP437_CI_AS ...
SQL_Latin1_General_Pref_CP850_CI_AS ...
SQL_Scandinavian_Pref_CP850_CI_AS ...
SQL_SwedishPhone_Pref_CP1_CI_AS ...
SQL_SwedishStd_Pref_CP1_CI_AS ...
在查询中修复这样的排序规则可能会在迁移代码时导致问题,
为什么?您打算将代码迁移到哪里?如果是另一个 RDBMS,那么您已经需要应对数据类型差异、SQL 方言差异、“最佳实践”差异等。那么为什么要担心排序规则呢?除非您确定要迁移到另一个 RDBMS,否则您应该充分利用当前平台,使您的系统尽可能最佳地工作,而不是由于以下原因而处于不太理想的状态:仅使用最低评论分母功能。
或稍后更改数据库排序规则。
你为什么要这样做?同样,具有显式 COLLATION 设置的任何字符串字段都不受数据库默认值的影响。
If you are looking for strict Case (and everything including Accent, etc) sensitivity on equivalence (we are not talking about range searches or sorting), then you can use a Binary collation (i.e. one ending in either _BIN
or _BIN2
). Just keep in mind that binary collations might not sort the way you might expect since they are not "dictionary" based sorts, at least not in terms of a single binary collation that would behave the same across all languages. They also don't make equivalences between languages (i.e. equating "a" with an "a" that has an accent).
自从最初发布这个答案以来,我发现上面的段落实际上是不好的建议。请这样做not如果目标是区分大小写,请使用二进制排序规则。它过于严格,在许多情况下不会给出准确的结果。详细信息和示例请参见:不,二进制排序规则不区分大小写.
另外,请做not使用以 just 结尾的二进制排序规则_BIN
因为自 SQL Server 2005 发布以来它们已过时,并且仅应在需要保持与另一个也使用 SQL Server 的系统的向后兼容性时使用_BIN
整理。如果您需要二进制排序规则,请使用以_BIN2
。详细信息和示例请参见:各种二进制排序规则之间的差异(文化、版本以及 BIN 与 BIN2).
UPDATE
我能够想出一个函数来获取传入排序规则的区分大小写的版本(如果存在)。然而,此函数仅有助于创建正确的动态 SQL;它不能在查询中内联使用来动态设置 COLLATE 子句(主要是因为不能这样做)。有两个参数:
-
@CollationName
-- 如果您传入此值,您将返回它的区分大小写的版本(如果存在)。这@DatabaseName
参数将被忽略。
-
@DatabaseName
-- 如果您不知道确切的排序规则,请离开@CollationName
as NULL
并将其传入,它将查找该数据库的默认排序规则。
- 如果两个参数都是
NULL
然后它将查找该函数所在数据库的默认排序规则。
- 如果传入或查找的排序规则已经区分大小写,则将返回该名称
- 待办事项(当我有时间时):查找没有默认值的数据库的服务器默认排序规则(它们将有
NULL
作为他们的默认排序规则名称)
该函数有两个版本:第一个是 TVF(因为它们更快)和标量 UDF(因为它们有时更容易交互)。
表值函数:
USE [Test];
SET ANSI_NULLS ON;
IF (OBJECT_ID(N'dbo.GetCaseSensitiveCollation') IS NOT NULL)
BEGIN
DROP FUNCTION dbo.GetCaseSensitiveCollation;
END;
GO
CREATE FUNCTION dbo.GetCaseSensitiveCollation
(
@CollationName sysname,
@DatabaseName sysname
)
RETURNS TABLE
--WITH SCHEMABINDING
-- Cannot schema bind table valued function 'dbo.GetCaseSensitiveCollation'
-- because it references system object 'sys.fn_helpcollations'.
AS RETURN
WITH collation(name) AS
(
SELECT CONVERT(sysname, COALESCE(@CollationName,
DATABASEPROPERTYEX(COALESCE(@DatabaseName, DB_NAME()), 'Collation')))
)
SELECT col.[name]
FROM sys.fn_helpcollations() col
CROSS JOIN collation
WHERE col.[name] = CASE WHEN collation.[name] LIKE N'%[_]CS[_]%'
THEN collation.[name]
ELSE REPLACE(collation.[name], N'_CI_', N'_CS_')
END;
GO
例子:
-- Get CS Collation for the specified Collation
SELECT [name] AS [BySpecificCollation]
FROM dbo.GetCaseSensitiveCollation(N'Indic_General_100_CI_AS_KS_WS', NULL);
-- Get CS Collation based on database default for the specified database
SELECT [name] AS [ByDefaultCollationForDB]
FROM dbo.GetCaseSensitiveCollation(NULL, N'msdb');
-- Get CS Collation based on database default for database that the function exists in
SELECT [name] AS [CurrentDB]
FROM Test.dbo.GetCaseSensitiveCollation(NULL, NULL);
-- Get CS Collation based on database default for the current database
USE [ReportServer];
SELECT [name] AS [CurrentDB]
FROM Test.dbo.GetCaseSensitiveCollation(NULL, DB_NAME());
标量用户定义函数:
USE [Test];
SET ANSI_NULLS ON;
IF (OBJECT_ID(N'dbo.GetCaseSensitiveCollation2') IS NOT NULL)
BEGIN
DROP FUNCTION dbo.GetCaseSensitiveCollation2;
END;
GO
CREATE FUNCTION dbo.GetCaseSensitiveCollation2
(
@CollationName sysname,
@DatabaseName sysname
)
RETURNS sysname
--WITH SCHEMABINDING
-- Cannot schema bind table valued function 'dbo.GetCaseSensitiveCollation2'
-- because it references system object 'sys.fn_helpcollations'.
AS
BEGIN
DECLARE @NewCollationName sysname;
;WITH collation(name) AS
(
SELECT CONVERT(sysname, COALESCE(@CollationName,
DATABASEPROPERTYEX(COALESCE(@DatabaseName, DB_NAME()), 'Collation')))
)
SELECT @NewCollationName = col.[name]
FROM sys.fn_helpcollations() col
CROSS JOIN collation
WHERE col.[name] = CASE WHEN collation.[name] LIKE N'%[_]CS[_]%'
THEN collation.[name]
ELSE REPLACE(collation.[name], N'_CI_', N'_CS_')
END;
RETURN @NewCollationName;
END;
GO
例子:
/* Get CS Collation for the specified Collation */
SELECT dbo.GetCaseSensitiveCollation2(N'Indic_General_100_CI_AS_KS_WS', NULL)
AS [BySpecificCollation];
-- Indic_General_100_CS_AS_KS_WS
/* Get CS Collation based on database default for the specified database */
SELECT dbo.GetCaseSensitiveCollation2(NULL, N'msdb') AS [ByDefaultCollationForDB];
-- SQL_Latin1_General_CP1_CS_AS
/* Get CS Collation based on database default for the current database */
USE [ReportServer];
SELECT Test.dbo.GetCaseSensitiveCollation2(NULL, DB_NAME()) AS [CurrentDB];
-- Latin1_General_CS_AS_KS_WS
/* Get CS Collation based on database default for database where the function exists */
SELECT Test.dbo.GetCaseSensitiveCollation2(NULL, NULL) AS [DBthatFunctionExistsIn];
-- SQL_Latin1_General_CP1_CS_AS