SQL: «Невозможно построить дату типа данных» при сравнении двух дат - PullRequest
0 голосов
/ 04 октября 2019

У меня проблема в запросе, когда SQL Server выдает ошибку

Невозможно создать дату типа данных, некоторые аргументы имеют значения, которые недопустимы

при сравнении двух объектов даты, которые сами по себе являются действительными.

Если я удаляю предложение where, оно разрешается без ошибок, но в тот момент, когда я пытаюсь сравнить их с любым реляционным оператором или оператором равенства, снова возникают ошибки.

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

with Years as 
(
    select 
        YEAR(getdate()) + 1 Year, 
        DATEFROMPARTS(YEAR(getdate()) + 1, 1, 1) FirstOfTheYear, 
        0 YearOffset
    union all
    select 
        Year - 1,
        DATEFROMPARTS(Year - 1, 1, 1),
        YearOffset + 1
    from Years
    where YearOffset < 5
),
Months as
(
    select 1 Month
    union all
    select Month + 1
    from Months
    where Month < 12
),
Days as 
(
    select 1 Day
    union all
    select Day + 1
    from Days
    where Day < 31
), 
Dates as 
(
    select cast(DATEFROMPARTS(Year, Month, Day) as date) Date
    from Years
    cross join Months
    cross join Days
    where DAY(EOMONTH(FirstOfTheYear, Month - 1)) >= Day
)
select Dates.Date, cast ('2019-10-01' as date), CAST ('2019-10-11' as date)
from Dates
where Date = cast ('2019-10-01' as date) -- Comment this line out and the error goes away, occurs with any date construction pattern
--where Dates.[Date] >= datefromparts(2019, 10, 01) and Dates.[Date] <= DATEFROMPARTS(2019, 10, 11)
order by date

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

Кроме того, вручную создается несколько дат (сначала2015-2019 гг., октябрьские даты в запросе) и запрос этого не приводит к отображению ошибки.

Редактировать: хочу подчеркнуть, что код уже обрабатывает февраль иправильно високосные годыВывод даты CTE действителен и выводит полный диапазон без ошибок. только , когда я ссылаюсь на дату в предложении where, она выдает ошибку

Edit2: я смог решить свою проблему, переключившись на другой шаблон генерации даты (добавление день за днем, в рекурсивном), но мне все еще любопытно, что вызывает эту ошибку.

Ответы [ 3 ]

1 голос
/ 05 октября 2019

Суть пары других ответов заключается в том, что атака на проблему таким способом, которым вы являетесь, не обязательно является наиболее эффективным способом создания таблицы дат. В большинстве случаев, когда люди ограничены SQL-сервером, они заставляют кого-то использовать таблицу Tally для этой цели. Это останется операцией, основанной на SET, вместо того, чтобы требовать зацикливания или рекурсии. Это означает, что предел рекурсии, который вы упомянули в одном из ваших комментариев, просто не применяется.

Таблица Tally - это набор числовых значений, которые вы затем можете использовать для генерации или создания значений, которые вы хотите. В этом случае это примерно 1827 дней (5 лет + 1 день), но может отличаться високосными годами. Високосные годы и февраль, вероятно, являются проблемами в вашем коде. В любом случае, для создания таблицы подсчета вы можете начать с 10 значений, а затем выполнить перекрестное соединение, пока не получите приемлемое количество комбинаций. 3 перекрестных соединения принесут вам 10000 значений, а ROW_NUMBER() - 1 можно использовать для генерации приращения на основе 0. После чего вы можете использовать DATEADD() для фактического создания дат:

;WITH cteTen AS (
    SELECT n FROM (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) T(n)
)

, cteTally AS (
    SELECT
        N = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
    FROM
        cteTen t10
        CROSS JOIN cteTen t100
        CROSS JOIN cteTen t1000
        CROSS JOIN cteTen t10000 
)

, cteStartOfNextYear AS (
    SELECT
        StartOfNextYear = s.[Date]
        ,NumOfDaysBetween = DATEDIFF(DAY,DATEADD(YEAR,-5,s.[Date]),s.[Date])
    FROM
        (VALUES (DATEFROMPARTS(YEAR(GETDATE()) + 1, 1, 1))) s([Date])
)

, cteDates AS (
    SELECT
        [Date] = DATEADD(DAY,- t.N, s.StartOfNextYear)
    FROM
        cteStartOfNextYear s
        INNER JOIN cteTally t
        ON t.N <= NumOfDaysBetween
)

SELECT *
FROM
    cteDates
ORDER BY
    [Date]

В нашем разговоре я понимаю, почему вы думаете, что EOMONTH () позаботится о проблеме, но это порядок операцийвроде, как бы, что-то вроде. Таким образом, часть DATEFROMPARTS () анализируется по всему набору данных перед интерпретацией предложения where. Поэтому он пытается построить дату 29,30 февраля и т. Д., Прежде чем ограничить ее числом дней, определенным EOMONTH () где пункт

0 голосов
/ 04 октября 2019

Вы просите DATEFROMPARTS преобразовать недопустимые комбинации дат и времени. Вот что выдает ошибку - не ваш CAST оператор.

См. Использование T-SQL DATEFROMPARTS для возврата NULL вместо броска ошибки , чтобы найти даты вашей проблемы в целом.

Ваш запрос создает даты, включающие 29, 30 и 31 февраля, а также 31 апреля, июнь, сентябрь и ноябрь.

Если вы просто хотите указать все даты с 2015 по 2020, вы можете сосчитатьвыходной и добавь к базовой дате. SQL Server будет обрабатывать проблемы месяца для вас:

--  Create up to 16 million integers
WITH N AS (SELECT 0 AS N FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7)) T(n))
,    M AS (SELECT 0 AS N FROM N A, N B, N C, N D, N E, N F, N G, N H)
,    Z AS (SELECT ROW_NUMBER() OVER (ORDER BY A.N) AS N FROM M A)

--  Filter only the integers you need; add to a start date
SELECT CAST(DATEADD(DAY, N-1, '2015-01-01') AS DATE) FROM Z
WHERE N < DATEDIFF(DAY, '2015-01-01', '2020-01-01')
0 голосов
/ 04 октября 2019

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

В любом случае 29, 30 или 31 февраля может привести к ошибке. Вы можете исправить этот подход, изменив подзапрос dates:

Dates as (
    select try_convert(date, concat(year, '-' month, '-', day)) as  Date
    from Years y cross join
         Months m cross join
         Days
    where try_convert(date, concat(year, '-' month, '-', day)) and
          DAY(EOMONTH(FirstOfTheYear, Month - 1)) >= Day
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...