用数据透视表将2行合并为2行

时间:2019-10-17 10:03:04

标签: sql oracle

你好,我尝试将两行合并为一个机构的进出时间。这是我的查询

   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;

这是小提琴http://sqlfiddle.com/#!4/d465a/1 我想将两行合并为一列,该行的进出日期为03-10-2019日期,类似地为04-10-2019日期。那就是为什么我使用分析功能 。但这不会合并成一排。所以显示4行,但我想显示2行。你能帮我吗?

3 个答案:

答案 0 :(得分:1)

您可以通过在表上使用透视图而不使用case表达式来更简单地执行此操作,例如:

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;

还有here's the SQLFiddle支持此答案。

这可以通过查找日期,然后确定当天的第n行进出行来实现。然后,我们对数据进行透视,将rn作为要透视的列之一,这意味着,如果一天中有3来回的出入,那么您将在输出中得到3行。

如果您希望IN和OUT在几天内工作(例如,在午夜之前到达,在午夜之后离开),则将需要其他解决方案。我在回答中假设与IN对应的OUT将始终在同一天。

顺便说一句,为什么将您的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'));

还有here's the supporting SQLFiddle

答案 1 :(得分:1)

您可以简化很多操作,并使用TRUNC代替ROW_NUMBER

Oracle设置

不将日期存储为字符串;使用适当的数据类型,因此在这种情况下,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


更新

  

from comment:代理机构每天都会有很多次进出,所以我使用此案例来获得先进先出的时间

Oracle设置

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

答案 2 :(得分:1)

假设问题中的数据是交错的,则数据透视似乎比必需的更为复杂。您可以只使用分析功能和过滤:

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';

Here是db <>小提琴。