Oracle sql как получить даты начала и окончания недель между двумя датами

Я хочу знать дни начала и окончания каждой недели между двумя датами. Формат даты: дд / мм / ггг чч24: ми: сс. Мне нужны недели в формате дд / мм / гггг чч24: ми: сс, потому что мне нужно вычислить дни и часы между начальным и конечным днем ​​недели с указанием времени

Я написал это заявление

WITH 
date_range AS (

SELECT
            pdm.des_comercial serie,
                pdm.id_material codserie,
                ri.id_accion intervencion,
                TO_CHAR(NVL(ri.fecha_salida_rev, SYSDATE), 'dd/mm/RRRR') fecha1,
                to_char((CASE
                    WHEN ri.fecha_salida_rev > TO_DATE('18/06/2019', 'dd/mm/yyyy') THEN TO_DATE('18/06/2019', 'dd/mm/yyyy')
                    WHEN ri.fecha_salida_Rev IS NULL THEN TO_DATE('18/06/2019', 'dd/mm/yyyy')
                    ELSE ri.fecha_salida_Rev
                END),'dd/mm/yyyy hh24:mi:ss') fechasalida,
                to_char((CASE
                    WHEN ri.fecha_entrada_rev < TO_DATE('01/06/2019', 'dd/mm/yyyy') THEN TO_DATE('01/06/2019', 'dd/mm/yyyy')
                    ELSE ri.fecha_entrada_Rev
                END),'dd/mm/yyyy hh24:mi:ss') fechaentrada
                ,
                ri.cod_taller_rev,
                ri.COD_MATRICULA,
                ri.fecha_entrada_rev start_date,
                ri.fecha_salida_rev end_date
            FROM
                r_intervencion ri,
                planificador.pl_dh_material pdm
            WHERE
                ri.id_accion = ri.amortizada_por
                AND ri.causa_entrada = 1
                AND ri.tipo_accion = 1
                AND pdm.id_material = ri.cod_serie
                AND pdm.hasta = 99999999
                AND ri.ID_ACCION = 'IM4'
                AND ri.fecha_salida_rev BETWEEN TO_DATE('01/06/2019', 'dd/mm/yyyy') AND TO_DATE('18/06/2019', 'dd/mm/yyyy')
                 ),

semanas AS (            


SELECT  LEVEL "Week"

     ,to_char(to_date(start_date,'dd/mm/yyyy hh24:mi:ss') + (7 * (LEVEL - 1)),'IW') startweek

       ,to_char(to_date(start_date ,'dd/mm/yyyy hh24:mi:ss')+ (7 * (LEVEL - 1)),'IW') + 6 endweek

       ,TO_CHAR(start_date + (7 * (LEVEL - 1)),'IW') "Iso Week",
       serie,
       codserie,
       intervencion,
       cod_taller_rev,
       cod_matricula,
       fechaentrada,
       fechasalida,
       start_date,
       end_date

FROM   date_range
CONNECT  BY LEVEL <= (to_char(To_date(end_date,'dd/mm/yyyy hh24:mi:ss'),'IW') - to_char(To_date(start_date,'dd/mm/yyyy hh24:mi:ss'),'IW')) / 7 + 1 

) 

SELECT startweek,
endweek,
to_date(endweek,'dd/mm/yyyy hh24:mi:ss') - to_date(startweek,'dd/mm/yyyy hh24:mi:ss')  dias,
serie,
codserie,
intervencion,
cod_taller_rev,
cod_matricula,
start_Date,
end_date,
fechaentrada,
fechasalida,
rd.descripcion
FROM semanas,r_depositos rd
WHERE cod_taller_rev = rd.cod_deposito

Когда я выполняю это, я получаю

Ошибка выполнения запроса

SQL Error [1840] [22008]: ORA-01840: ORA-01840: input value not long enough for date format

Ошибка в

 ,to_char(to_date(start_date,'dd/mm/yyyy hh24:mi:ss') + (7 * (LEVEL - 1)),'IW') startweek

       ,to_char(to_date(start_date ,'dd/mm/yyyy hh24:mi:ss')+ (7 * (LEVEL - 1)),'IW') + 6 endweek

Как получить начало и конец недели в формате дд / мм / гггг чч24: ми: сс

РЕДАКТИРОВАТЬ

start_date                   end_date

20/05/2019 20:00:00         05/06/2019  08:00:00 

недели

20/05/2019 20:00:00 26/05/2019  -> 6 days and xxx hours
27/05/2019          02/06/2019 -> 7 days
03/06/2019          05/06/2019 08:00:00 -> 3 days and xxx hours

Мне нужно рассчитать разницу в днях и часах для каждой недели.

For example between 20/05/2019 20:00:00 and 26/05/2019
and last one between 03/06/2019 and 05/06/2019 08:00:00

Моя проблема связана с расчетом

to_date(endweek,'dd/mm/yyyy hh24:mi:ss') - to_date(startweek,'dd/mm/yyyy hh24:mi:ss')  dias,

endweek and startweek have to have dd/mm/yyyy hh24:mi:ss

person Carlota    schedule 19.06.2019    source источник
comment
Предположительно start_date (или fecha_entrada_rev) уже имеет тип данных DATE, поэтому вы не должны вызывать to_date() для этого (вы делаете неявный to_char() с настройками NLS, который затем не соответствует формату, который вы пытаетесь использовать для обратного преобразования )? Ваш подход кажется ошибочным: даты не имеют внутреннего удобочитаемого формата, даже если ваш клиент показывает их в указанном вами формате. Если вы хотите выполнить манипуляции и сравнение, оставьте их как даты - конвертируйте только в форматированные строки для отображения.   -  person Alex Poole    schedule 19.06.2019
comment
Тогда у меня вопрос. Как я могу написать to_char (to_date (start_date, 'dd / mm / yyyy hh24: mi: ss') + (7 * (LEVEL - 1)), 'IW') startweek без to_char, потому что мне нужно извлечь (7 * ( 1-Й УРОВЕНЬ))   -  person Carlota    schedule 19.06.2019
comment
Я не уверен, что понимаю. to_char(start_date + (7 * (LEVEL - 1)),'IW') даст вам номер недели ISO, что вы уже делаете позже. Может быть, вы хотите вместо этого trunc(start_date, 'IW') получить реальное начало этой недели?   -  person Alex Poole    schedule 19.06.2019
comment
Я не могу выполнить trunc (start_date), потому что мне нужно выполнить вычисления с start_date в формате 'dd / mm / yyyy hh24: mi: ss'   -  person Carlota    schedule 19.06.2019
comment
Даты не имеют формата. например trunc(sysdate, 'IW') может отображаться клиентом как 17-июн-19, 2019-06-17, 2019-06-17 00:00:00, 17.06.2019 00:00:00, 17.06.2019 00:00 : 00 или множество других в зависимости от клиента и настроек NLS (включая другие языки, если вы используете названия / сокращения месяцев). Но это не имеет ничего общего с использованием усеченной даты в расчетах. (Было бы полезно, если бы вы предоставили минимальный воспроизводимый пример с образцами данных и результатов, например, со столбцами, не имеющими отношения к вопросу удаленный.)   -  person Alex Poole    schedule 19.06.2019
comment
Я редактировал пример данных   -  person Carlota    schedule 19.06.2019


Ответы (2)


Моя проблема связана с расчетом

to_date(endweek,'dd/mm/yyyy hh24:mi:ss') - to_date(startweek,'dd/mm/yyyy hh24:mi:ss')  dias,

endweek and startweek have to have dd/mm/yyyy hh24:mi:ss

Даты Oracle хранятся во внутреннем формате, о котором обычно не нужно беспокоиться. Ваше приложение или клиент форматирует дату в виде строки в соответствии с его собственными настройками или настройками NLS сеанса.

Когда вы делаете что-то вроде:

to_date(endweek,'dd/mm/yyyy hh24:mi:ss')

ты действительно делаешь:

to_date(to_char(endweek),'dd/mm/yyyy hh24:mi:ss')

и поскольку для неявного вызова to_char() не указана явная маска формата, используются настройки NLS текущего сеанса. В зависимости от настройки может возникнуть ошибка; или может повредить значение - например, смешивание масок YY и YYYY может привести к потере века, преобразовав 2019 в 0019. (Учитывая получаемую вами ошибку, ваши настройки NLS могут быть необычными?).

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

У Oracle есть и другие функции для управления значениями даты, в том числе trunc(), поэтому я думаю, вам может понадобиться что-то вроде этого - показывая разницу тремя способами, хотя есть и другие, и вы можете отформатировать последний, как хотите:

with date_range (start_date, end_date) as (
  -- dummy data from your example
  select to_date('20/05/2019 20:00:00', 'DD/MM/YYYY HH24;MI:SS') as start_date,
    to_date('05/06/2019 08:00:00', 'DD/MM/YYYY HH24;MI:SS') as end_date
  from dual
),
semanas as (            
  select level as week,
    start_date,
    end_date,
    greatest(trunc(start_date + (7 * (level - 1)), 'IW'), start_date) as start_week,
    least(trunc(start_date + (7 * level), 'IW'), end_date) as end_week
  from date_range
  connect by level <= (trunc(end_date, 'IW') - trunc(start_date, 'IW')) / 7 + 1
)
select week,
  to_char(start_week, 'IW') as iso_week,
  to_char(start_week, 'DD/MM/YYYY HH24:MI:SS') as start_week,
  to_char(end_week, 'DD/MM/YYYY HH24:MI:SS') as end_week,
  end_week - start_week as diff_num,
  numtodsinterval(end_week - start_week, 'DAY') as diff_interval,
  to_char(date '1999-12-31' + (end_week - start_week), 'FMDD "days" HH24 "hours"') as diff_words
from semanas;

      WEEK IS START_WEEK          END_WEEK              DIFF_NUM DIFF_INTERVAL       DIFF_WORDS      
---------- -- ------------------- ------------------- ---------- ------------------- ----------------
         1 21 20/05/2019 20:00:00 27/05/2019 00:00:00 6.16666667 +06 04:00:00.000000 6 days 4 hours  
         2 22 27/05/2019 00:00:00 03/06/2019 00:00:00          7 +07 00:00:00.000000 7 days 0 hours  
         3 23 03/06/2019 00:00:00 05/06/2019 08:00:00 2.33333333 +02 08:00:00.000000 2 days 8 hours  

Как написано в настоящее время, connect by работает правильно, только если date_range CTE генерирует единственное значение; если вы действительно получаете несколько строк из своего реального запроса, вам придется проделать немного больше работы, или переключиться на рекурсивные CTE, или перекрестное соединение / применение, в зависимости от вашей версии Oracle.

person Alex Poole    schedule 19.06.2019

Ваш endweek расчет

to_char(to_date(start_date ,'dd/mm/yyyy hh24:mi:ss')+ (7 * (LEVEL - 1)),'IW') + 6 endweek

Это пытается добавить число 6 к строке символов. Я подозреваю, что вы хотели

to_char(to_date(start_date ,'dd/mm/yyyy hh24:mi:ss') + (7 * (LEVEL - 1) + 6),'IW') endweek

Здесь я переместил + 6, поэтому вы добавляете 6 к значению даты, а не к строке символов.

person Bob Jarvis - Reinstate Monica    schedule 19.06.2019