PL/SQL Calculate sum of start/end dates difference - sql

I have a table with start and end dates of non-attendance and i want to get the sum of days of absenteeism grouped by id and by month. I do this by calculating difference between dates +1. The problem is that sometimes dates periods overlap with each others. Here is an example:
ID ABS_START ABS_END NBR_ABS
5 04/02/2016 04/02/2016 1
5 05/02/2016 05/02/2016 1
5 06/02/2016 07/02/2016 2
5 07/02/2016 20/02/2016 14
5 08/02/2016 14/02/2016 7
NBR_ABS = number of absences relative to dates period
Expected results:
ID ABS_MONTH NBR_ABS_MONTH
5 2016/02 17
6 2016/02 0
7 2016/02 5
8 2016/02 13
9 2016/02 2
NBRE_ABS_MONTH = number of absences by id for whole month
Is there any way to deal with such issue in oracle environment ?
Thanks for your answers!

One simple way is to get a list of dates in a range and check. For instance:
with dates as (
select (date '2016-02-01') + rownum - 1
from all_objects
where rownum <= 29
)
select i.id, count(*) as absence_201602
from dates cross join
(select distinct id from t) i
where exists (select 1
from t
where t.id = i.id and
d.date between t.abs_start and t.abs_end
)
group by id;

Related

Counting SUM(VALUE) from previous cell

I have the following table:
A
Sum(Tickets)
01-2022
5
02-2022
2
03-2022
8
04-2022
1
05-2022
3
06-2022
3
07-2022
4
08-2022
1
09-2022
5
10-2022
5
11-2022
3
I would like to create the following extra column 'TotalSum(Tickets)' but I am stuck....
Anyone who can help out?
A
Sum(Tickets)
TotalSum(Tickets)
01-2022
5
5
02-2022
2
7
03-2022
8
15
04-2022
1
16
05-2022
3
19
06-2022
3
22
07-2022
4
26
08-2022
1
27
09-2022
5
32
10-2022
5
37
11-2022
3
40
You may use SUM() as a window function here:
SELECT A, SumTickets, SUM(SumTickets) OVER (ORDER BY A) AS TotalSumTickets
FROM yourTable
ORDER BY A;
But this assumes that you actually have a bona-fide column SumTickets which contains the sums. Assuming you really showed us the intermediate result of some aggregation query, you should use:
SELECT A, SUM(Tickets) AS SumTickets,
SUM(SUM(Tickets)) OVER (ORDER BY A) AS TotalSumTickets
FROM yourTable
GROUP BY A
ORDER BY A;
left join the same table where date is not bigger, then sum that for every date:
select
table1.date,
sum(t.tickets)
from
table1
left join table1 t
on t.date<= table1.date
group by
table1.date;

Get earliest value from a column with other aggregated columns in postgresql

I have a very simple stock ledger dataset.
1. date_and_time store_id product_id batch opening_qty closing_qty inward_qty outward_qty
2. 01-10-2021 14:20:00 56 a 1 5 1 0 4
3. 01-10-2021 04:20:00 56 a 1 8 5 0 3
4. 02-10-2021 15:30:00 56 a 1 9 2 1 8
5. 03-10-2021 08:40:00 56 a 2 2 6 4 0
6. 04-10-2021 06:50:00 56 a 2 8 4 0 4
Output I want:
select date, store_id,product_id, batch, first(opening_qty),last(closing_qty), sum(inward_qty),sum(outward_qty)
e.g.
1. date store_id product_id batch opening_qty closing_qty inward_qty outward_qty
2. 01-10-2021 56 a 1 8 1 0 7
I am writing a query using First_value window function and tried several others but not able to get the out put I want.
select
date,store_id,product_id,batch,
FIRST_VALUE(opening_total_qty)
OVER(
partition by date,store_id,product_id,batch
ORDER BY created_at
) as opening__qty,
sum(inward_qty) as inward_qty,sum(outward_qty) as outward_qty
from table
group by 1,2,3,4,opening_total_qty
Help please.
As your expected result is one row per group of rows with the same date, you need aggregates rather than window functions which provide as many rows as the ones filtered by the WHERE clause. You can try this :
SELECT date_trunc('day', date),store_id,product_id,batch
, (array_agg(opening_qty ORDER BY datetime ASC))[1] as opening__qty
, (array_agg(closing_qty ORDER BY datetime DESC))[1] as closing_qty
, sum(inward_qty) as inward_qty
, sum(outward_qty ) as outward_qty
FROM table
GROUP BY 1,2,3,4
see the test result in dbfidle.

Using a table in Join where one table has all the dates of last three months - Oracle

I have a table say as below:
Date Sales_Quantity
1-JAN-2021 4
5-JAN-2021 5
15-FEB-2021 10
31-MAR-2021 11
What I want is to generate a report on 31-MAR-2021, displaying all the dates on the left-hand side, for the last three months, and where ever there is no sale display zero.
The result should look like this:
1-JAN-2021,4
2-JAN-2021,0
3-JAN-2021,0
4-JAN-2021,0
5-JAN-2021,5
6-JAN-2021,0
...
...
30-MAR-2021,0
31-MAR-2021,11
I am guessing I can achieve this by left outer join, but how do I get a left table with all the dates of the last 3 months?
Any help will be highly appreciated. Thank you.
Data you already have:
SQL> select * From test;
DATUM SALES_QUANTITY
----------- --------------
01-jan-2021 4
05-jan-2021 5
15-feb-2021 10
Using a CTE, I'm creating a calendar for this year (sysdate says so; you can create it for any year, or as many years you want) and outer joining it to the test table.
SQL> with calendar as
2 (select trunc(sysdate, 'yyyy') + level - 1 datum
3 from dual
4 connect by level <= add_months(trunc(sysdate, 'yyyy'), 12) -
5 trunc(sysdate, 'yyyy')
6 )
7 select c.datum,
8 nvl(t.sales_quantity, 0) sales_quantity
9 from calendar c left join test t on t.datum = c.datum
10 order by c.datum;
DATUM SALES_QUANTITY
----------- --------------
01-jan-2021 4
02-jan-2021 0
03-jan-2021 0
04-jan-2021 0
05-jan-2021 5
06-jan-2021 0
07-jan-2021 0
08-jan-2021 0
09-jan-2021 0
10-jan-2021 0
11-jan-2021 0
<snip>

multiple group by for a table query

I have a table like this:
Month Type Price
============================
1 a 12
2 b 43
1 a 11
4 c 22
1 b 33
2 c 4
3 a 25
2 b 35
4 c 20
I want to get a query that has result some thing like this:
Month Type Total Price
============================
1 a 23
1 b 33
2 b 78
2 c 4
3 a 25
4 c 44
means:
prices are Total Price of special Type in a Month.
for example we have type 'a' in month '1' and '3'
Total Prices of 'a' in month '1' is 23 and in month '3' is 25
I think we should use multiple group by.
I can group it just by Type or Month but not by both of them.
thanks for helping
You can specify a list of expressions in the GROUP BY clause
SELECT Month, Type, SUM(Price) AS [Total Price]
FROM MyTable
GROUP BY Month, Type
ORDER BY Month, Type
In GROUP BY, list all the involved columns, except those that have an aggregate function (SUM, MIN, MAX, AVG etc.) applied to them.

How can I find consecutive active weeks in SQL?

What I would like to do is find the number of consecutive weeks that someone is active on Sundays and assign them a value. They have to participate in at least 2 races a day to be counted as active for the week.
If they are active for 2 consecutive weeks I would like to assign a value of 100, 3 consecutive weeks a value of 200, 4 consecutive weeks a value of 300, and continuing up to 9 consecutive weeks.
My difficulty is not determining consecutive weeks, but breaks in between consecutive dates. Suppose the following dataset:
CustomerID RaceDate Races
1 2/2/2014 2
1 2/9/2014 5
1 2/16/2014 3
1 2/23/2014 3
1 3/2/2014 4
1 3/9/2014 3
1 3/16/2014 3
2 2/2/2014 2
2 2/9/2014 3
2 3/2/2014 2
2 3/9/2014 4
2 3/16/2014 3
CustomerID 1 would have 7 consecutive weeks for a value of 600.
The hard part for me is CustomerID 2. They would have 2 consecutive weeks AND 3 consecutive weeks. So their total value would be 100 + 200 = 300.
I would like to be able to do this with any different combination of consecutive weeks.
Any help please?
EDIT: I am using SQL Server 2008 R2.
When looking for sequential values, there is a simple observation that helps. If you subtract a sequence from the dates then the value is a constant. You can use this as a grouping mechanism
select CustomerId, min(RaceDate) as seqStart, max(RaceDate) as seqEnd,
count(*) as NumDaysRaced
from (select t.*,
dateadd(week, - row_number() over (partition by customerID, RaceDate),
RaceDate) as grp
from table t
where races >= 2
) t
group by CustomerId, grp;
You can then use this to get your final "points":
select CustomerId,
sum(case when NumDaysRaced > 1 then (NumDaysRaced - 1) * 100 else 0 end) as Points
from (select CustomerId, min(RaceDate) as seqStart, max(RaceDate) as seqEnd,
count(*) as NumDaysRaced
from (select t.*,
dateadd(week, - row_number() over (partition by customerID, RaceDate),
RaceDate) as grp
from table t
where races >= 2
) t
group by CustomerId, grp
) c
group by CustomerId;