У меня есть поле c_days
в таблице my_table
, которое принимает числовые значения от 1 до 31.
В этом поле числа от 1 до 9 являются однозначными.
Пишу условие, если c_day
больше сегодняшнего, то нужно отображать c_day
в формате даты to_date
, если меньше, то отображать c_day
в формате даты, только следующий месяц.
Например, c_day
равно 14, а сегодня у нас 8 число, значит нужно вывести дату 14.02.2023, Если c_day
равно 5, то нужно вывести дату следующего месяца 05.03.2023
Я сделал что-то вроде этого:
SELECT
C_DAY,
CASE
WHEN C_DAY >= TO_CHAR(SYSDATE, 'DD') THEN
TO_DATE(C_DAY || '.' || TO_CHAR(SYSDATE, 'MM') || '.' || TO_CHAR(SYSDATE, 'YYYY'), 'dd.mm.yyyy')
WHEN C_DAY < TO_CHAR(SYSDATE, 'DD') THEN
TO_DATE(C_DAY || '.' || TO_CHAR(SYSDATE, 'MM') || '.' || TO_CHAR(SYSDATE, 'YYYY'), 'dd.mm.yyyy')
WHEN C_DAY IS NULL THEN
null
END AS new_field
FROM my_table
Проблема в том, что конечный результат не преобразуется в формат даты, я думал, что это причина того, что даты могут отображаться как 1.03.2023, 7.03.2023, поэтому я попытался преобразовать его в
TO_CHAR(C_DAY, 'fm00')
и сделал это:
SELECT
C_DAY,
CASE
WHEN TO_CHAR(C_DAY, 'fm00') >= TO_CHAR(SYSDATE, 'DD') THEN
TO_DATE(TO_CHAR(C_DAY, 'fm00') || '.' || TO_CHAR(SYSDATE, 'MM') || '.' || TO_CHAR(SYSDATE, 'YYYY'), 'dd.mm.yyyy')
WHEN TO_CHAR(C_DAY, 'fm00') < TO_CHAR(SYSDATE, 'DD') THEN
TO_DATE(TO_CHAR(C_DAY, 'fm00') || '.' || TO_CHAR(SYSDATE, 'MM') || '.' || TO_CHAR(SYSDATE, 'YYYY'), 'dd.mm.yyyy')
WHEN C_DAY IS NULL THEN
null
END AS new_field
FROM my_table
Но он даже не работает, он показывает ошибку ора
Конечно, оно преобразуется в значение DATE
, иначе вы получите ошибку. Возможно, вас не устраивает формат отображения (по умолчанию).
Вот один из вариантов:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> WITH
2 my_table (c_day)
3 AS
4 (SELECT 14 FROM DUAL
5 UNION ALL
6 SELECT 5 FROM DUAL
7 UNION ALL
8 SELECT 30 FROM DUAL)
9 SELECT c_day,
10 TO_DATE (
11 LPAD (LEAST (c_day, TO_CHAR (LAST_DAY (SYSDATE), 'dd')), 2, '0')
12 || '.'
13 || TO_CHAR (
14 CASE
15 WHEN c_day < TO_CHAR (SYSDATE, 'dd')
16 THEN
17 ADD_MONTHS (SYSDATE, 1)
18 ELSE
19 SYSDATE
20 END,
21 'mm.yyyy'),
22 'dd.mm.yyyy') AS result
23 FROM my_table;
C_DAY RESULT
---------- ----------
14 14.02.2023
5 05.03.2023
30 28.02.2023
SQL>
еще одна ошибка ora-01858
Кстати, бывает и такой случай, февраль заканчивается 28 числа, и если c_day равно 29, 30, 31, может из-за этого у меня вылетает ошибка, как этого избежать?
Что вы хотите сделать в таком случае?
Я установил его на последний день текущего месяца; см. отредактированный код.
Я не знаю почему, но когда я пытаюсь использовать ваш выбор с моей основной таблицей, я получаю сообщение об ошибке ORA-01858: нечисловой символ был найден там, где ожидался числовой. Я вывожу все уникальные значения, это числа от 1 до 31 и null
Вы уверены, что использовали именно тот код, который я разместил? Похоже, вы использовали другую модель формата даты (например, используя ИМЯ месяца (например, ДД-МОН-ГГГГ), а не его номер (например, ДД.ММ.ГГГГ)).
Давайте продолжим обсуждение в чате.
Насколько я понимаю ваш вопрос, одним из решений может быть это
SELECT
C_DAY,
CASE
WHEN C_DAY >= TO_CHAR(SYSDATE, 'DD') THEN
ADD_MONTHS(TO_DATE(TO_CHAR(SYSDATE, 'YYYY-MM-')||C_DAY , 'YYYY-MM-DD'), 1)
WHEN C_DAY < TO_CHAR(SYSDATE, 'DD') THEN
TO_DATE(TO_CHAR(SYSDATE, 'YYYY-MM-')||C_DAY , 'YYYY-MM-DD')
END AS new_field
FROM my_table
Функция ADD_MONTHS
работает следующим образом:
Если
date
является последним днем месяца или если в результирующем месяце меньше дней, чем дневной компонентdate
, то результатом является последний день результирующего месяца. В противном случае результат имеет тот же компонент дня, что иdate
.
Я думаю, что компонент «день» должен отражать значение столбца C_DAY, а не SYSDATE.
@Littlefoot правильно, я обновил его. Но все еще нуждается в тонкой настройке, если C_DAY
больше, чем количество дней в текущем месяце. Ваш ответ лучше!
Теперь, кхм, это не сработает, если C_DAY = 30 (поскольку в феврале не так уж много дней) :)
Вы можете сделать это без каких-либо преобразований строки в дату (или наоборот), используя:
SELECT C_DAY,
LEAST(
ADD_MONTHS(
TRUNC(SYSDATE, 'MM'),
CASE WHEN c_day <= EXTRACT(DAY FROM SYSDATE) THEN 0 ELSE 1 END
) + c_day - 1,
LAST_DAY(
ADD_MONTHS(
TRUNC(SYSDATE, 'MM'),
CASE WHEN c_day <= EXTRACT(DAY FROM SYSDATE) THEN 0 ELSE 1 END
)
)
) AS new_field
FROM my_table
Что для примера данных:
CREATE TABLE my_table(c_day) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 31;
Выходы:
Все работает, спасибо чувак, у меня такой же вопрос, можешь помочь?
Посмотрите на свой код. Независимо от того, больше ли
C_DAY
, чемTO_CHAR(SYSDATE, 'DD')
или нет, вы возвращаете одно и то же выражение! Вы также можете написать это корочеTO_DATE(C_DAY || TO_CHAR(SYSDATE, '.MM.YYYY'), 'DD.MM.YYYY')