将两行合并为两列,并以枢轴合并为两列

问题描述 投票:0回答:3

您好,我试图将两行合并为一列,并为代理商分配了进出时间。这是我的查询

with rws as (

SELECT 
RowNum as sr,   
agency_name, 
action,
TIME

FROM
(
  SELECT T.*,
        CASE
        WHEN T.ACTION = 'IN' THEN ROW_NUMBER() OVER(
            PARTITION BY agency_name, TRUNC(TO_DATE(TIME, 'dd-mm-yyyy hh12:mi:ss PM'))
            ORDER BY
            TO_DATE(TIME, 'dd-mm-yyyy hh12:mi:ss PM')
        )
        END AS IN_TIME_RN,
        CASE
        WHEN T.ACTION = 'OUT' THEN ROW_NUMBER() OVER(
            PARTITION BY agency_name, TRUNC(TO_DATE(TIME, 'dd-mm-yyyy hh12:mi:ss PM'))
            ORDER BY TO_DATE(TIME, 'dd-mm-yyyy hh12:mi:ss PM')
            DESC
        )
        END AS OUT_TIME_RN


  FROM
  (
        SELECT                 
        agency_name,Time,action

    FROM
        kvtest         
    )T
  )
  WHERE
  CASE
  WHEN ACTION = 'IN' THEN IN_TIME_RN
  ELSE OUT_TIME_RN
  END = 1 
  )


  select * from rws

  pivot ( 
  min ( time ) for action in (
  'IN' , 'OUT'  
  )
  );

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

sql oracle
3个回答
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


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 <>小提琴。


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

© www.soinside.com 2019 - 2024. All rights reserved.