SQL неверный обратный возврат преобразования null вместо ошибки

StackOverflow https://stackoverflow.com/questions/4100008

  •  29-09-2019
  •  | 
  •  

Вопрос

У меня есть таблица с столбцом Varchar, и я хочу найти значения, которые соответствуют определенным номерам. Так что позволяет сказать, что столбец содержит следующие записи (кроме миллионов строк в реальной жизни):

123456789012
2345678
3456
23 45
713?2
00123456789012

Так что я решаю, я хочу, чтобы все строки, которые численно 123456789012 написать заявление, которое выглядит что-то подобное:

SELECT * FROM MyTable WHERE CAST(MyColumn as bigint) = 123456789012

Он должен вернуть первый и последний ряд, но вместо этого весь запрос ударил, потому что он не может преобразовать «23 45» и «713? 2» для Bigint.

Есть ли другой способ сделать преобразование, которое вернет NULL для значений, которые не могут преобразовать?

Это было полезно?

Решение

SQL Server не гарантирует булевой оператор короткого замыкания, см. На SQL Server Boolean Operator Operator Coving. Отказ Так что все решения, используя ISNUMERIC(...) AND CAST(...) принципиально ошибочны (они могут работать, но эй, может произвольно не потерпеть неудачу позже зависящий от сформированного плана). Лучшее решение использует чехол, так как предполагает Томас: CASE ISNUMERIC(...) WHEN 1 THEN CAST(...) ELSE NULL END. Отказ Но, как указал Гбн, ISNUMERIC Застепенно Sinkicky в выявлении того, какие «числовые» средства и многие случаи, когда можно ожидать, что он возвращается 0, он возвращает 1. Так что смешивание корпуса с тобой:

CASE WHEN MyRow NOT LIKE '%[^0-9]%' THEN CAST(MyRow as bigint) ELSE NULL END

Но настоящая проблема в том, что если у вас есть миллионы строк, и вы должны искать их подобные, вы всегда будете в конечном итоге сканирование сквозной, поскольку выражение не является SARG-IS (независимо от того, как мы его переписали). Реальная проблема здесь является чистота данных и должна быть адресована на соответствующем уровне, где данные заполнены. Другое, что следует учитывать, это если возможно, что можно создать сохраняемый компьютерный столбец с этим выражением и создать фильтрованный индекс на нем, который исключает нулевые (т. Е. Не числовой). Это немного ускорило вещи.

Другие советы

Если вы используете SQL Server 2012, вы можете использовать 2 новых метода:

Оба метода эквивалентны. Они возвращают значение, отлимое указанному типу данных, если актеры преуспевают; В противном случае возвращается NULL. Единственное отличие состоит в том, что преобразование является специфическим SQL Server, CAST - ANSI. Использование CAST сделает ваш код более портативным (хотя и не уверен, если любые другие провайдеры базы данных TRY_CACH)

ISNUMIC примет пустую строку и значения, такие как 1.23 или 5E-04, так что может быть ненадежной.

И вы не знаете, на каком порядке будет оценено все, что он все еще может потерпеть неудачу (SQL является декларативным, а не процедурным, поэтому то, где предложение, вероятно, не будет оценено вправо)

Так:

  • Вы хотите принять значение, которое состоит Только персонажей 0-9
  • Вам необходимо наносить материализацию фильтра «номер», чтобы он был применен перед литым

Что-то вроде:

SELECT 
*
FROM
   (
   SELECT TOP 2000000000 *
   FROM MyTable
   WHERE MyColumn NOT LIKE '%[^0-9]%'  --double negative rejects anything except 0-9
   ORDER BY MyColumn 
   ) foo
WHERE 
    CAST(MyColumn as bigint) = 123456789012 --applied after number check

Редактировать: Быстрый пример, который не удается.

CREATE TABLE #foo (bigintstring varchar(100))
INSERT #foo (bigintstring )VALUES ('1.23')
INSERT #foo (bigintstring )VALUES ('1 23')
INSERT #foo (bigintstring )VALUES ('123')

SELECT * FROM #foo
WHERE
   ISNUMERIC(bigintstring) = 1 
   AND 
   CAST(bigintstring AS bigint) = 123
SELECT * 
    FROM MyTable 
    WHERE ISNUMERIC(MyRow) = 1
        AND CAST(MyRow as float) = 123456789012

Функция ISNUMIC () должна дать вам то, что вам нужно.

SELECT * FROM MyTable
WHERE ISNUMERIC(MyRow) = 1
AND CAST(MyRow as bigint) = 123456789012

И добавить заявление о случаях, как Томас предложил:

SELECT * FROM MyTable
WHERE CASE(ISNUMERIC(MyRow)
        WHEN 1 THEN CAST(MyRow as bigint)
        ELSE NULL
      END = 123456789012

http://msdn.microsoft.com/en-us/library/ms186272.aspx.

SELECT *
FROM MyTable
WHERE (ISNUMERIC(MyColumn) = 1) AND (CAST(MyColumn as bigint) = 123456789012)

Кроме того, вы можете использовать оператор регистра, чтобы получить нулевые значения.

SELECT 
    CASE
        WHEN (ISNUMERIC(MyColumn) = 1) THEN CAST(MyColumn as bigint)
        ELSE NULL
    END AS 'MyColumnAsBigInt'
FROM tableName

Если вам требуется дополнительная фильтрация, для численности, которые недействительны для Bigint, вы можете использовать следующие вместо ISNUMIC:

Patindex ('% [^ 0-9]%', mycolumn)) = 0

Если вам нужны десятичные значения вместо целых чисел, вместо этого отбрасывают и измените регулярное выражение в% [^ 0-9.]% '

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top