Сведения о вопросе

Sadijon

11:46, 5th August, 2020

Теги

sql-server   datetime    

SQL сервер DateTime сбой преобразования

Просмотров: 478   Ответов: 10

У меня есть большая таблица с 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

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

@Jon Лимджап

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


@SQLMenace

Проблема скорее всего в том, как хранятся данные, есть только два безопасных формата; ISO YYYYMMDD; ISO 8601 yyyy-mm-dd Thh:mm:ss:mmm (без пробелов)

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

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


@SQLMenace

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

@Brian Schkerke

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

Спасибо! Вот и все.



  Сведения об ответе

#hash

09:29, 18th August, 2020

Поместите 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' датой по умолчанию по вашему выбору.


  Сведения об ответе

dumai

19:05, 16th August, 2020

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

Не совсем setbased но если только 3 строки из 1 миллиона плохи это сэкономит вам много времени

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

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

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


  Сведения об ответе

#hash

20:07, 22nd August, 2020

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

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

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


  Сведения об ответе

PHPH

06:08, 8th August, 2020

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

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

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

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


  Сведения об ответе

DINO

12:18, 2nd August, 2020

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


  Сведения об ответе

PROGA

22:35, 22nd August, 2020

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

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

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

where lastUpdate > getDate() -31

Вы также должны учитывать часы и секунды конечно


  Сведения об ответе

fo_I_K

04:57, 2nd August, 2020

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


  Сведения об ответе

lats

18:02, 10th August, 2020

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

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


  Сведения об ответе

ASER

22:03, 29th August, 2020

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

ISO YYYYMMDD

ISO 8601 yyyy-mm-dd Thh:mm:ss:mmm(без пробелов)

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

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


  Сведения об ответе

COOL

01:06, 27th August, 2020

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

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

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться