Причинение ошибки неверного дня при использовании временных дат в предложении WHERE - PullRequest
0 голосов
/ 16 октября 2018

Мне велено создать календарь, похожий на календарь, на основе существующих записей и указать, существует ли запись на эту дату.

Чтобы получить пример сценария, возьмите эти записи из таблицы образца:TIME_LOG (ID, PUNCH_TIME).

1   1/1/2018 8:00:00
2   1/1/2018 12:12:00
...
n   2/14/2020 8:00:00

В этом примере мне нужно сделать следующее:

  1. Получить все имеющиеся месяцы наTIME_LOG, которые относятся к январю 2018 года и февралю 2020 года.
  2. Перечислите все даты в этих двух месяцах: 1–31 января 2018 года плюс 1–29 февраля 2020 года. Затем укажите его в столбце DATE_TOKEN из набора результатов.
  3. Установите 'Record found' или 'No records found', существует ли значение из столбца DATE_TOKEN из TIME_LOG.Сделайте это как столбец IS_FOUND набора результатов.

Чтобы получить этот набор результатов, это мой предварительный запрос:

SELECT   a.date_token,
         NVL2 (b.date_token, 'Record found.',
               'No records found.') AS is_found
    FROM (SELECT TO_DATE (a.MONTH || '/' || b.DAY || '/' || a.YEAR,
                          'MM/DD/YYYY'
                         ) AS date_token
            FROM (SELECT   TO_CHAR (EXTRACT (MONTH FROM a.punch_time)
                                   ) AS MONTH,
                           TO_CHAR (EXTRACT (YEAR FROM a.punch_time)) AS YEAR
                      FROM vw_each_punch a
                  GROUP BY TO_CHAR (EXTRACT (MONTH FROM a.punch_time)),
                           TO_CHAR (EXTRACT (YEAR FROM a.punch_time))) a
                 JOIN
                 (SELECT     TO_CHAR (ROWNUM) AS DAY
                        FROM DUAL
                  CONNECT BY ROWNUM <= 31) b
                 -- I placed this condition to eliminate dates such as February 31, etc.
                 -- and it works unless I uncomment the WHERE clause below.
                 ON b.DAY <=
                      EXTRACT (DAY FROM LAST_DAY (TO_DATE (   a.MONTH
                                                           || '/1/'
                                                           || a.YEAR,
                                                           'MM/DD/YYYY'
                                                          )
                                                 )
                              )
                 ) a
         LEFT JOIN
         (SELECT   TRUNC (a.punch_time) AS date_token
              FROM vw_each_punch a
          GROUP BY TRUNC (a.punch_time)) b ON b.date_token = a.date_token
-- WHERE TRUNC (a.date_token, 'MONTH') = '1-FEB-2020'
ORDER BY 1, 2

Это работает нормально и возвращает 60 строкнабор результатов, аналогичный этому (DATE_TOKEN, IS_FOUND):

1/1/2018    Record found.
1/2/2018    No records found.
1/3/2018    No records found.
...
2/13/2018   No records found.
2/14/2018   Record found.
2/15/2018   No records found.
...

, пока мне не понадобилось фильтровать набор результатов по определенным месяцам.Когда я пытаюсь раскомментировать предложение WHERE в моем запросе, чтобы показать только даты февраля 2020 года, оно выдает мне ошибку:

ORA-01839: date not valid for month specified

Набор результатов загружается полностью без каких-либо ошибок, если нетWHERE предложение, показанное предыдущим набором результатов с 60 строками.Я подозреваю, что ошибка происходит из-за того, что февраль 2020 года составляет менее 31 дня, но это было удалено условием b.DAY <= EXTRACT (DAY FROM LAST_DAY (TO_DATE (a.MONTH || '/1/' || a.YEAR, 'MM/DD/YYYY'))).Кроме того, я изменил условие с февраля 2020 года на январь 2018 года, но оно по-прежнему не работает.

Можно ли как-нибудь использовать предложение WHERE, не выдавая ошибку ORA-01839?

Вот пример запущенного SQL с использованием факторинга подзапроса, если вам нужно запустить его как можно скорее:

WITH time_log_temp (id, punch_time) 
     AS (SELECT 1, 
                To_date('2018-1-1 8:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM') 
         FROM   dual 
         UNION ALL 
         SELECT 2, 
                To_date('2018-1-1 12:12:00 AM', 'YYYY-MM-DD HH:MI:SS AM') 
         FROM   dual 
         UNION ALL 
         SELECT 2, 
                To_date('2020-2-14 8:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM') 
         FROM   dual), 
     inter 
     AS (SELECT a.date_token, 
                Nvl2 (b.date_token, 'Record found.', 'No records found.') AS 
                is_found 
         FROM   (SELECT To_date (a.month 
                                 || '/' 
                                 || b.day 
                                 || '/' 
                                 || a.year, 'MM/DD/YYYY') AS date_token 
                 FROM   (SELECT To_char (Extract (month FROM a.punch_time)) AS 
                                MONTH, 
                                To_char (Extract (year FROM a.punch_time))  AS 
                                YEAR 
                         FROM   time_log_temp a 
                         GROUP  BY To_char (Extract (month FROM a.punch_time)), 
                                   To_char (Extract (year FROM a.punch_time))) a 
                        join (SELECT To_char (ROWNUM) AS DAY 
                              FROM   dual 
                              CONNECT BY ROWNUM <= 31) b 
                          -- I placed this condition to eliminate dates such as February 31, etc. 
                          -- and it works unless I uncomment the WHERE clause below. 
                          ON b.day <= Extract (day FROM Last_day ( 
                                                        To_date (a.month 
                                                                 || '/1/' 
                                                                 || a.year, 
                                                        'MM/DD/YYYY')))) a 
                left join (SELECT Trunc (a.punch_time) AS date_token 
                           FROM   time_log_temp a 
                           GROUP  BY Trunc (a.punch_time)) b 
                       ON b.date_token = a.date_token 
         ORDER  BY 1, 
                   2) 
SELECT * 
FROM   inter 
-- WHERE  Trunc(date_token, 'MONTH') = '1-JAN-2018' 

1 Ответ

0 голосов
/ 16 октября 2018

Я не уверен, как вы получаете именно эту ошибку из фильтров, которые вы показывали, но в основном проблема заключается в том, что вы сравниваете свою (усеченную) дату со строкой, которая должна быть неявно преобразована вдата для сравнения.Это зависит от ваших настроек NLS, в частности NLS_DATE_FORMAT;и это явно не соответствует строковому формату.

Вместо этого вы должны изменить фильтр для сравнения с датой:

WHERE  Trunc(date_token, 'MONTH') = to_date('1-FEB-2020', 'DD-MON-YYYY')

... которая все еще зависит от вашего NLS_DATE_LANGUAGEдля названия месяца или чуть лучше:

WHERE  Trunc(date_token, 'MONTH') = to_date('01-02-2020', 'DD-MM-YYYY')

или, еще лучше, короче и однозначно, используйте литерал даты ANSI:

WHERE  Trunc(date_token, 'MONTH') = DATE'2020-02-01'

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

WHERE date_token >= DATE '2020-02-01' AND date_token < DATE'2020-03-01'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...