2013-05-23 3 views
-5

Я использовал функцию MIN для сравнения символьных данных в столбце, который допускал null, с катастрофическими результатами. ;-) Вот очень упрощенный пример, который показывает такую ​​же вещь:Преобразует все в varbinary правильный способ сравнения символьных данных?

  1. Определить количество строк в таблице sys.indexes:

    select count(*) from sys.indexes; 
    
  2. Выполнить этот SQL:

    select count(name), substring(cast(min(name) as varbinary),1,1) 
        from sys.indexes; 
    

    Если счетчик совпадений №1, остановитесь здесь. Попробуйте использовать другую базу данных (с большим количеством объектов).

  3. Перейдите на вкладку «Сообщения». Вы должны увидеть: Предупреждение: значение Null исключается агрегатом или другой операцией SET.

  4. Как бы вы обрабатывали нули в столбце «имя», чтобы устранить это предупреждение? с коалесцированием?

  5. Заменить "имя" с "сливаются (имя, полукокса (0x7E))" и запустить SQL:

    select count(coalesce(name,char(0x7e))), 
        substring(cast(min(coalesce(name,char(0x7e))) as varbinary),1,1) 
        from sys.indexes; 
    
  6. Примечание результат функции MIN в # 5 (0x7E вместо 0x63) ,

Вопросы:

А. Является ли сливаться надлежащим образом обрабатывать предупреждения и отсутствующие данные (нуль) за № 4?

B. Поскольку результат в # 6 не является тем, что ожидается, каков правильный способ сравнения символьных данных в SQL Server? Преобразовать все в varbinary?

[Отредактировано ...]

В нижеприведенных дискуссии, существует некоторая путаница и дискуссии о взаимосвязи между нулевой заменой через COALESCE и результаты сравнений. Связь между ними заключается в следующем: когда вы выбираете строку (включая один символ) в качестве замещающего нулевую замену (шаги №4 и №5 выше), эта строка должна удовлетворять ожидаемым результатам сравнения (сравнения), которые являются выполняется против значений других данных в запросе. При некоторых сопоставлениях поиск подходящей строки может быть сложнее, чем при других сопоставлениях.

+2

Просто игнорируйте предупреждение. Это только информационное сообщение. Вы не должны использовать «varbinary» для сравнения символьных данных. –

+0

Если вы игнорируете предупреждение, подсчеты отключены. Это то, что привлекло мое внимание, в первую очередь, в исходном коде, что довольно много связано. –

+5

'COUNT (col)' только считает значения NOT NULL. Используйте 'COUNT (*)' для подсчета строк. Я не вижу, как это в какой-то мере связано с сопоставлением персональных данных, можете ли вы рассказать нам об исходном выпуске, который у вас был? –

ответ

2

отредактирован и Восстановлено

Ответ А .: Да, или вы можете использовать ISNULL() с тем же результатом, как COALESCE() в этом случае.

Ответа на этот вопрос: Не конвертировать varchar в varbinary для сравнения, но понять порядок сортировки при использовании агрегатов.

Я думаю, что этот фрагмент кода отвечает на подсчет с проблемой NULL, но я все еще немного путаются вопрос:

select count(*) from sys.indexes; 
-- 697 results 
go 
select count(isnull(name,'')) from sys.indexes; 
-- 697 results 
go 
select count(name) from sys.indexes; 
-- 567 results 
go 

И это получает количество записей для name поля MIN (на основе по обобщению и SQL порядок сортировки строковых полей):

select i.name 
     ,subCnt.Cnt 
from (select min(name) as name from sys.indexes) as i 
join (select name, count(*) as Cnt from sys.indexes group by name) as subCnt 
on  subCnt.name = i.name; 

и этот запрос объясняет совокупный порядок сортировки и почему выше запрос выбирает значение, возвращаемое в name поле:

select name, row_number() over (order by name) from sys.indexes order by name; 

И этот запрос показывает (Latin1_General_BIN) порядок сортировки моего COLLATION даже при замене NULLs с гольца (0x7E):

select coalesce(name,char(0x7e)) 
     , row_number() over (order by coalesce(name,char(0x7e))) 
from sys.indexes order by 2; 

И это показывает разницу сортировки порядка между параметрами сортировки в SQL Server (который определяет, что является MIN или MAX в поле строки):

declare @test table (oneChar char(1) collate Latin1_General_BIN 
        , oneChar2 char(1) collate SQL_Latin1_General_CP1_CI_AS 
        , varb varbinary) 

insert into @test (oneChar) 
select 'c' union all 
select '~' union all 
select 'P' union all 
select 'X' union all 
select 'q' union all 
select NULL 

update @test set varb = cast(isnull(oneChar,char(0x7E)) as varbinary), oneChar2 = oneChar 

select min(oneChar) from @test -- 'P' 
select min(oneChar2) from @test -- '~' 
select min(varb) from @test -- 0x50, the varbinary equivalent of oneChar 

и если вы хотите, количество всех строк, и вы хотите, MIN() от имени без учета NULLs (и не видя предупреждение, для whateve г причина), используйте:

select i1.Cnt 
     ,i2.name 
from (select count(*) as Cnt from sys.indexes) as i1 
     ,(select min(name) as name from sys.indexes where name is not null) as i2 

И что бы вы ни делали, конечно, не бросил все поле, как различные параметры сортировки просто сделать некоторую фильтрацию. Этот вопрос принадлежит дискуссионным форумам, а не как простой вопрос/ответ.

+1

Ответ 'A' изменит семантику. 'SELECT MIN (ISNULL (имя, '')) from (SELECT 'Foo' UNION ALL SELECT NULL) T (name)' возвращает '' '', даже если он не существует в данных. –

+0

Эх, да, ты прав. Наверное, я не совсем понимаю, что пытается сделать OP. –

+0

В вопросе «B» в исходном сообщении я пытался получить MIN, чтобы вернуть ожидаемый результат, поскольку в SQL в # 6 (а также в другом SQL с использованием различных операторов сравнения, которые я пытался), это не так. Попробуйте добавить функцию MIN к вашим вторым и третьим операторам SQL в вашем ответе (с или без подстроки и/или varbinary, или с использованием других функций сравнения/операторов). Когда подсчеты правильные, сравнения неверны и наоборот. –

0

Я предполагаю, что есть причина, вы не могли бы использовать ISNULL делать что-то вдоль линий: ISNULL (MyField, «Некоторые строки я буду знать, является нулевой»)

P.S. будьте осторожны с производительностью этого на больших наборах данных в производственной среде в зависимости от того, что вы делаете.

+1

Это то же самое, что и ответ, который уже удален, поскольку это изменяет семантику запроса. Два значения, которые имеют значение NULL, не должны быть преобразованы в 'some string', потому что нет возможности предположить, что эти два значения NULL * должны быть равны. Это работает только в случае, когда 'NULL' и' some string' означают одно и то же, что не совсем точно. –

+0

Использование ISNULL вместо COALESCE дает те же результаты: select count (isnull (name, char (0x7e))), substring (cast (min (isnull (name, char (0x7e))) как varbinary), 1,1) из sys.indexes; –

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