2014-02-21 2 views
4

У меня странная проблема: при выполнении агрегатной функции в столбце varchar типа cast я получаю «Msg 8114, уровень 16, состояние 5, строка 1. Ошибка преобразования типа данных nvarchar в bigint». Запросы, где предложение должно отфильтровывать нечисловые значения.MSSQL aggregate игнорирует where where

Структура таблицы аналогична следующим образом:

IF EXISTS (SELECT * FROM sys.all_objects ao WHERE ao.name = 'Identifier' AND ao.type = 'U') BEGIN DROP TABLE Identifier END 
IF EXISTS (SELECT * FROM sys.all_objects ao WHERE ao.name = 'IdentifierType' AND ao.type = 'U') BEGIN DROP TABLE IdentifierType END 

CREATE TABLE IdentifierType 
(
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [Style] [int] NULL, 
    CONSTRAINT [PK_IdentifierType_ID] PRIMARY KEY CLUSTERED ([ID] ASC) 
) ON [PRIMARY] 

CREATE TABLE Identifier 
(
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [IdentifierTypeID] [int] NOT NULL, 
    [Value] [nvarchar](4000) NOT NULL, 
    CONSTRAINT [PK_Identifier_ID] PRIMARY KEY CLUSTERED ([ID] ASC) 
) ON [PRIMARY] 

ALTER TABLE Identifier WITH CHECK ADD CONSTRAINT [FK_Identifier_IdentifierTypeID] FOREIGN KEY([IdentifierTypeID]) REFERENCES IdentifierType ([ID]) 
GO 

Identifier.Value представляет собой столбец VARCHAR, она может и действительно содержит не-числовых данных. Фильтрация запроса в IdentifierType.Style = 0 должна означать, что 'Value' возвращает только строковые представления целых чисел. Ниже приведен запрос с ошибкой «Msg 8114, уровень 16, состояние 5, строка 1. Ошибка преобразования типа данных nvarchar в bigint».

SELECT 
    MAX(CAST(Value AS BIGINT)) 
FROM 
    Identifier i, 
    IdentifierType it 
WHERE 
    i.IdentifierTypeID = it.ID AND 
    it.Style = 0 

Если я продлить статью, где включить «И IsNumeric (i.Value) = 1» он возвращает максимальное целое значение. Это означает, что в моем результирующем наборе есть нечисловая строка. Тем не менее, я не получаю строк, возвращаемые из этого:

SELECT 
    * 
FROM 
    Identifier i, 
    IdentifierType it 
WHERE 
    i.IdentifierTypeID = it.ID AND 
    it.Style = 0 AND 
    ISNUMERIC(i.Value) <> 1 

Я был не в состоянии идентичности строки (s), которые расцепление типа броска. Вышеприведенный запрос должен был раскрывать исключительные строки. Кроме того, нет ни пустых, ни чрезвычайно длинных строк (наибольшая строка имеет длину 6 символов)

Возможно ли, что MSSQL пытается выполнить CAST на всех строках, а не сначала фильтровать с помощью предложения WHERE?

Или кто-нибудь еще видел что-то подобное?

Существует вторая работа, которая создает экземпляр компонента запроса во временную таблицу, а затем выбирает значение MAX из этого.

SELECT 
    Value 
INTO 
    IdentifierClone 
FROM 
    Identifier i, 
    IdentifierType it 
WHERE 
    i.IdentifierTypeID = it.ID AND 
    it.Style = 0 

SELECT MAX(CAST(Value as BIGINT)) FROM IdentifierClone 

Подзапрос не работает, однако.

Любая помощь или мысли будут оценены.

+3

См [SQL Server не должен вызывать нелогичные ошибки] (https://connect.microsoft.com/SQLServer/feedback/details/537419/ sql-server-should-not-raise-notogical-errors) на сайте обратной связи Connect. SQL Server иногда перескакивает вперед и выполняет более ранние преобразования, которые вызывают ошибки, которые не будут, если бы он фактически обработал запрос в правильном логическом порядке. –

+0

У меня была аналогичная проблема с isnumeric, пытаясь найти не числовые значения и не получить ожидаемый результат из-за самого isnumeric. Если это проблема заказа, попробуйте организовать с помощью подтаблицы, чтобы убедиться, что это лучше, сделайте предложение where в подтаблице, а затем сделайте свою актерскую съемку. Вы уверены, что он отфильтрован до этого. –

+2

@MatheseF - если вы ссылаетесь на подзапрос, вы ошибаетесь в том, что «уверен, что он был отфильтрован до» - оптимизатор может и * будет * перегруппировать преобразования и фильтровать даже с подзапросами и все равно может произвести эти нелогичные ошибки. Единственный верный способ, который я знаю, чтобы предотвратить это, состоит в том, чтобы разделить запрос на два полностью отдельных запроса и первым запросом заполнить переменную temp table/table, которую затем выполняет второй запрос. –

ответ

0

Попробуйте использовать выражение REGEX, чтобы найти запись о проблеме. Вот пример, где ISNUMERIC не обнаруживает проблему, но выражение регулярного выражения делает

CREATE TABLE tst (value nvarchar(4000)) 
INSERT INTO tst select '£' 
-- Record found ... 
SELECT * FROM tst WHERE value NOT LIKE '%[0-9]%' 
-- No record found ... 
SELECT * from tst where isnumeric(value) <> 1 
+0

Если вы хотите предложить метод проверки того, является ли строка конвертируемой для целого числа, условие в вашем первом SELECT должно быть 'LIKE '% [^ 0 -9]% ''(чтобы найти значение, которое не является конвертируемым *). –

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