Как один фильтр основан на том, можно ли преобразовать поле в числовое значение? - PullRequest
1 голос
/ 26 января 2011

У меня есть отчет, который использовался довольно давно - фактически, система счетов компании в значительной степени опирается на этот отчет (Отказ от ответственности: я его не писал). Фильтрация основана на том, попадает ли поле типа VarChar (50) между двумя числовыми значениями, переданными пользователем.

Проблема в том, что поле, по которому данные фильтруются, теперь не только имеет простые нечисловые значения, такие как «/ A», «TEST» и множество других нечисловых данных, но также имеет числовые значения, которые кажется, бросает вызов любому типу числового преобразования, которое я могу придумать.

Следующий (упрощенный) тестовый запрос демонстрирует ошибку:

Declare  @StartSummary Int,
         @EndSummary Int

Select   @StartSummary = 166285,
         @EndSummary = 166289

Select   SummaryInvoice
From     Invoice
Where    IsNull(SummaryInvoice, '') <> ''
And      IsNumeric(SummaryInvoice) = 1
And      Convert(int, SummaryInvoice) Between @StartSummary And @EndSummary

Я также пытался выполнить преобразования, используя bigint, real и float, и все они выдают мне похожие ошибки:

Сообщение 8115, Уровень 16, Состояние 2, Строка 7 Преобразование арифметической ошибки переполнения выражение для типа данных int.

Я пробовал другие более крупные числовые типы данных, такие как BigInt, с той же ошибкой. Я также пытался использовать подзапросы, чтобы обойти проблему преобразования, только извлекая поля, содержащие числовые данные, и затем преобразовывая их в запросе оболочки, но затем я получаю другие ошибки, которые являются вариациями темы, указывающими, что значение хранится в поле SummaryInvoice не может быть преобразовано в соответствующий тип данных.

Если не извлекать только те записи с числовыми полями SummaryInvoice во временную таблицу, а затем запрашивать временную таблицу, есть ли одношаговое решение, которое решило бы эту проблему?

Редактировать : Вот данные поля, которые, как я подозреваю, вызывают проблему:

SummaryInvoice


+11111111111111111111111111

IsNumeric указывает, что это поле числовое - оно равно . Но попытка конвертировать его в BigInt вызывает арифметическое переполнение. Есть идеи? Похоже, это не единичный инцидент, похоже, было несколько записей, заполненных данными, которые вызывают эту проблему.

Ответы [ 4 ]

3 голосов
/ 26 января 2011

Похоже, у вас возникнут проблемы с функцией ISNUMERIC, поскольку она возвращает 1, если может быть приведена к любому типу чисел (включая ., ,, e0 и т. Д.). Если у вас номера больше 2 ^ 63-1, вы можете использовать DECIMAL или NUMERIC. Я не уверен, что вы можете использовать PATINDEX для выполнения регулярных выражений SummaryInvoice, но если вы можете, то вам следует попробовать это:

SELECT SummaryInvoice
FROM Invoice
WHERE ISNULL(SummaryInvoice, '') <> ''
AND CASE WHEN PATINDEX('%[^0-9]%',SummaryInvoice) > 0 THEN CONVERT(DECIMAL(30,0), SummaryInvoice) ELSE -1 END
BETWEEN @StartSummary And @EndSummary
1 голос
/ 26 января 2011

Вы не можете гарантировать, в каком порядке будут применяться фильтры предложений WHERE.

Один ужасный вариант для отделения внутреннего и внешнего.

SELECT
   *
FROM
    (
    Select   TOP 2000000000
             SummaryInvoice
    From     Invoice
    Where    IsNull(SummaryInvoice, '') <> ''
    And      IsNumeric(SummaryInvoice) = 1
    ORDER BY SummaryInvoice
    ) foo
WHERE
    Convert(int, SummaryInvoice) Between @StartSummary And @EndSummary

Другой с использованием CASE

Select   SummaryInvoice
From     Invoice
Where    IsNull(SummaryInvoice, '') <> ''
    And     
    CASE WHEN IsNumeric(SummaryInvoice) = 1 THEN Convert(int, SummaryInvoice) ELSE -1 END
          Between @StartSummary And @EndSummary

YMMV

Редактировать: после обновления вопроса

  1. использовать десятичное число (38,0), а не int
  2. Изменить ISNUMERIC (SummaryInvoice) на ISNUMERIC(SummaryInvoice + '0e0')
0 голосов
/ 26 января 2011

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

Рефакторинг базы данных - это не весело, но это нужно делать в случае проблем с целостностью данных.Я предполагаю, что вы на самом деле не выставляете кому-то счет на 11,111,111,111,111,111,111,111,111 или «тест».Поэтому не допускайте, чтобы эти значения когда-либо вводились (если вы не можете изменить структуру на правильный тип данных, рассмотрите триггер для предотвращения ввода плохих данных) и удалите те, которые у вас есть, которые являются плохими.

0 голосов
/ 26 января 2011

И с IsNumeric (SummaryInvoice) = 1, не будет короткого замыкания в SQL Server.

Но может быть, вы можете использовать

AND (CASE IsNumeric (SummaryInvoice) = 1, затем преобразованиеint, SummaryInvoice) ELSE 0 END) Между @StartSummary и @ EndSummary

...