Count times date occurs between two dates in a second table - sql

I have two tables [Charges] and [Defects] and want to produce [Desired Query Output] where the output counts the occurrances of defect when [Charges].ChargeDate is between (and including) [Defects].OpenDate and [Defects].CloseDate. For [Defects] table, a close date of NULL means it has not closed yet. Seems simple enough, but I haven't found a good example of how to do this. Can you help??
I'm using SQL Server version 12.
[Charges]
Order Charge ChargeDate
1 1.2 07/10/2020
1 0.6 07/15/2020
6 0.002 07/20/2020
8 0.13 07/01/2020
8 1.1 06/18/2020
8 0.3 06/19/2020
10 2.3 06/24/2020
[Defects]
Order DefectID OpenDate CloseDate
1 25 06/01/2020 NULL
1 27 07/09/2020 07/12/2020
1 30 05/01/2020 07/20/2020
8 45 06/19/2020 06/19/2020
8 47 06/12/2020 07/05/2020
8 48 06/19/2020 NULL
10 49 06/24/2020 NULL
[Desired Query Output]
Order Charge ChargeDate DefectCnt
1 1.2 07/10/2020 3
1 0.6 07/15/2020 2
6 0.002 07/20/2020 0
8 0.13 07/01/2020 2
8 1.1 06/18/2020 1
8 0.3 06/19/2020 3
10 2.3 06/24/2020 1

You can use a correlated subquery or a lateral join:
select
c.*,
(
select count(*)
from defects d
where
d.order = c.order
and c.ChargeDate >= d.OpenDate
and (d.CloseDate is null or c.ChargeDate <= d.CloseDate)
) as DefectCnt
from charges c

Related

Postgresql calculate percentage of values out of sum of values of specific rows

I need to calculate percentage of hours per each project, not out of all the quantity of projects.
Here is the initial table:
employee_id
project_id
hours
999111111
1
31.4
999111111
2
8.5
999333333
3
42.1
999888888
1
21.0
999888888
2
22.0
999444444
2
12.2
999444444
3
10.5
999444444
1
null
999444444
10
10.1
999444444
20
11.8
999887777
30
30.8
999887777
10
10.2
999222222
10
34.5
999222222
30
5.1
999555555
30
19.2
999555555
20
14.8
999666666
20
null
Needed output:
employee_id
project_id
percent
999111111
1
60
999111111
2
20
999333333
3
80
999888888
1
40
999888888
2
52
999444444
2
29
999444444
3
20
999444444
1
null
999444444
10
18
999444444
20
44
999887777
30
56
999887777
10
19
999222222
10
63
999222222
30
9
999555555
30
35
999555555
20
56
999666666
20
null
I understand how to calculate out of overall COUNT of all hours, but I need percentage per employee out of COUNT of hours within the same project ID, and that's what I'm struggling with. How can it be done?
Assuming every project would have at least one record with a non zero hours value, we can try using this query:
SELECT employee_id, project_id,
100.0 * hours / SUM(hours) OVER (PARTITION BY project_id) AS percent
FROM yourTable
ORDER BY project_id, employee_id;
You can use window functions to calculate the percentage of hours for each project:
SELECT employee_id, project_id,
100.0 * SUM(hours) OVER (PARTITION BY project_id) / SUM(hours) OVER () AS percent
FROM yourTable
ORDER BY employee_id, project_id;

Joining two tables where date in second table should be first date after date in first table i.e earliest date after date in first table

Problem statement description :-
I have two tables - table1 and table2 . table1 contains data of quantity buy of id=7 and table2 contain data of quantity sold of same id=7. Both table1 and table2 is sorted according to date i.e from oldest to latest date and every time only 1 quantity is buy or sold.
table1:
table1
date_buy
id
qty_buy
rolling_sum_qty_buy
30-07-2019
7
1
1
20-10-2019
7
1
2
17-01-2020
7
1
3
15-02-2020
7
1
4
15-02-2020
7
1
5
15-02-2020
7
1
6
14-07-2021
7
1
7
19-09-2021
7
1
8
25-12-2021
7
1
9
30-12-2021
7
1
10
10-02-2022
7
1
11
15-03-2022
7
1
12
15-03-2022
7
1
13
14-06-2022
7
1
14
table2:-
table2
date_sold
id
qty_sold
rolling_sum_qty_sold
01-08-2019
7
1
1
15-09-2019
7
1
2
27-12-2019
7
1
3
01-02-2020
7
1
4
12-02-2020
7
1
5
25-07-2021
7
1
6
25-07-2021
7
1
7
28-08-2021
7
1
8
10-09-2021
7
1
9
12-09-2021
7
1
10
25-04-2022
7
1
11
-- SQL scrtpt of table1:
CREATE TABLE IF NOT EXISTS table1 (
`date_buy` DATETIME,
`id` INT,
`qty_buy` INT,
`rolling_sum_qty_buy` INT
);
INSERT INTO table1 VALUES
('2019-07-30 00:00:00',7,1,1),
('2019-10-20 00:00:00',7,1,2),
('2020-01-17 00:00:00',7,1,3),
('2020-02-15 00:00:00',7,1,4),
('2020-02-15 00:00:00',7,1,5),
('2020-02-15 00:00:00',7,1,6),
('2021-07-14 00:00:00',7,1,7),
('2021-09-19 00:00:00',7,1,8),
('2021-12-25 00:00:00',7,1,9),
('2021-12-30 00:00:00',7,1,10),
('2022-02-10 00:00:00',7,1,11),
('2022-03-15 00:00:00',7,1,12),
('2022-03-15 00:00:00',7,1,13),
('2022-06-14 00:00:00',7,1,14);
-- sql script of table2:
CREATE TABLE IF NOT EXISTS table2 (
`date_sold` DATETIME,
`id` INT,
`qty_sold` INT,
`rolling_sum_qty_sold` INT
);
INSERT INTO table2 VALUES
('2019-08-01 00:00:00',7,1,1),
('2019-09-15 00:00:00',7,1,2),
('2019-12-27 00:00:00',7,1,3),
('2020-02-01 00:00:00',7,1,4),
('2020-02-12 00:00:00',7,1,5),
('2021-07-25 00:00:00',7,1,6),
('2021-07-25 00:00:00',7,1,7),
('2021-08-28 00:00:00',7,1,8),
('2021-09-10 00:00:00',7,1,9),
('2021-09-12 00:00:00',7,1,10),
('2022-04-25 00:00:00',7,1,11);
-- Now, i want to join this two table on two condition
for every date i.e date_buy column in table1 i should get output where date i.e date_sold is greater than date_buy and i want first date i.e. date_sold which is greater than that particular date i.e date_buy.
i also want those rows from table1 in my output which does not get joined with table2
so that i can easily find out the remaining quantity because in table1 i have quantity buy and after joining with table2 i will get quantity sold, so the cases where i get null values then in that case i can assume that that much quantity is remaining.
--My output:-
Earlier when there was no date issue then i was simply using left join to join table1 and table2 on rolling sum condition and where there was null cases i was taking sum of qty to get remaining qty but right now i have that condition too so i cant use rolling_sum_cond column directly in join condition.
-- query which i was using and output which i was getting earlier
select * from table1
left join table2
on table1.rolling_sum_qty_buy=table2.rolling_sum_qty_sold
date_buy
id
qty_buy
rolling_sum_qty_buy
date_sold
id-2
qty_sold
rolling_sum_qty_sold
30-07-2019
7
1
1
01-08-2019
7
1
1
20-10-2019
7
1
2
15-09-2019
7
1
2
17-01-2020
7
1
3
27-12-2019
7
1
3
15-02-2020
7
1
4
01-02-2020
7
1
4
15-02-2020
7
1
5
12-02-2020
7
1
5
15-02-2020
7
1
6
25-07-2021
7
1
6
14-07-2021
7
1
7
25-07-2021
7
1
7
19-09-2021
7
1
8
28-08-2021
7
1
8
25-12-2021
7
1
9
10-09-2021
7
1
9
30-12-2021
7
1
10
12-09-2021
7
1
10
10-02-2022
7
1
11
25-04-2022
7
1
11
15-03-2022
7
1
12
NULL
NULL
NULL
NULL
15-03-2022
7
1
13
NULL
NULL
NULL
NULL
14-06-2022
7
1
14
NULL
NULL
NULL
NULL
and to find out remaining quantity , i was using null condition
query:-
with cte as
(
select * from table1
left join table2
on table1.rolling_sum_qty_buy=table2.rolling_sum_qty_sold
)
select sum(qty_buy) as remaining_qty
from cte
where cte.date_sold is null
remaining_qty
3
-- my expectation
now i have to use date condition also to get the output
-- Expected Output
date_buy
id
qty_buy
rolling_sum_qty_buy
date_sold
id
qty_sold
rolling_sum_qty_sold
30-07-2019
7
1
1
01-08-2019
7
1
1
20-10-2019
7
1
2
27-12-2019
7
1
3
17-01-2020
7
1
3
01-02-2020
7
1
4
15-02-2020
7
1
4
25-07-2021
7
1
6
15-02-2020
7
1
5
25-07-2021
7
1
7
15-02-2020
7
1
6
28-08-2021
7
1
8
14-07-2021
7
1
7
10-09-2021
7
1
9
19-09-2021
7
1
8
25-04-2022
7
1
11
25-12-2021
7
1
9
NULL
NULL
NULL
NULL
30-12-2021
7
1
10
NULL
NULL
NULL
NULL
10-02-2022
7
1
11
NULL
NULL
NULL
NULL
15-03-2022
7
1
12
NULL
NULL
NULL
NULL
15-03-2022
7
1
13
NULL
NULL
NULL
NULL
14-06-2022
7
1
14
NULL
NULL
NULL
NULL
-- Please help me to get the following output. Any help would be appreciated.
I am using postgresql.
That was a challenging one.
with recursive cte as
(
select t2.date_sold
,t2.rolling_sum_qty_sold
,true as is_match
,1 as last_rolling_sum_qty_buy
from t2 join t on t2.rolling_sum_qty_sold = t.rolling_sum_qty_buy
where t2.rolling_sum_qty_sold = 1
union all
select t2.date_sold
,t2.rolling_sum_qty_sold
,t2.date_sold >= t.date_buy
,cte.last_rolling_sum_qty_buy + case when t2.date_sold >= t.date_buy then 1 else 0 end
from t2
join cte on cte.rolling_sum_qty_sold + 1 = t2.rolling_sum_qty_sold
join t on t.rolling_sum_qty_buy = cte.last_rolling_sum_qty_buy + 1
)
select t.date_buy
,t.id
,t.qty_buy
,t.rolling_sum_qty_buy
,cte.date_sold
,cte.rolling_sum_qty_sold
from t left join cte on cte.last_rolling_sum_qty_buy = t.rolling_sum_qty_buy and is_match
date_buy
id
qty_buy
rolling_sum_qty_buy
date_sold
rolling_sum_qty_sold
2019-07-30
7
1
1
2019-08-01
1
2019-10-20
7
1
2
2019-12-27
3
2020-01-17
7
1
3
2020-02-01
4
2020-02-15
7
1
4
2021-07-25
6
2020-02-15
7
1
5
2021-07-25
7
2020-02-15
7
1
6
2021-08-28
8
2021-07-14
7
1
7
2021-09-10
9
2021-09-19
7
1
8
2022-04-25
11
2021-12-25
7
1
9
null
null
2021-12-30
7
1
10
null
null
2022-02-10
7
1
11
null
null
2022-03-15
7
1
12
null
null
2022-03-15
7
1
13
null
null
2022-06-14
7
1
14
null
null
Fiddle

SQL Query to get the vehicle ID and his locations to the most recent date(values distrubuted in 3 distinct tables)

This is my first question here.
And I've been struggling for a while and I can't solve it.
So I want to get for all the circuits the location(latitude and longitude) to the most recent date.
The schema that I'm using contains important information so i will give an example:
table Circuit{
NR int Primary Key,
VEHICLE_ID int,
etc...
}
table StopPoints{
NR int Primary Key,
Circuit int Foreign Key Circuit.NR,
Latitude float,
Longitude float,
etc...
}
table StopPoint_Flux{
NR int Primary Key,
StopPoint int Foreign Key StopPoints.NR,
Date DATE,
etc...
}
And an example of the data in it:
Circuit:
NR
VEHICLE_ID
1
100
2
208
3
210
4
1
StopPoints:
NR
Circuit
Latitude
Longitude
1
1
0.3
5.0
2
1
1.7
8.0
3
2
7.0
10.0
4
4
11.0
1.2
5
3
45.0
46.0
6
4
43.1
6.23
7
1
34.42
36.98
8
3
40.48
47.50
StopPoint_Flux:
NR
StopPoint
Date
1
1
21/12/2020
2
2
19/12/2020
3
2
1/12/2020
4
1
5/12/2020
5
3
2/12/2020
6
5
7/12/2020
7
4
30/12/2020
8
5
7/12/2020
9
6
5/12/2020
10
8
1/12/2020
11
7
30/12/2020
12
6
8/12/2020
13
1
21/12/2020
The result that I need:
VEHICLE_ID
Latitude
Longitude
Date
100
34.42
36.98
30/12/2020
208
7.0
10.0
2/12/2020
210
45.0
46.0
7/12/2020
1
11.0
1.2
30/12/2020
If ROW_NUMBER works.
SELECT
q.VEHICLE_ID
, q.Latitude
, q.Longitude
, q.Date
FROM
(
SELECT
circuit.VEHICLE_ID
, stop.Latitude
, stop.Longitude
, flux.Date
, ROW_NUMBER() OVER (PARTITION BY circuit.NR
ORDER BY flux.Date DESC, stop.NR) AS rn
FROM Circuit AS circuit
JOIN StopPoints AS stop
ON stop.circuit = circuit.NR
JOIN StopPoint_Flux AS flux
ON flux.StopPoint = stop.NR
) q
WHERE q.rn = 1
vehicle_id
latitude
longitude
date
100
34.42
36.98
2020-12-30
208
7
10
2020-12-02
210
45
46
2020-12-07
1
11
1.2
2020-12-30
db<>fiddle here

How to query data and its count in multiple range at same time

I have a table like below,
id
number
date
1
23
2020-01-01
2
12
2020-03-02
3
23
2020-09-02
4
11
2019-03-04
5
12
2019-03-23
6
23
2019-04-12
I want to know is that how many times each number appears per year, such as,
number
2019
2020
23
1
2
12
1
1
11
1
0
I'm kinda stuck.. tried with left join or just a single select, but still, cannot figure out how to make it, please help thank you!
SELECT C.NUMBER,
SUM
(
CASE
WHEN C.DATE BETWEEN '20190101'AND '20191231'
THEN 1 ELSE NULL
END
) AS A_2019,
SUM
(
CASE
WHEN C.DATE BETWEEN '20200101'AND '20201231'
THEN 1 ELSE NULL
END
) AS A_2020
FROM I_have_a_table_like_below AS C
GROUP BY C.NUMBER

Keep first record in group and populate rest with Null/0 in SQL?

I have the following table in my database:
date sales
1 2010-12-13 10
2 2010-12-13 10
3 2010-12-13 10
4 2010-12-13 10
5 2010-12-13 10
6 2010-12-14 20
7 2010-12-14 20
8 2010-12-14 20
9 2010-12-14 20
10 2010-12-14 20
Is there a way to attain the first record only and populate the rest with NULL or 0 for the remainder of the group? AS the grouping will be done by date and sales:
For example the intended output is:
date sales
1 2010-12-13 10
2 2010-12-13 0
3 2010-12-13 0
4 2010-12-13 0
5 2010-12-13 0
6 2010-12-14 20
7 2010-12-14 0
8 2010-12-14 0
9 2010-12-14 0
10 2010-12-14 0
So essentially to keep the first record but make the rest of the records in the group be 0 (maybe Null if that is quicker/easier)
The closest i have got to solving this is attaining just the first record through an inner join - but I think a partition over may solve it - just stuck at the moment!
Any help appreciated!
Using SQLite - but also GCP (SQL) is accesible to me
This might work in SQLite:
CASE WHEN id = MIN(id) OVER(PARTITION BY date) THEN sales ELSE 0 END as sales
If it doesn't you can prepare a subquery that has only the min ID per date and join it in:
SELECT
CASE WHEN y.id IS NULL THEN 0 ELSE sales END as sales
FROM
x
LEFT JOIN (SELECT MIN(id) as id FROM x GROUP BY date) y ON x.id= y.id