Ошибка преобразования даты и времени SQL Server

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

  •  09-06-2019
  •  | 
  •  

Вопрос

У меня есть большая таблица с более чем 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 ГГГГММДД;ISO 8601 гггг-мм-дд Чч:мм:сс:ммм (без пробелов)

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

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


@SQLMenace

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

@Брайан Шкерке

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

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

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

Решение

Поместите 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 строки из 1 миллиона являются плохими, это сэкономит вам много времени.

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

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

затем просто посмотрите таблицу BadDates и исправьте это

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

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

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

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

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

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

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

В вызове преобразования вам необходимо указать третий параметр стиля, например, формат даты и времени, которые хранятся как varchar, как указано в этом документе: ПРИВЕДЕНИЕ и ПРЕОБРАЗОВАНИЕ (T-SQL)

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

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

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

ISO ГГГГММДД

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

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

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

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

Запустите это, чтобы посмотреть, что произойдет

select isdate('20080131')
select isdate('01312008')

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

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

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

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

where lastUpdate > getDate() -31

Конечно, нужно также учитывать часы и секунды.

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