2015-03-07 2 views
2

Есть ли способ получить зависящую от регистра версию варианта сортировки для использования в запросе?Как получить чувствительную к регистру версию сортировки в SQL Server?

Предположим, что запрос может быть использован для баз данных с различными сопоставлениями, некоторые из которых не чувствительны к регистру и могут иметь разные культуры. (например, несколько клиентов)

Однако этот запрос всегда должен вести себя с учетом регистра, если это возможно, не изменяя культуру сортировки и другие свойства.

Например, если в БД используется SQL_Latin1_General_CP1_CI_AS (CI здесь означает нечувствительность к регистру), я бы хотел использовать SQL_Latin1_General_CP1_CS_AS (CS для чувствительности к регистру).

упрощенный пример запроса:

DECLARE @Title nvarchar(2) = 'qQ' 

--Case insensitive (following DB collation) 
SELECT REPLACE(@Title, 'q', 'o') --Result: 'oo' 

--Case sensitive, but fixed to a collation 
SELECT REPLACE(@Title COLLATE SQL_Latin1_General_CP1_CS_AS, 'q', 'o') --Result: 'oQ' 

Крепление сортировки, как это в запросе может вызвать проблемы при переносе кода, или изменение параметров сортировки БД на последнюю дату.

Есть ли встроенная функция для получения версии текущего сопоставления с учетом регистра или обходной путь, который может быть использован для этого?

ответ

4

Коллажи не обязательно определяются значением по умолчанию для базы данных: они также могут быть установлены в поле строки.

Нет, я никогда не видел пути (и я посмотрел) для выполнения динамических сопоставлений за пределами использования Dynamic SQL для записи предложения COLLATE в запрос. Или, если число параметров, которые вы должны учитывать довольно минимальны, может возможно попробовать что-то вроде следующего:

SELECT ... 
FROM ... 
WHERE (@CaseSensitive = 1 AND [Field] LIKE N'%' + @Name + N'%' COLLATE Something_CS_AS) 
OR (@CaseSensitive = 0 AND [Field] LIKE N'%' + @Name + N'%') 

Кроме того, нет прямой эквивалентность между случаем (или даже Accent, Кана или Ширина) чувствительны и нечувствительны. Хотя большая часть времени есть чувствительные к регистру аналог регистронезависимой сортировки, есть 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; 

Возвращает:

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   ... 

Фиксирующими такая сортировка в запросе может вызвать проблемы при переносе кода,

Почему? Где вы планируете переносить код? Если в другую СУБД, то вам уже нужно бороться с различиями типов данных, различиями диалектов SQL, различиями в «передовых методах» и т. Д. Так почему же беспокоиться о сопоставлениях? Если вы точно не знаете, что будете мигрировать на другую СУРБД, вы должны сделать свою систему максимально возможной, используя существующую платформу, насколько это возможно, а не существующую в менее оптимальном состоянии из-за используя только функцию с наименьшим комментарием-знаменателем.

или изменение сортировки БД на последнюю дату.

Зачем вам это нужно? Опять же, все строковые поля с явным значением COLLATION не зависят от базы данных по умолчанию.


Если вы ищете строгого Case (и все, включая Accent и т.д.) чувствительность на эквивалентности (мы не говорить о поиске диапазона или сортировки), то вы можете использовать двоичный сопоставление (т.е. одно окончание либо _BIN, либо _BIN2). Просто имейте в виду, что двоичные сортировки могут не сортироваться так, как вы могли бы ожидать, поскольку они не являются сортировками на основе словаря, по крайней мере, не с точки зрения единой бинарной сортировки, которая будет вести себя одинаково на всех языках. Они также не делают эквивалентности между языками (т. Е. Приравнивают «а» к «а», имеющему акцент).

С момента первоначального опубликования этого ответа я обнаружил, что приведенный выше параграф является на самом деле плохим советом. Пожалуйста, сделайте не используйте двоичную сортировку, если цель - чувствительность к регистру. Он слишком строгий и во многих случаях не дает точных результатов.

Кроме того, пожалуйста не использование двоичной сортировки, оканчивающиеся всего _BIN, поскольку они устарели, так как SQL Server 2005 был выпущен и должен использоваться только при необходимости поддерживать обратную совместимость с другой системой также с использованием _BIN сортировки. Если вам нужна бинарная сортировка, используйте один конец в _BIN2.


UPDATE

я смог придумать функцию, чтобы получить случай чувствительную версию, если таковая существует, ушедшей в обобщению. Однако эта функция поможет только создать правильный динамический SQL; он не может использоваться inline в запросе, чтобы динамически устанавливать предложение COLLATE (главным образом потому, что это невозможно). Есть два параметра:

  • @CollationName - если вы передаете это, вы получите обратно регистрозависимое версию этого, если таковой существует. Параметр @DatabaseName будет проигнорирован.
  • @DatabaseName - если вы не знаете точное сопоставление, оставьте @CollationNameNULL и передайте его, и он будет искать настройку по умолчанию для этой базы данных.
  • Если оба Титулы будут NULL то он будет искать параметры сортировки по умолчанию для базы данных, которая существует функция в.
  • Если переданная или смотрел вверх сверка уже чувствительно к регистру, то имя будет возвращены
  • TO DO (когда у меня есть время): искать сервер по умолчанию параметры сортировки для баз данных, которые не имеют значения по умолчанию (они будут иметь NULL как их имя сортировки по умолчанию)

есть две версии функции: сначала это TVF (как быстрее) и Scalar 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()); 

Скалярное User-Defined Function:

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 
+0

У нас есть большое количество устаревших клиентских баз данных с одинаковой структурой, но с разными сопоставлениями. Некоторые из них нечувствительны к регистру. Было бы здорово, если бы не пришлось адаптировать SP к каждому из них. Если этого не сделать, нам, возможно, придется динамически строить и выполнять запросы. Или, скорее, есть сценарии, которые генерируют варианты SP. Я просто надеялся, что есть более элегантное решение :) – Oxians

+0

@Oxians Я вижу, что вы уже приняли (спасибо), но я добавил предложение к вершине, связанному с «динамическими» запросами. Убедитесь, что вы проверили несколько случаев, хотя этот шаблон обычно не тарифицируется, а также Dynamic SQL. –

+0

@Oxians Я нашел способ получить регистрационную форму (если таковая существует) конкретной сортировки. Посмотрите раздел UPDATE моего ответа :). –

Смежные вопросы