Объединить 2 строки в один с 2 столбцами с помощью оси - PullRequest
0 голосов
/ 17 октября 2019

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

   select
   agency_name,       
   "IN",
   out
   from   (
       select 
        eofficeuat.entrylog_vehicle.agent_id,

        eofficeuat.cnf_agents.agent_name,
        eofficeuat.gatepass.agency_name,
        eofficeuat.gatepass.agency_id,
        TO_CHAR(eofficeuat.entrylog_vehicle.scantime, 'dd-mm-yyyy HH12:MI:SS PM') as Time,            
        eofficeuat.entrylog_vehicle.action as action,
        eofficeuat.gatelist.shortname as gate,
        eofficeuat.entrylog_vehicle.passnumber,
        eofficeuat.entrylog_vehicle.cardnumber,
        eofficeuat.gatepass.vehicletype,
        eofficeuat.gatepass.ISSUEDATETIME,
        row_number() over (
        partition by agency_name, trunc(to_date(TO_CHAR(eofficeuat.entrylog_vehicle.scantime, 'dd-mm-yyyy HH12:MI:SS PM'), 'dd-mm-yyyy hh:mi:ss pm')), action order by eofficeuat.entrylog_vehicle.scantime) rn
    FROM
        eofficeuat.entrylog_vehicle
        INNER JOIN eofficeuat.cnf_agents ON eofficeuat.entrylog_vehicle.agent_id = eofficeuat.cnf_agents.agent_id
        INNER JOIN eofficeuat.gatelist ON eofficeuat.entrylog_vehicle.gate_id = eofficeuat.gatelist.id
        INNER JOIN eofficeuat.gatepass ON eofficeuat.entrylog_vehicle.passnumber = eofficeuat.gatepass.id

    WHERE 

        eofficeuat.entrylog_vehicle.passnumber = '10000920616'
        and eofficeuat.entrylog_vehicle.scantime between TO_DATE ('03/10/2019', 'dd/mm/yyyy') and TO_DATE ('04/10/2019', 'dd/mm/yyyy') 


    )
  pivot (max(time) for action in ('IN' as "IN", 'OUT' as "OUT"))
  order by rn;

, а вот Fiddle http://sqlfiddle.com/#!4/d465a/1 Я хотел объединить две строки в одну, которая имеет вход и выход на дату 03-10-2019 и аналогично на 04-10-Дата 2019Вот почему я использовал функцию аналитики. но это не слияние в один ряд. так что там показаны 4 строки, но я хотел показать 2 строки. не могли бы вы помочь мне в этом?

Ответы [ 3 ]

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

Вы можете значительно упростить его и использовать TRUNC вместо ROW_NUMBER:

Oracle Setup :

Не хранить даты какстроки;используйте соответствующие типы данных, поэтому в этом случае DATE:

create table kvtest ( agency_name, action, TIME ) AS
SELECT 'ABC LIMITED', 'IN',  DATE '2019-10-03' + INTERVAL '15:52:26' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'OUT', DATE '2019-10-03' + INTERVAL '18:17:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'IN',  DATE '2019-10-04' + INTERVAL '15:52:26' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'OUT', DATE '2019-10-04' + INTERVAL '18:17:48' HOUR TO SECOND FROM DUAL;

Запрос :

SELECT agency_name,
       "IN",
       OUT
FROM   (
  SELECT T.*,
         TRUNC( TIME ) AS day
  FROM   kvtest t
)
PIVOT ( MIN( time ) FOR action IN ( 'IN' AS "IN", 'OUT' AS "OUT" ) )
ORDER BY day;

Вывод :

| AGENCY_NAME |                   IN |                  OUT |
|-------------|----------------------|----------------------|
| ABC LIMITED | 2019-10-03T15:52:26Z | 2019-10-03T18:17:48Z |
| ABC LIMITED | 2019-10-04T15:52:26Z | 2019-10-04T18:17:48Z |

SQL Fiddle


Обновление :

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

Oracle Setup :

create table kvtest ( agency_name, action, TIME ) AS
SELECT 'ABC LIMITED', 'IN',  DATE '2019-10-03' + INTERVAL '15:52:26' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'OUT', DATE '2019-10-03' + INTERVAL '18:17:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'IN',  DATE '2019-10-03' + INTERVAL '18:52:26' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'OUT', DATE '2019-10-03' + INTERVAL '19:17:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'IN',  DATE '2019-10-04' + INTERVAL '15:52:26' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'OUT', DATE '2019-10-04' + INTERVAL '18:17:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'IN',  DATE '2019-10-05' + INTERVAL '23:17:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'OUT', DATE '2019-10-06' + INTERVAL '00:17:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'IN',  DATE '2019-10-06' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 'ABC LIMITED', 'OUT', DATE '2019-10-06' + INTERVAL '15:00:00' HOUR TO SECOND FROM DUAL;

Запрос :

SELECT agency_name,
       MIN( CASE action WHEN 'IN'  THEN TIME END ) AS "IN",
       MAX( CASE action WHEN 'OUT' THEN TIME END ) AS OUT
FROM   kvtest 
GROUP BY agency_name, TRUNC( TIME )
ORDER BY agency_name, TRUNC( TIME );

Вывод :

| AGENCY_NAME |                   IN |                  OUT |
|-------------|----------------------|----------------------|
| ABC LIMITED | 2019-10-03T15:52:26Z | 2019-10-03T19:17:48Z |
| ABC LIMITED | 2019-10-04T15:52:26Z | 2019-10-04T18:17:48Z |
| ABC LIMITED | 2019-10-05T23:17:48Z |               (null) |
| ABC LIMITED | 2019-10-06T09:00:00Z | 2019-10-06T15:00:00Z |

SQLFiddle

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

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

select agency_name, time as in_time, out_time
from (select k.*,
             min(case when "action" = 'OUT' then time end) over
                  (partition by agency_name
                   order by time desc
                  ) as out_time
      from kvtest k
     ) k
where "action" = 'IN';

Здесь - это db <> скрипка.

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

Вы можете сделать это намного проще, используя сводную таблицу в таблице без выражений, например:

select agency_name,
       dt,
       "IN",
       out
from   (select agency_name,
               trunc(to_date(time, 'dd-mm-yyyy hh:mi:ss pm')) dt,
               to_date(time, 'dd-mm-yyyy hh:mi:ss pm') time,
               action,
               row_number() over (partition by agency_name, trunc(to_date(time, 'dd-mm-yyyy hh:mi:ss pm')), action order by time) rn
        from   kvtest)
pivot (max(time) for action in ('IN' as "IN", 'OUT' as "OUT"))
order by rn;

И вот SQLFiddle для поддержки этого ответа.

Это работает, находя день, а затем идентифицируя n-ую строку входа и выхода в этот день. Затем мы поворачиваем данные, оставляя rn одним из столбцов для поворота, что означает, что если бы в день было 3 входа и выхода, в результате вы бы получили 3 строки.

Есливы хотите, чтобы входы и выходы работали в течение нескольких дней (например, приходите до полуночи, уходите после полуночи), вам нужно другое решение. В своем ответе я предположил, что соответствующий OUT для IN всегда будет в один и тот же день.

Кстати, почему ваш столбец TIME определяется как VARCHAR2? Это плохой тип данных для хранения информации о дате или времени. Я надеюсь, что ваша таблица действительно имеет тип данных DATE (или TIMESTAMP). Если это не так, я настоятельно рекомендую вам взглянуть на изменение типа данных на более подходящий.


Если вы просто хотите получить самое раннее время IN и самое последнее время OUT в день, все, что вам нужно, этоусловный агрегатный запрос, например:

select agency_name,
       trunc(to_date(time, 'dd-mm-yyyy hh:mi:ss pm')) dt,
       min(case when action = 'IN' then to_date(time, 'dd-mm-yyyy hh:mi:ss pm') end) "IN",
       max(case when action = 'OUT' then to_date(time, 'dd-mm-yyyy hh:mi:ss pm') end) out
from   kvtest
group by agency_name,
         trunc(to_date(time, 'dd-mm-yyyy hh:mi:ss pm'));

и вот вспомогательный SQLFiddle .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...