PIVOT by timestamp - sql

I need to pivot the time (hour) for the results of this query, so there could be up to 24 hours running across the top as you can replicate in an Excel pivot table. Is there an easy and efficient way to replicate this in the query?
Query:
SELECT
trunc(complete_dstamp) "Date",
to_char(complete_dstamp, 'HH24') "Hour",
user_id "User",
sum(update_qty) "Qty"
FROM
inventory_transaction
WHERE
to_loc_id = 'CONTAINER'
and trunc(complete_dstamp) > (
trunc(current_timestamp)-1
)
GROUP BY
trunc(complete_dstamp),
to_char(complete_dstamp, 'HH24'),
user_id
ORDER BY
1,
2
Desired output:
Current output:

Use conditional aggregation:
SELECT TO_CHAR(complete_dstamp, 'YYYY-MM-DD') AS "Date",
user_id AS "User",
SUM(CASE TO_CHAR(complete_dstamp, 'HH24') WHEN '00' THEN update_qty END) AS hour_0,
SUM(CASE TO_CHAR(complete_dstamp, 'HH24') WHEN '01' THEN update_qty END) AS hour_1,
SUM(CASE TO_CHAR(complete_dstamp, 'HH24') WHEN '02' THEN update_qty END) AS hour_2,
SUM(CASE TO_CHAR(complete_dstamp, 'HH24') WHEN '03' THEN update_qty END) AS hour_3,
SUM(CASE TO_CHAR(complete_dstamp, 'HH24') WHEN '04' THEN update_qty END) AS hour_4,
-- ...
SUM(CASE TO_CHAR(complete_dstamp, 'HH24') WHEN '23' THEN update_qty END) AS hour_23,
sum(update_qty) AS grand_total
FROM inventory_transaction
WHERE to_loc_id = 'CONTAINER'
AND complete_dstamp >= trunc(current_timestamp)
GROUP BY
TO_CHAR(complete_dstamp, 'YYYY-MM-DD'),
user_id
ORDER BY
"Date",
"User"
Or a PIVOT:
SELECT "Date",
user_id AS "User",
hour_0,
hour_1,
hour_2,
hour_3,
hour_4,
-- ...
hour_23,
COALESCE(hour_0, 0)
+ COALESCE(hour_1, 0)
+ COALESCE(hour_2, 0)
+ COALESCE(hour_3, 0)
+ COALESCE(hour_4, 0)
-- ...
+ COALESCE(hour_23, 0) AS grand_total
FROM (
SELECT TO_CHAR(complete_dstamp, 'YYYY-MM-DD') AS "Date",
TO_CHAR(complete_dstamp, 'HH24') AS hour,
user_id,
update_qty
FROM inventory_transaction
WHERE to_loc_id = 'CONTAINER'
AND complete_dstamp >= trunc(current_timestamp)
)
PIVOT (
SUM(update_qty) FOR hour IN (
'00' AS hour_0,
'01' AS hour_1,
'02' AS hour_2,
'03' AS hour_3,
'04' AS hour_4,
-- ...
'23' AS hour_23
)
)
db<>fiddle here

Related

SQL Query to give output in one row

I have created the below query -
select person_number
,sal.current_salary
,(CASE WHEN '2021-03-31' between to_char(sal.date_From ,'YYYY-MM-DD')
AND to_char(sal.date_to ,'YYYY-MM-DD')
THEN SALARY_AMOUNT
else
null
END) "2019_sal"
,(CASE WHEN '2020-03-31' between to_char(sal.date_From ,'YYYY-MM-DD')
AND to_char(sal.date_to ,'YYYY-MM-DD')
THEN SALARY_AMOUNT
else
null
END) "2020_sal"
,(CASE WHEN '2019-03-31' between to_char(sal.date_From ,'YYYY-MM-DD')
AND to_char(sal.date_to ,'YYYY-MM-DD')
THEN SALARY_AMOUNT
else
null
END) "2019_sal"
from per_all_people_f papf,
comp_salary sal
where papf.person_id = sal.person_id
and papf.person_number = :p_emp_number
and trunc(sysdate) between papf.effective_start_date and papf.effective_end_date
This gives me output like -
Person_number Current_salary 2019_sal 2020_sal
10 292929
10 287226
10 300000
11 282726
11 278090
Is there anyway i can tweak the above query to give me the output like -
Person_number Current_salary 2019_sal 2020_sal
10 292929 287226 300000
11 282726 278090
I.e. get the data against and employee in a single row. Can I use any function for this ?
Use simple aggregation to get the "MAX" value in each column for each person then group by that person. This will collapse each value into 1 record for each person number. It does however assume there will always only be 1 value for each column for a given person_number. Based on sample data; that appears to be the case.
select person_number
,max(sal.current_salary) "current_Salary"
,max(CASE WHEN '2021-03-31' between to_char(sal.date_From ,'YYYY-MM-DD')
AND to_char(sal.date_to ,'YYYY-MM-DD')
THEN SALARY_AMOUNT
else
null
END) "2019_sal"
,max(CASE WHEN '2020-03-31' between to_char(sal.date_From ,'YYYY-MM-DD')
AND to_char(sal.date_to ,'YYYY-MM-DD')
THEN SALARY_AMOUNT
else
null
END) "2020_sal"
,max(CASE WHEN '2019-03-31' between to_char(sal.date_From ,'YYYY-MM-DD')
AND to_char(sal.date_to ,'YYYY-MM-DD')
THEN SALARY_AMOUNT
else
null
END) "2019_sal"
from per_all_people_f papf,
comp_salary sal
where papf.person_id = sal.person_id
and papf.person_number = :p_emp_number
and trunc(sysdate) between papf.effective_start_date and papf.effective_end_date
GROUP BY person_number

UNION ALL IS NOT WORKING WITH GROUP BY IN SQL

I have to create a query that shows whether the employees has taken what kind of leave on time card or if they have taken any leave "Vacation".
The output should look like:
SELECT person_number,
Sum(
CASE
WHEN element = 'Overtime' THEN measure
END) AS overtime_measure_hours,
Sum(
CASE
WHEN elements LIKE 'Regular Pay' THEN measure
END) AS regular_measure_hours,
Sum(
CASE
WHEN elements IN ( 'Double_Time' ) THEN measure
END) AS Other_amount,
Max(
CASE
WHEN elements IN ( 'Double_Time' ) THEN 'Double'
END) AS Other_code
(
SELECT papf.person_number,
atrb.attribute_category element,
atrb.measure measure_hours,
rec.start_date,
rec.end_date
FROM per_all_people_f papf,
hwm_tm_rec rec,
fusion.hwm_tm_rep_atrbs atrb,
fusion.hwm_tm_rep_atrb_usages ausage,
hwm_tm_statuses status
WHERE 1=1
AND atrb.tm_rep_atrb_id = ausage.tm_rep_atrb_id
AND ausage.usages_source_id = rec.tm_rec_id
AND ausage.usages_source_version = rec.tm_rec_version
AND status.tm_bldg_blk_id = rec.tm_rec_id
AND status.tm_bldg_blk_version = rec.tm_rec_version
AND rec.tm_rec_type IN ( 'RANGE',
'MEASURE' )
AND papf.person_number = '101928'
AND trunc (status.date_to) = to_date ('31/12/4712', 'DD/MM/YYYY')
--and atrb.attribute_category in( 'Overtime','Regular Pay', 'Double_Time')
AND trunc (sh21.start_time) BETWEEN trunc(:P_From_Date) AND trunc(:P_To_Date) )
GROUP BY person_number
UNION ALL
SELECT person_number,
sum(
CASE
WHEN element = 'Overtime' THEN measure
END) AS overtime_measure_hours,
sum(
CASE
WHEN elements LIKE 'Regular Pay' THEN measure
END) AS regular_measure_hours,
sum(
CASE
WHEN absence_name IN ( 'Vacation' ) THEN abs_duration
END) AS Other_amount,
max(
CASE
WHEN absence_name IN ( 'Vacation' ) THEN absence_name
END) AS Other_code
(
SELECT papf.person_number,
atrb.attribute_category element,
atrb.measure measure_hours,
rec.start_date,
rec.end_date,
abs.NAME absence_name,
abs.duration abs_duration
FROM per_all_people_f papf,
hwm_tm_rec rec,
fusion.hwm_tm_rep_atrbs atrb,
fusion.hwm_tm_rep_atrb_usages ausage,
hwm_tm_statuses status,
anc_absence_types_vl abs
WHERE 1=1
AND atrb.tm_rep_atrb_id = ausage.tm_rep_atrb_id
AND ausage.usages_source_id = rec.tm_rec_id
AND ausage.usages_source_version = rec.tm_rec_version
AND status.tm_bldg_blk_id = rec.tm_rec_id
AND status.tm_bldg_blk_version = rec.tm_rec_version
AND rec.tm_rec_type IN ( 'RANGE',
'MEASURE' )
AND papf.person_number = '101928'
AND trunc (status.date_to) = to_date ('31/12/4712', 'DD/MM/YYYY')
--and atrb.attribute_category in( 'Overtime','Regular Pay', 'Double_Time')
AND trunc (sh21.start_time) BETWEEN trunc(:P_From_Date) AND trunc(:P_To_Date)
AND abs.absence_type_id = atrb.absence_type_id )
GROUP BY person_number
Although these queries are working separately it is not working with group by on each subquery.
If I merge the two queries, the issue I am having is with Other_code & other_amount. These columns have values from two different tables.
How can I solve this?
Output should be returned for the two dates p_from_date- 01-Jan-2021 and p_to_date- 31-Jul-2021 as:
Person_Number Overtime_measure_hours Regular_Measure_hours Other_code Other_amount
101928 18 15 Double_Time 34
101928 18 15 Vacation 1
i.e. Overtime_measure_hours, Regular_Measure_hours and Other_amount should have the sum of these values.

Pivot without aggregate function

I am trying to turn some parts of my rows into columns. To my knowledge, I am only able to use a pivot with an aggregate function,but I would just be pivoting text. For each client I have up to 4 rows grouped by a DLSEQUENCE field. Instead of having the 4 rows, I would like everything to be on 1 row.
SELECT CASE
WHEN Sched_time BETWEEN TRUNC(SCHED_TIME) + INTERVAL '8' HOUR + INTERVAL '30' MINUTE
AND TRUNC(SCHED_TIME) + INTERVAL '14' HOUR + INTERVAL '45' MINUTE AND
TO_CHAR(SCHED_TIME, 'DY') IN ('MON', 'TUE', 'WED', 'THU', 'FRI')
THEN 'ABC'
ELSE 'DEF'
END AS Organization,
Client_Last_Name,
Client_First_Name,
Sched_Time,
Field_Name,
CASE
WHEN Recoded_Response = '1' THEN 'Yes'
WHEN Recoded_Response = '2' THEN 'No'
ELSE Recoded_Response
END AS Responses,
Dlsequence
FROM DAILY_LOG_CUSTOM_DATA
WHERE SERVICE_NAME = 'Medical'
AND FIELD_CATEGORY = 'Background Information'
AND Field_Name IN
(
'Restraint?',
'History',
'Findings',
'Treatment'
)
AND Sched_Time >= TO_DATE('2020-03-01 01:00:00', 'YYYY/MM/DD HH:MI:SS')
AND Sched_Time < TO_DATE('2020-03-31 12:59:00', 'YYYY/MM/DD HH:MI:SS')
Order BY Dlsequence
Here is my table:
I would like the response fields that go with ('Restraint?','History','Findings','Treatment') to have their own column for each DLSEQUENCE field.
The following should do what you had in mind:
SELECT DLSEQUENCE,
ORGANIZATION,
CLIENT_LAST_NAME,
CLIENT_FIRST_NAME,
SCHED_TIME,
LISTAGG("Restraint?", ',') WITHIN GROUP (ORDER BY DLSEQUENCE) AS "Restraint?",
LISTAGG("Findings", ',') WITHIN GROUP (ORDER BY DLSEQUENCE) AS "Findings",
LISTAGG("History", ',') WITHIN GROUP (ORDER BY DLSEQUENCE) AS "History",
LISTAGG("Treatment", ',') WITHIN GROUP (ORDER BY DLSEQUENCE) AS "Treatment"
FROM (SELECT DLSEQUENCE,
ORGANIZATION,
CLIENT_LAST_NAME,
CLIENT_FIRST_NAME,
SCHED_TIME,
CASE
WHEN FIELD_NAME = 'Restraint?' THEN RESPONSES
ELSE NULL
END AS "Restraint?",
CASE
WHEN FIELD_NAME = 'Findings' THEN RESPONSES
ELSE NULL
END AS "Findings",
CASE
WHEN FIELD_NAME = 'History' THEN RESPONSES
ELSE NULL
END AS "History",
CASE
WHEN FIELD_NAME = 'Treatment' THEN RESPONSES
ELSE NULL
END AS "Treatment"
FROM YOUR_TABLE)
GROUP BY DLSEQUENCE,
ORGANIZATION,
CLIENT_LAST_NAME,
CLIENT_FIRST_NAME,
SCHED_TIME
db<>fiddle here

SQL union query not returning both columns

I am trying to retrieve data from a database in a particular format to hook into a dashboard app. I need one column called "Import" with counts beneath it, and another called "Export" with relevant counts in that. I have the following query right now but it is only returning an Import column and I've verified there is relevant data to show as Export as well. Any ideas? Adding a screenshot of the result I'm getting...
SELECT count(*) as "Import", a.ship_id "Ship"
FROM SERVICE_EVENTS a JOIN CONTAINERS b ON a.eq_nbr = b.nbr
WHERE (
(to_char(sysdate, 'HH24') between '07' and '17' and a.performed between trunc(sysdate) + 7/24 and sysdate and category = 'I')
OR
(to_char(sysdate, 'HH24') between '18' and '23' and a.performed between trunc(sysdate) + 18/24 and sysdate and category = 'I')
OR
(to_char(sysdate, 'HH24') between '00' and '06' and a.performed between trunc(sysdate - 1) + 18/24 and sysdate and category = 'I')
)
AND a.TSERV_ID in ('LOAD', 'DISCHARGE') group by a.ship_id
UNION
SELECT count(*) as "Export", a.ship_id "Ship"
FROM SERVICE_EVENTS a JOIN CONTAINERS b ON a.eq_nbr = b.nbr
WHERE (
(to_char(sysdate, 'HH24') between '07' and '17' and a.performed between trunc(sysdate) + 7/24 and sysdate and category = 'E')
OR
(to_char(sysdate, 'HH24') between '18' and '23' and a.performed between trunc(sysdate) + 18/24 and sysdate and category = 'E')
OR
(to_char(sysdate, 'HH24') between '00' and '06' and a.performed between trunc(sysdate - 1) + 18/24 and sysdate and category = 'E')
)
AND a.TSERV_ID in ('LOAD', 'DISCHARGE') group by a.ship_id
;
You can do a conditional aggregation and simplify your query to this:
SELECT
a.ship_id AS Ship,
COUNT(CASE WHEN category = 'I' THEN 1 END) AS Import,
COUNT(CASE WHEN category = 'E' THEN 1 END) AS Export
FROM SERVICE_EVENTS a
JOIN CONTAINERS b
ON a.eq_nbr = b.nbr
WHERE
(
(to_char(sysdate, 'HH24') BETWEEN '07' AND '17' AND a.performed BETWEEN trunc(sysdate) + 7/24 AND sysdate)
OR (to_char(sysdate, 'HH24') BETWEEN '18' AND '23' AND a.performed BETWEEN trunc(sysdate) + 18/24 AND sysdate)
OR (to_char(sysdate, 'HH24') BETWEEN '00' AND '06' AND a.performed BETWEEN trunc(sysdate - 1) + 18/24 AND sysdate)
)
AND a.TSERV_ID in ('LOAD', 'DISCHARGE')
GROUP BY a.ship_id
You can explicitly select null as the column not being counted and do one more aggregation.
select max(Import) as Import, max(Export) as Export, Ship
from (
SELECT count(*) as "Import", null as "Export", a.ship_id "Ship"
FROM SERVICE_EVENTS a JOIN CONTAINERS b ON a.eq_nbr = b.nbr
WHERE (
(to_char(sysdate, 'HH24') between '07' and '17' and a.performed between trunc(sysdate) + 7/24 and sysdate and category = 'I')
OR
(to_char(sysdate, 'HH24') between '18' and '23' and a.performed between trunc(sysdate) + 18/24 and sysdate and category = 'I')
OR
(to_char(sysdate, 'HH24') between '00' and '06' and a.performed between trunc(sysdate - 1) + 18/24 and sysdate and category = 'I')
)
AND a.TSERV_ID in ('LOAD', 'DISCHARGE') group by a.ship_id
UNION
SELECT null as import, count(*) as "Export", a.ship_id "Ship"
FROM SERVICE_EVENTS a JOIN CONTAINERS b ON a.eq_nbr = b.nbr
WHERE (
(to_char(sysdate, 'HH24') between '07' and '17' and a.performed between trunc(sysdate) + 7/24 and sysdate and category = 'E')
OR
(to_char(sysdate, 'HH24') between '18' and '23' and a.performed between trunc(sysdate) + 18/24 and sysdate and category = 'E')
OR
(to_char(sysdate, 'HH24') between '00' and '06' and a.performed between trunc(sysdate - 1) + 18/24 and sysdate and category = 'E')
)
AND a.TSERV_ID in ('LOAD', 'DISCHARGE') group by a.ship_id
) t
group by Ship

Oracle SQL Count Records per Hour

So, what I'm trying to do basically is count the amount of shipments, per hour, per carrier, but I'm having difficulties getting it to sum in the columns I want. The goal is to instead of having 1 or 2 under Hour 1 or Hour 2 it would have sum(total shipments). Essentially 24 hours so we could see trends in time of day...
Code:
select router_destination_code,
case when to_char(app_last_updated_date_utc, 'HH24') = '00' then '1' else NULL end as "Hour 1",
case when to_char(app_last_updated_date_utc, 'HH24') = '01' then '2' else NULL end as "Hour 2",
case when to_char(app_last_updated_date_utc, 'HH24') = '02' then '3' else NULL end as "Hour 3",
--case when app_last_updated_date_utc between 'dec/07/2013 16:00:00' and 'dec/14/2013 17:00:00' then count(Router_Destination_code) else NULL end as "Hour_1"
count(Router_Destination_code) as Shipments
from booker.routing_container_history
where
app_last_updated_by_module in ('ManualSlam', 'slam')
and app_last_updated_date_utc between 'dec/07/2013 16:00:00' and 'dec/14/2013 16:00:00'
group by
router_destination_code,
case when to_char(app_last_updated_date_utc, 'HH24') = '00' then '1' else NULL end,
case when to_char(app_last_updated_date_utc, 'HH24') = '01' then '2' else NULL end,
case when to_char(app_last_updated_date_utc, 'HH24') = '02' then '3' else NULL end
order by
case when to_char(app_last_updated_date_utc, 'HH24') = '00' then '1' else NULL end,
case when to_char(app_last_updated_date_utc, 'HH24') = '01' then '2' else NULL end,
case when to_char(app_last_updated_date_utc, 'HH24') = '02' then '3' else NULL end,
count(Router_Destination_code) desc;
Output:
But, the Goal is to have the shipments underneath the hour.
Thank you.
Here is a new approach I did.. But it is showing lots of zeros, and would like it only to show numbers the whole way across.
select router_destination_code,
count(case when to_char(app_last_updated_date_utc, 'HH24') = '16' then router_destination_code else NULL end) as "Hour 1",
count(case when to_char(app_last_updated_date_utc, 'HH24') = '17' then router_destination_code else NULL end) as "Hour 2"
--case when app_last_updated_date_utc between 'dec/07/2013 16:00:00' and 'dec/14/2013 17:00:00' then count(Router_Destination_code) else NULL end as "Hour_1"
--count(Router_Destination_code) as Shipments
from booker.routing_container_history
where
app_last_updated_by_module in ('ManualSlam', 'slam')
and app_last_updated_date_utc between 'dec/07/2013 16:00:00' and 'dec/14/2013 16:00:00'
group by
router_destination_code,
case when to_char(app_last_updated_date_utc, 'HH24') = '16' then router_destination_code else NULL end,
case when to_char(app_last_updated_date_utc, 'HH24') = '17' then router_destination_code else NULL end
order by
case when to_char(app_last_updated_date_utc, 'HH24') = '16' then router_destination_code else NULL end,
case when to_char(app_last_updated_date_utc, 'HH24') = '17' then router_destination_code else NULL end,
count(Router_Destination_code) desc;
I do not know if I understood you well but you may try to just extract hour from date and then group by:
Check this fiddle (sorry for bad syntax):
http://sqlfiddle.com/#!4/a0a97/6
create table a (id number, data date);
insert into a (id, data)
values (1, to_date( '2013-01-01 13:12:01', 'yyyy-mm-dd hh24:mi:ss'));
insert into a (id, data)
values (1, to_date( '2013-01-01 13:12:01', 'yyyy-mm-dd hh24:mi:ss'));
insert into a (id, data)
values (1, to_date( '2013-01-01 14:12:01', 'yyyy-mm-dd hh24:mi:ss'));
select to_char(data,'HH24'), count(1) from a group by to_char(data,'HH24');