Home > database >  Pivot two columns and keep the values same in sql
Pivot two columns and keep the values same in sql

Time:12-08

I have created a query to get differnt time types and hours

SELECT calc_time.hours             measure,
       calc_time.payroll_time_type elements,
       calc_time.person_id,
       calc_time.start_time
FROM   hwm_tm_rep_work_hours_sum_v calc_time,
       per_all_people_f papf
WHERE  grp_type_id = 200
AND    payroll_time_type IN ( 'Afternoon shift',
                             'TL',
                             'Evening shift',
                             'Regular Pay ',
                             'OT' )
AND    (
              To_date(To_char(calc_time.start_time, 'YYYY-MM-DD') , 'YYYY-MM-DD') BETWEEN To_date(To_char(:From_Date, 'YYYY-MM-DD'), 'YYYY-MM-DD')
              AND    To_date( To_char(:To_Date, 'YYYY-MM-DD'), 'YYYY-MM-DD' ) )
and  papf.person_id = calc_time.person_id

I get the output like -

Start_time     person_id                 elements            measure
01-Jan-2021     198                     Regular Pay           10
01-Jan-2021     198                       OT                  2
01-jAN-2021     198                     Afternoon shift       2
16-JAN-2021     198                     Regular Pay           10
17-JAN-2021     198                     OT                    3
20-JAN-2021     198                     EVENING SHIFT         8


08-JAN-2021     11                      Regular Pay           8
09-JAN-2021     11                        OT                  1
08-JAN-2021     11                          tl                2
10-JAN-2021     12                      Evening shift         9
11-JAN-2021     12                     Evening shift         9

I want this output to be dispplayed as follows WITHIN TWO DATES THAT I PASS AS PARAMETER - LIKE PARAMETER TO AND FROM DATE 01-JAN-2021 AND 31-JAN-2021

 person_id        Regular_pay       OT          OTHER_MEASURE               OTHER_CODE
   198                   20           5             2                       Afternoon shift
   198                   20           5             8                     EVENING SHIFT
   11                     8           1             2                           TL
   12                                               18                        Evening shift
   

So sum of Regular pay and OT IN seperate columns and all others in other_measure and other_code

How can i tweak the main query to achieve this ?

CodePudding user response:

You could try something like this - In your question, unfortunately, it is not clear in which table which columns/values ​​are available.

SELECT 
  calc_time.person_id,
  (select sum(calc_time.start_time) FROM hwm_tm_rep_work_hours_sum_v calc_time where papf.person_id = calc_time.person_id and calc_time.payroll_time_type = 'Regular Pay') as Regular_Pay,
...
FROM   hwm_tm_rep_work_hours_sum_v calc_time,
       per_all_people_f papf
WHERE  grp_type_id = 200
AND    payroll_time_type IN ( 'Afternoon shift',
                             'TL',
                             'Evening shift',
                             'Regular Pay ',
                             'OT' )
AND    (
              To_date(To_char(calc_time.start_time, 'YYYY-MM-DD') , 'YYYY-MM-DD') BETWEEN To_date(To_char(:From_Date, 'YYYY-MM-DD'), 'YYYY-MM-DD')
              AND    To_date( To_char(:To_Date, 'YYYY-MM-DD'), 'YYYY-MM-DD' ) )
and  papf.person_id = calc_time.person_id

-- use a group by
GROUP BY
  calc_time.person_id

CodePudding user response:

You can use:

SELECT *
FROM   (
  SELECT c.person_id,
         SUM(CASE c.payroll_time_type WHEN 'Regular Pay' THEN SUM(c.hours) END)
           OVER (PARTITION BY c.person_id) AS regular_pay,
         SUM(CASE c.payroll_time_type WHEN 'OT' THEN SUM(c.hours) END)
           OVER (PARTITION BY c.person_id) AS OT,
         SUM(c.hours) AS other_measure,
         c.payroll_time_type AS Other_code
  FROM   hwm_tm_rep_work_hours_sum_v c
         INNER JOIN per_all_people_f p
         ON (p.person_id = c.person_id)
  WHERE  grp_type_id = 200
  AND    payroll_time_type IN (
           'Afternoon shift',
           'TL',
           'Evening shift',
           'Regular Pay',
           'OT'
         )
  AND    c.start_time >= TRUNC(:from_date)
  AND    c.start_time <  TRUNC(:to_date)   INTERVAL '1' DAY
  GROUP BY
         c.person_id,
         c.payroll_time_type
)
WHERE   other_code NOT IN ('Regular Pay', 'OT');

Which, for the sample data:

CREATE TABLE hwm_tm_rep_work_hours_sum_v (start_time, person_id, payroll_time_type, hours) AS
SELECT DATE '2021-01-01', 198, 'Regular Pay',     10 FROM DUAL UNION ALL
SELECT DATE '2021-01-01', 198, 'OT',               2 FROM DUAL UNION ALL
SELECT DATE '2021-01-01', 198, 'Afternoon shift',  2 FROM DUAL UNION ALL
SELECT DATE '2021-01-16', 198, 'Regular Pay',     10 FROM DUAL UNION ALL
SELECT DATE '2021-01-17', 198, 'OT',               3 FROM DUAL UNION ALL
SELECT DATE '2021-01-20', 198, 'Evening shift',    8 FROM DUAL UNION ALL
SELECT DATE '2021-01-08',  11, 'Regular Pay',      8 FROM DUAL UNION ALL
SELECT DATE '2021-01-09',  11, 'OT',               1 FROM DUAL UNION ALL
SELECT DATE '2021-01-08',  11, 'TL',               2 FROM DUAL UNION ALL
SELECT DATE '2021-01-10',  12, 'Evening shift',    9 FROM DUAL UNION ALL
SELECT DATE '2021-01-11',  12, 'Evening shift',    9 FROM DUAL;

CREATE TABLE per_all_people_f (person_id, grp_type_id) AS
SELECT 198, 200 FROM DUAL UNION ALL
SELECT  11, 200 FROM DUAL UNION ALL
SELECT  12, 200 FROM DUAL;

Outputs:

PERSON_ID REGULAR_PAY OT OTHER_MEASURE OTHER_CODE
11 8 1 2 TL
12 18 Evening shift
198 20 5 2 Afternoon shift
198 20 5 8 Evening shift

db<>fiddle here

CodePudding user response:

You may use aggregation and then apply model clause to calculate the required columns. Below is the code with comments, assuming you can manage filter by dates.

select *
from t
PERSON_ID | ELEMENTS        | MEASURE
--------: | :-------------- | ------:
      198 | Regular Pay     |       1
      198 | Regular Pay     |       2
      198 | Afternoon shift |       3
      198 | Afternoon shift |       4
      198 | OT              |       5
      198 | OT              |       6
      198 | EVENING SHIFT   |       7
      198 | EVENING SHIFT   |       8
       11 | Regular Pay     |      11
       11 | Regular Pay     |      12
       11 | TL              |      13
       11 | TL              |      14
       11 | EVENING SHIFT   |      15
       11 | EVENING SHIFT   |      16
       12 | TL              |      21
       12 | TL              |      22
       12 | EVENING SHIFT   |      23
       12 | EVENING SHIFT   |      24
select
  person_id,
  ot,
  regular_pay,
  elements as other_code,
  mes as other_measure
from (
  /*First you need to aggregate all the measures by person_id and code*/
  select
    person_id,
    elements,
    sum(measure) as mes
  from t
  /*Date filter goes here*/
  group by
    person_id,
    elements
)
model
  /*RETURN UPDATED ROWS
   will do the trick,
  because we'll update only "other"
  measures, so OT and Regular pay will no go
  to the output*/
  return updated rows
  /*Where to break the calculation*/
  partition by (person_id)
  /*To be able to reference by code*/
  dimension by (elements)
  measures (
    mes,
    0 as ot,
    0 as regular_pay
  )
  rules upsert (
    ot[
      elements not in ('OT', 'Regular Pay')
    ] = sum(mes)['OT'],
    regular_pay[
      elements not in ('OT', 'Regular Pay')
    ] = sum(mes)['Regular Pay']
  )
PERSON_ID |   OT | REGULAR_PAY | OTHER_CODE      | OTHER_MEASURE
--------: | ---: | ----------: | :-------------- | ------------:
      198 |   11 |           3 | EVENING SHIFT   |            15
      198 |   11 |           3 | Afternoon shift |             7
       11 | null |          23 | TL              |            27
       11 | null |          23 | EVENING SHIFT   |            31
       12 | null |        null | TL              |            43
       12 | null |        null | EVENING SHIFT   |            47

db<>fiddle here

  • Related