Aggregate result from Oracle request - sql

I have a table looking like this :
Table: table_name
name priority day hour
-------------------------------------
name1 1 monday 21:00
name2 3 tuesday 21:00
name3 1 monday 21:00
name4 2 monday 21:00
name5 2 sunday 22:00
name6 1 sunday 23:00
name7 1 thursday 00:00
name8 2 sunday 22:00
Is someone as any idea how I can do a request, then manipulate the result to aggregate the result as below (without the column header of course) :
priority day hour name
-----------------------------------------------
1 monday 21:00 name1,name3
3 tuesday 21:00 name2
2 monday 21:00 name4
2 sunday 22:00 name5,name8
1 sunday 23:00 name6
1 thursday 00:00 name7
I want to group by priority and day and hour.
Priority can be 1 to 5.
Day can obviously be Monday to Sunday
Hour can obviously be any hours :)
Name can be anything.
At the end, I need a SQL query to write the formatted results into a file.

As a workaround for the ORA-01489 you can use XMLAGG as a workaround as per this answer
with extract
SET LONG 2000000
SET pagesize 50000
SELECT rtrim(xmlagg(XMLELEMENT(e,text,',').EXTRACT('//text()')
).GetClobVal(),',') very_long_text
FROM
(SELECT to_char(to_date(level,'j'), 'jsp') text FROM dual CONNECT BY LEVEL < 250
)

STRAGG can be usefull
SELECT priority, day, hour, stragg(name)
FROM table_name
GROUP BY priority, day, hour
and if it is not sufficient use LISTAGG
SELECT priority, day, hour, LISTAGG(name, ',') WITHIN GROUP (ORDER BY name)
FROM table_name
GROUP BY priority, day, hour

Related

Grouping shift data by 7-day windows in SQL Server 2012

What I want to do is to calculate the number of shifts and hours worked by each employee in any given 7-day period. In order to achieve this, I need to identify and group 'islands' of shifts. Note that this 7-day period is not tied to a calendar week and the beginning and ending of this 7-day period would vary from employee to employee. This is sets it apart from other similar questions asked her in the past.
I have a table like this:
Person ID Start Date End Date Start time End time Hours Worked
12345 06-07-20 06-07-20 6:00 AM 7:45 AM 1.75
12345 06-07-20 06-07-20 8:15 AM 8:45 AM 0.50
12345 06-07-20 06-07-20 9:19 AM 9:43 AM 0.40
12345 08-07-20 08-07-20 12:00 AM 12:39 AM 0.65
12345 09-07-20 09-07-20 10:05 PM 11:59 PM 1.90
12345 11-07-20 11-07-20 4:39 PM 4:54 PM 0.25
12345 22-07-20 22-07-20 7:00 AM 7:30 AM 0.50
12345 23-07-20 23-07-20 1:00 PM 3:00 PM 2.00
12345 24-07-20 24-07-20 9:14 AM 9:35 AM 0.35
12345 27-07-20 27-07-20 4:00 PM 6:00 PM 2.00
12345 27-07-20 27-07-20 2:00 PM 4:00 PM 2.00
12345 28-07-20 28-07-20 9:00 AM 10:00 AM 1.00
12345 28-07-20 28-07-20 4:39 AM 4:59 AM 0.34
I want group and summarise the data above like this:
Person ID From To Number of shifts Number of Hours
12345 06-07-20 11-07-20 6 5.45
12345 22-07-20 28-07-20 7 8.19
Note that the first grouping for employee 12345 starts on 06-07-20 and ends on 11-07-20 because these shifts fall within the 06-07-20 - 13-07-20 7-day window.
The next day 7-day window is from 22-07-20 to 28-07-20, which means that the start date for the 7-day window has to be dynamic and based on the data i.e. not constant which makes this a complex task.
Also note that an employee may work multiple shifts in a day and that the shifts may not be consecutive.
I was playing around with using DATEDIFF() with LAG() and LEAD() but was unable to get to where I want. Any help would be appreciated.
I think you need a recursive CTE gor this. The idea is to enumerate the shifts of each person, and then iteratively walk the dataset, while keeping track of the first date of the period - when there is more than 7 days between the start of a period and the current date, the start date resets, and a new group starts.
with recursive
data as (select t.*, row_number() over(partition by personid order by start_date) rn from mytable t)
cte as (
select personid, start_date, start_date end_date, hours_worked, rn
from data
where rn = 1
union all
select
c.personid,
case when d.start_date > dateadd(day, 7, c.start_date) then d.start_date else c.start_date end,
d.start_date,
d.hours_worked,
d.rn
from cte c
inner join data d on d.personid = c.personid and d.rn = c.rn + 1
)
select personid, start_date, max(start_date) end_date, count(*) no_shifts, sum(hours_worked)
from cte
group by personid, start_date
This assumes that:
dates do not span over multiple days, as shown in your sample data
dates are stored as date datatype, and times as time

BigQuery: Select top 3 Day of Sales with GroupBy and save in separate columns leaving others

Input:
Have got Table 'A'
Store Category Sales Day
11 aaa 1.5 Sunday
11 aaa 0.5 Monday
11 aaa 2.5 Tuesday
11 aaa 2.0 Wednesday
11 aaa 3.0 Thursday
11 aaa 3.5 Friday
11 aaa 0.5 Saturday
22 bbb 0.5 Sunday
22 bbb 1.5 Monday
22 bbb 2.3 Tuesday
22 bbb 0.3 Wednesday
22 bbb 1.4 Thursday
22 bbb 4.1 Friday
22 bbb 0.2 Saturday
Scenario:
Have to take average of Sales grouped by Store,Category and save in separate column as well as pick top 3 sales day and save in separate column. As a result, one row for one store,category as shown below in sample output.
Expected Output:
Store Category AvgSales PeakDay1 PeakDay2 PeakDay3
11 aaa 1.92 Friday Thursday Tuesday
22 bbb 1.47 Friday Tuesday Monday
Tried Query:
SELECT
Store,
Category,
avg(Sales) as AvgSales,
ARRAY_AGG(Sales ORDER BY Sales DESC LIMIT 3) #but this line will not produce results in 3 separate columns
FROM A
GROUP BY Site, Category
Thanks in Advance!
You could use array agg, but row_number() seem simple enough:
select store, category, avg(sales),
max(case when seqnum = 1 then day end) as peakday1,
max(case when seqnum = 2 then day end) as peakday2,
max(case when seqnum = 3 then day end) as peakday3
from (select store, category, day,
row_number() over (partition by store, category order by sales desc) as seqnum
from a
) a
group by store, category;
If you want to put this into an array column instead, you can use:
SELECT Store, Category, avg(Sales) as AvgSales,
ARRAY_AGG(day ORDER BY Sales DESC LIMIT 3)
FROM A
GROUP BY Site, Category
Below is for BigQuery Standard SQL
#standardSQL
SELECT Store, Category, AvgSales,
Days[OFFSET(0)] PeakDay1,
Days[SAFE_OFFSET(1)] PeakDay2,
Days[SAFE_OFFSET(2)] PeakDay3
FROM (
SELECT Store, Category,
ROUND(AVG(Sales), 2) AvgSales,
ARRAY_AGG(Day ORDER BY Sales DESC LIMIT 3) Days
FROM `project.dataset.table` t
GROUP BY Store, Category
)
If to apply to sample data from your question - output is
Row Store Category AvgSales PeakDay1 PeakDay2 PeakDay3
1 11 aaa 1.93 Friday Thursday Tuesday
2 22 bbb 1.47 Friday Tuesday Monday

SQL: Differences between times and counting the frequency

I have the following data ordered by events, ID and then start_time:
EVENT ID START_TIME END_TIME
1 101 1:00 2:00
1 101 3:00 3:30
1 102 1:00 4:00
1 102 5:00 6:00
2 103 10:00 11:00
2 103 12:00 13:00
2 103 13:30 14:00
2 103 14:30 15:00
And I want to end up with the following:
Difference_hour Frequency
1 3
0,5 2
I would like to obtain a query that is looking at the difference between the END_TIME of an ID and the START_TIME of the same ID within the same EVENT (to mention specifically, i am not interested in the difference between the START_TIME and END_TIME of the same row).
Example: in event 1 we have to ID's 101, and I would like to have the difference between the first END_TIME (2:00) and the following START_TIME on the second row 3:00). The difference is 1 hour. If we do this similar for ID 102, we end up with another difference of 1 hour.
In the end, I would like to count the frequency of each of the differences, which can be seen in the second table.
select diff_hour, count(*)
from
(
select (next_start - end_time)*86400 as diff_hour
from
(
select end_time, lead(start_time) over (partition by event, id order by start_time) next_start
from MyTable
) x1
where next_start is not null
) x2
group by diff_hour

SQL Server Query to Get Available Employee based on Schedule

I have two tables, parent table Employees and child table Employees_Availability, like this:
Employees table:
EmployeesID Name Group Availability_Order Available
--------------------------------------------------------------
1 Steve Sales 1 TRUE
2 Ann Sales 2 TRUE
3 Jack Sales 3 FALSE
4 Sandy Support 4 TRUE
5 Bill Support 5 TRUE
6 John Support 6 TRUE
Employees_Schedule table:
EmployeesID Day From To
----------------------------------------------
1 Monday 8:00 12:00
1 Monday 13:00 17:00
2 Monday 12:00 13:00
3 Tuesday 7:30 11:30
3 Wednesday 7:30 11:30
3 Friday 14:30 16:30
4 Tuesday 11:30 17:00
5 Wednesday 8:00 12:00
5 Wednesday 13:00 17:00
5 Thursday 12:00 13:00
5 Friday 7:30 11:30
6 Friday 12:00 13:00
How can I create a query that given date/time and Group return first available employee? I am using SQL Server 2012. Here is what I started doing but got stuck:
Select top 1
Name
from
Empolyees e join? Employees_Schedule s
on
e.employeesID = s.EmployeesID
where
e.group = 'Sales'
and DATENAME(Weekday,'5/24/2016 10:00') = s.Day
and CAST('5/24/2016 10:00' AS TIME) 'hh:mm' >= CAST(s.from AS TIME)
and CAST('5/24/2016 10:00' AS TIME) 'hh:mm' <= CAST(s.to AS TIME)
order by
e.availability_order
Thanks
Have you looked into Window Function and CTE? You could easily achieve this with, for example..
Row_Number() OVER(PARTITION BY day ORDER BY starttime ASC) as ColumnName
Combined with predicate
WHERE columnName = 1 AND groupName = 'groupname'
For detail, read BOL on OVER()Clause here, and CTE here.
It looks like you're close. If you wrap the main part of your SQL in a Common Table Expression and use the row_number() window function then you can find the first available:
;with cte as (
Select top 1
Name,
row_number() over (order by ea.From) PrioritySequence
from
Empolyees e join? Employees_Schedule s
on
e.employeesID = s.EmployeesID
where
e.group = 'Sales'
and DATENAME(Weekday,'5/24/2016 10:00') = s.Day
and CAST('5/24/2016 10:00' AS TIME) 'hh:mm' >= CAST(s.from AS TIME)
and CAST('5/24/2016 10:00' AS TIME) 'hh:mm' <= CAST(s.to AS TIME)
)
select *
from cte
where PrioritySequence = 1

Update the list of dates to have the same day

I have this in my table
TempTable
Id Date
1 1-15-2010
2 2-14-2010
3 3-14-2010
4 4-15-2010
i would like to change every record so that they have all same day, that is the 15th
like this
TempTable
Id Date
1 1-15-2010
2 2-15-2010 <--change to 15
3 3-15-2010 <--change to 15
4 4-15-2010
what if i like on the 30th?
the records should be
TempTable
Id Date
1 1-30-2010
2 2-28-2010 <--change to 28 because feb has 28 days only
3 3-30-2010 <--change to 30
4 4-30-2010
thanks
You can play some fun tricks with DATEADD/DATEDIFF:
create table T (
ID int not null,
DT date not null
)
insert into T (ID,DT)
select 1,'20100115' union all
select 2,'20100214' union all
select 3,'20100314' union all
select 4,'20100415'
SELECT ID,DATEADD(month,DATEDIFF(month,'20100101',DT),'20100115')
from T
SELECT ID,DATEADD(month,DATEDIFF(month,'20100101',DT),'20100130')
from T
Results:
ID
----------- -----------------------
1 2010-01-15 00:00:00.000
2 2010-02-15 00:00:00.000
3 2010-03-15 00:00:00.000
4 2010-04-15 00:00:00.000
ID
----------- -----------------------
1 2010-01-30 00:00:00.000
2 2010-02-28 00:00:00.000
3 2010-03-30 00:00:00.000
4 2010-04-30 00:00:00.000
Basically, in the DATEADD/DATEDIFF, you specify the same component to both (i.e. month). Then, the second date constant (i.e. '20100130') specifies the "offset" you wish to apply from the first date (i.e. '20100101'), which will "overwrite" the portion of the date your not keeping. My usual example is when wishing to remove the time portion from a datetime value:
SELECT DATEADD(day,DATEDIFF(day,'20010101',<date column>),'20100101')
You can also try something like
UPDATE TempTable
SET [Date] = DATEADD(dd,15-day([Date]), DATEDIFF(dd,0,[Date]))
We have a function that calculates the first day of a month, so I just addepted it to calculate the 15 instead...