Ошибка преобразования даты в SQL Server - PullRequest
3 голосов
/ 26 августа 2008

У меня большая таблица с 1 миллионами + записей. К сожалению, создатель таблицы решил поместить даты в поле varchar(50).

Мне нужно сделать простое сравнение дат -

datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31

Но он не работает на convert():

Conversion failed when converting datetime from character string.

Очевидно, что в этой области есть что-то, что ей не нравится, и поскольку записей так много, я не могу сказать, просто посмотрев на это. Как правильно санировать все поле даты, чтобы оно не сбилось на convert()? Вот что у меня сейчас:

select count(*)
from MyTable
where
    isdate(lastUpdate) > 0
    and datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31

@ SQLMenace

Меня не беспокоит производительность в этом случае. Это будет одноразовый запрос. Изменение таблицы на поле даты и времени не является обязательным.

@ Джон Лимджап

Я попытался добавить третий аргумент, и это не имеет значения.


@ SQLMenace

Проблема скорее всего в том, как хранятся данные, есть только два безопасных формата; ИСО ГГГГММДД; ISO 8601 гггг-мм-дд чч: мм: сс: ммм (без пробелов)

Разве проверка isdate() не позаботится об этом?

Мне не нужна точность 100%. Я просто хочу получить большинство записей за последние 30 дней.


@ SQLMenace

select isdate('20080131') -- returns 1
select isdate('01312008') -- returns 0

@ Brian Schkerke

Поместите CASE и ISDATE в функцию CONVERT ().

Спасибо! Это сделал это.

Ответы [ 10 ]

8 голосов
/ 26 августа 2008

Поместите CASE и ISDATE в функцию CONVERT().

SELECT COUNT(*) FROM MyTable
WHERE
    DATEDIFF(dd, CONVERT(DATETIME, CASE IsDate(lastUpdate)
        WHEN 1 THEN lastUpdate
        ELSE '12-30-1899'
    END), GetDate()) < 31

Замените '12-30-1899' на выбранную вами дату по умолчанию.

3 голосов
/ 26 августа 2008

Как насчет написания курсора для циклического просмотра содержимого, попытки преобразования для каждой записи? При возникновении ошибки выведите первичный ключ или другие идентифицирующие сведения для записи проблемы. Я не могу придумать основанный на множестве способ сделать это.

Не полностью установленный, но если только 3 строки из 1 миллиона плохие, это сэкономит вам много времени

select * into BadDates
from Yourtable
where isdate(lastUpdate) = 0

select * into GoodDates
from Yourtable
where isdate(lastUpdate) = 1

, тогда просто посмотрите на таблицу BadDates и исправьте это

2 голосов
/ 26 августа 2008

ISDATE () позаботится о строках, которые не были отформатированы должным образом, если они действительно выполнялись первыми. Однако, если вы посмотрите на план выполнения, вы, вероятно, обнаружите, что вначале применяется предикат DATEDIFF - таким образом, причина вашей боли.

Если вы используете SQL Server Management Studio, нажмите CTRL + L , чтобы просмотреть примерный план выполнения для конкретного запроса.

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

1 голос
/ 26 августа 2008

Как насчет написания курсора для циклического просмотра содержимого и попытки приведения для каждой записи?

При возникновении ошибки выведите первичный ключ или другую идентификационную информацию для записи проблемы.

Я не могу придумать, как это сделать на основе множеств.

Редактировать - ах да, я забыл об ISDATE (). Определенно лучший подход, чем использование курсора. +1 к SQLMenace.

0 голосов
/ 14 марта 2009

Я уверен, что изменение таблицы / столбца может не быть вариантом из-за каких-либо устаревших системных требований, но вы задумывались о создании представления, в котором встроена логика преобразования даты, если вы используете более свежую версию sql, тогда вы можете даже использовать индексированное представление?

0 голосов
/ 26 августа 2008

Разве проверка isdate () не позаботится об этом?

Запустите это, чтобы увидеть, что происходит

select isdate('20080131')
select isdate('01312008')
0 голосов
/ 26 августа 2008

Скорее всего, проблема в том, как хранятся данные, есть только два безопасных формата

ISO ГГГГММДД

ISO 8601 гггг-мм-дд чч: мм: сс: ммм (без пробелов)

они будут работать независимо от того, на каком языке вы говорите.

Вам может понадобиться сделать SET DATEFORMAT YMD (или как там хранятся данные), чтобы заставить его работать

0 голосов
/ 26 августа 2008

Распечатайте записи. Дайте распечатку идиоту, который решил использовать varchar (50), и попросите его найти запись о проблеме.

В следующий раз они могут просто увидеть смысл выбора подходящего типа данных.

0 голосов
/ 26 августа 2008

При вызове преобразования вам нужно указать третий параметр стиля, например, формат даты и времени, которые хранятся как varchar, как указано в этом документе: CAST и CONVERT (T-SQL)

0 голосов
/ 26 августа 2008

Я бы посоветовал навести порядок и поменять столбец на datetime, потому что делал что-то вроде этого

WHERE datediff(dd, convert(datetime, lastUpdate), getDate()) < 31

не может использовать индекс, и он будет во много раз медленнее, чем если бы у вас был столбец datetime, n и сделал

where lastUpdate > getDate() -31

Вам также необходимо учитывать часы и секунды курса

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...