Как я могу запросить «между» числовыми данными в нечисловом поле? - PullRequest
7 голосов
/ 21 сентября 2010

У меня есть запрос, который я только что нашел в базе данных, который не работает, что приводит к падению отчета.Основная суть запроса:

Select *
From table
Where IsNull(myField, '') <> ''
And IsNumeric(myField) = 1
And Convert(int, myField) Between @StartRange And @EndRange

Теперь, myField не содержит числовые данные во всех строках [это тип nvarchar] ... но этот запрос, очевидно, был разработан так, что он заботится только оо строках, где данные в этом поле являются числовыми.

Проблема с этим заключается в том, что T-SQL (насколько я понимаю) не замыкает замыкание предложения Where, в результате чего он отключается от записей, где данныене является числовым, за исключением:

Msg 245, Level 16, State 1, Line 1 Conversion failed when converting the nvarchar value '/A' to data type int.

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

Мой первый анализ, чтобы попытаться проанализировать возвращенные данные и посмотреть, что происходит:

Select *
From (
   Select *
   From table
   Where IsNull(myField, '') <> ''
   And IsNumeric(myField) = 1
) t0
Where Convert(int, myField) Between @StartRange And @EndRange

Но я получаю ту же ошибку, ясделал для первого запроса, который я не уверен, что понимаю, так как я не преобразовываю данные, которые не должны быть числовыми на данный момент.Подзапрос должен был вернуть только те строки, в которых myField содержит числовые данные.

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

Заранее спасибо

Ответы [ 4 ]

6 голосов
/ 21 сентября 2010

IsNumeric говорит только о том, что строку можно преобразовать в один из числовых типов в SQL Server.Он может быть в состоянии конвертировать его в деньги или в число с плавающей точкой, но не может конвертировать его в int.

Измените

IsNumeric(myField) = 1

на:

not myField like '%[^0-9]%' and LEN(myField) < 9

(то есть вы хотите, чтобы myField содержал только цифры и помещался в int)

Редактировать примеры:

select ISNUMERIC('.'),ISNUMERIC('£'),ISNUMERIC('1d9')

результат:

----------- ----------- -----------
1           1           1

(1 row(s) affected)
3 голосов
/ 21 сентября 2010

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

Select *
From ( TOP 2000000000
   Select *
   From table
   Where IsNumeric(myField) = 1
   And IsNull(myField, '') <> ''
   ORDER BY Key
) t0
Where Convert(int, myField) Between @StartRange And @EndRange

и другое

Select *
From table
Where

CASE
   WHEN IsNumeric(myField) = 1 And IsNull(myField, '') <> ''
   THEN Convert(int, myField) ELSE @StartRange-1
END Between @StartRange And @EndRange
  • Первый метод - «промежуточная материализация»: он вызывает сортировку на рабочем столе.
  • 2-й полагается на оценку CASE ORDER гарантированно
  • Ни симпатичен, ни фантастичен

SQL декларативен: вы говорите оптимизатору, что вы хотите, а не как это делать.Трюки, приведенные выше, заставляют делать вещи в определенном порядке.

1 голос
/ 21 сентября 2010

Используйте оператор CASE.

declare @StartRange int
declare @EndRange int

set @StartRange = 1
set @EndRange = 3

select *
from TestData
WHERE Case WHEN ISNUMERIC(Value) = 0 THEN 0
            WHEN Value IS NULL THEN 0
            WHEN Value = '' THEN 0
            WHEN CONVERT(int, Value) BETWEEN @StartRange AND @EndRange THEN 1
            END = 1
1 голос
/ 21 сентября 2010

Не уверен, поможет ли это вам, но я где-то читал, что неправильное преобразование с использованием CONVERT всегда приведет к ошибке в SQL. Поэтому я думаю, что было бы лучше использовать CASE в предложении where, чтобы избежать использования CONVERT для всех строк

...