3 Month Rolling Average - sql

I need some help calculating a rolling 3 month average cost from the two dataset below. Which is the 3 month Average of Dataset1 / Dataset 2. I'm not sure if using cte is the correct route. Any insight is appreciated.
Dataset 1:
SELECT(factAdmissions.ContractCode + '-' +factAdmissions.BenefitPlanCode) AS [Contract Code],
factAdmissions.AdmitCCYYMM,
ISNULL(sum(AmountPaid),0)As [Amount Paid]
FROM factAdmissions
WHERE factAdmissions.AdmitCCYYMM >='200701'
AND factAdmissions.AcuteSNFIndAdmit ='a'
AND factAdmissions.ContractCode Is Not Null
AND factAdmissions.BenefitPlanCode Is Not Null
AND BusinessUnitCode In('EP','HF', 'VN')
AND AdmitCCYYMM BETWEEN (#StartDate) AND (#EndDate)
AND factAdmissions.ContractCode + '-' +factAdmissions.BenefitPlanCode IN (#Product)
Group by factAdmissions.ContractCode,
factAdmissions.BenefitPlanCode,
factAdmissions.AdmitCCYYMM
Order by factAdmissions.ContractCode,
factAdmissions.BenefitPlanCode,
factAdmissions.AdmitCCYYMM;
Dataset2:
Select
(factMembership.ContractCode+'-'+ factMembership.BenefitPlanCode) As Product,
EffectiveCCYYMM,
ISNULL(count(Distinct MemberId),0) As MemberCount
From factMembership
Where EffectiveCCYYMM >= '200701'
AND BusinessUnitCode In('EP','HF', 'VN')
AND EffectiveCCYYMM BETWEEN (#StartDate) AND (#EndDate)
AND factMembership.ContractCode + '-' +factMembership.BenefitPlanCode IN (#Product)
Group by
ContractCode,
BenefitPlanCode,
EffectiveCCYYMM
Order by 1;
Dataset 1: Output 201101 201102 201103
Cost $420,627 $73,608 $93,792
Dataset 2:Output 201103
Memberscount 400
Desired Output: 201103
3 Month Rolling Average $490.02

DECLARE #Admission TABLE (
Code VARCHAR(100),
YearMonth CHAR(6),
TotalPaid DECIMAL
)
DECLARE #Membership TABLE (
Code VARCHAR(100),
YearMonth CHAR(6),
TotalMembers int
)
INSERT #Admission (Code, YearMonth, TotalPaid) VALUES
('123', '201301', 10),
('xyz', '201301', 20),
('123', '201302', 15),
('xyz', '201302', 15),
('123', '201303', 30),
('xyz', '201303', 40),
('123', '201304', 30),
('xyz', '201304', 40),
('123', '201305', 15),
('xyz', '201305', 15),
('123', '201306', 20),
('xyz', '201306', 10)
INSERT #Membership (Code, YearMonth, TotalMembers) VALUES
('123', '201301', 1),
('xyz', '201301', 2),
('123', '201302', 1),
('xyz', '201302', 3),
('123', '201303', 3),
('xyz', '201303', 5),
('123', '201304', 3),
('xyz', '201304', 5),
('123', '201305', 1),
('xyz', '201305', 3),
('123', '201306', 1),
('xyz', '201306', 2)
SELECT
a.Code,
a.YearMonth,
TotalPaid,
TotalMembers,
TotalPaid / CAST(TotalMembers AS FLOAT) CostPerMember,
CAST(a.YearMonth + '01' AS date) AS ProperDate
INTO #Values
FROM #Admission a
INNER JOIN #Membership m ON a.Code = m.Code AND a.YearMonth = m.YearMonth
SELECT
main.Code,
main.YearMonth,
main.TotalPaid,
main.TotalMembers,
COUNT(*) AS PointsToAverage,
SUM(sub.TotalPaid) AS TotalPaidOverPeriod,
SUM(sub.TotalMembers) AS TotalMembersOverPeriod,
SUM(sub.CostPerMember) AS TotalCostPerMemberOverPeriod,
ROUND(SUM(sub.TotalPaid) / CAST(COUNT(*) AS FLOAT), 2) AS RollingAverageTotalPaid,
ROUND(SUM(sub.TotalMembers) / CAST(COUNT(*) AS FLOAT), 2) AS RollingAverageTotalMembers,
ROUND(SUM(sub.CostPerMember) / CAST(COUNT(*) AS FLOAT), 2) AS RollingAverageCostPerMember
FROM
#Values main
INNER JOIN #Values sub ON main.Code = sub.Code
AND sub.ProperDate BETWEEN DATEADD(month, -2.9, main.ProperDate) AND main.ProperDate
GROUP BY
main.Code,
main.YearMonth,
main.TotalPaid,
main.TotalMembers

Related

Fill table using last value per user

I've got data on customers balance on a daily basis, with the following structure in BigQuery:
CREATE TABLE if not EXISTS balance (
user_id int,
updated_ag DATE,
value float
);
INSERT INTO balance VALUES
(1, '2021-01-01', 0),
(1, '2021-01-02', 1),
(1, '2021-01-05', 2),
(1, '2021-01-07', 5),
(2, '2021-01-01', 5),
(2, '2021-01-03', 0),
(2, '2021-01-04', 1),
(2, '2021-01-06', 2);
I have one row for a user on a given day if the balance on that day changed.
I'd like to complete the data for each user by putting the balance of the last day where there was an update, with the dates being between the first day with balance and last date in the table.
So, the output table in this example would have the following values:
(1, '2021-01-01', 0),
(1, '2021-01-02', 1),
(1, '2021-01-03', 1),
(1, '2021-01-04', 1),
(1, '2021-01-05', 2),
(1, '2021-01-06', 2),
(1, '2021-01-07', 5),
(2, '2021-01-01', 5),
(2, '2021-01-02', 5),
(2, '2021-01-03', 0),
(2, '2021-01-04', 1),
(2, '2021-01-05', 1),
(2, '2021-01-06', 2),
(2, '2021-01-07', 2)
What's the simplest way of doing this in BigQuery?
Try this one:
select user_id, generated_date, value
from (
select
*,
ifnull(lead(date) over(partition by user_id order by date) - 1, max(date) over()) date_to
from balance
), unnest(generate_date_array(date, date_to, interval 1 day)) generated_date

SQL query cast from a group

I'm trying to make a query whit a 'cast group by' - output.
But not quit sure how to tackle this or even to start (I think i need cast and group by but not sure).
I have some code below which hopefully explains what I'm trying to do.
Could somebody help me to make a query to have the correct output?
CREATE TABLE buyers
(
id int,
buyer_name varchar(20)
);
INSERT INTO buyers
VALUES (1, 'harry'), (2, 'zoe'), (3, 'rose');
CREATE TABLE grocery
(
id int,
name VARCHAR(20),
id_buyers int
);
INSERT INTO grocery
VALUES (1, 'milk', 1), (2, 'milk', 1), (3, 'ham', 1), (4, 'bread', 2),
(5, 'bread', 2), (6, 'bread', 2), (6, 'milk', 2), (7, 'milk', 2),
(8, 'ham', 3);
CREATE TABLE wanted_output
(
name VARCHAR(20),
stuffed_lists VARCHAR(20)
);
INSERT INTO wanted_output
VALUES ('harry', '2x milk, 1x ham'), ('zoe', '3x bread, 2x milk'),
('rose', '1x ham ');
EDIT :
at this point I have this (not working):
SELECT
buyers.buyer_name,
COALESCE(
STUFF(
(SELECT ' , ' +
CAST( grocery.name AS varchar(max))
from grocery
where grocery.id_buyers = buyers.id
FOR XML PATH('')
), 1, 2, '' ),'')
AS wanted_output
FROM buyers
nvm afther some fideling :
SELECT
buyers.buyer_name,
COALESCE(
STUFF(
(SELECT ' , ' + cast(count(*) as varchar(4)) + ' x ' +
CAST( grocery.name AS varchar(max))
from grocery
where grocery.id_buyers = buyers.id
group by grocery.name
FOR XML PATH('')
), 1, 2, '' ),'')
AS wanted_output
FROM buyers

SQL query to get all records with sum less than 10000

I have data in a table which looks like the below data set.
I want to get a group of those items whose price sum is less than 10000.
CREATE TABLE Table1
(slno int, item varchar(10), price int);
INSERT INTO Table1
(slno, item, price)
VALUES
(1, 'item1', 1000),
(2, 'item2', 2000),
(3, 'item3', 3000),
(4, 'item4', 4000),
(5, 'item5', 5000),
(6, 'item6', 6000),
(7, 'item7', 10000),
(8, 'item8', 2000),
(9, 'item9', 8000),
(10, 'item10', 2500),
(11, 'item11', 9000),
(12, 'item12', 1000),
(13, 'item13', 2500),
(14, 'item14', 2500),
(15, 'item15', 2500);
My sql query looks like this:
SELECT slno, item,price
FROM
(
SELECT slno, item,price
(
SELECT SUM(price)
FROM Table1
WHERE slno<= t.slno
) total
FROM Table1 t
) q
WHERE total <= 1000
ORDER BY item
It's not giving the expected result though, it's giving only one set of records:
(1, 'item1', 1000),
(2, 'item2', 2000),
(3, 'item3', 3000),
(4, 'item4', 4000)
whereas I need it to give me something like this:
1ST SET
(1, 'item1', 1000),
(2, 'item2', 2000),
(3, 'item3', 3000),
(4, 'item4', 4000)
2ND SET
(7, 'item7', 10000),
#GordonLinoff
This type of operation requires a recursive CTE. In this case, you can assign a group to each row using such logic. The following assumes that slno has no gaps as in your example data:
with cte as (
select slno, item, price, 1 as grp, price as running_price
from table1
where slno = 1
union all
select t1.slno, t1.item, t1.price,
(case when t1.price + cte.running_price > 10000 then grp + 1 else grp end),
(case when t1.price + cte.running_price > 10000 then t1.price else cte.running_price + t1.price end)
from cte join
table1 t1
on t1.slno = cte.slno + 1
)
select *
from cte
order by slno;
Here is a db<>fiddle.

Select duplicate persons with duplicate memberships

SQL Fiddle with schema and my intial attempt.
CREATE TABLE person
([firstname] varchar(10), [surname] varchar(10), [dob] date, [personid] int);
INSERT INTO person
([firstname], [surname], [dob] ,[personid])
VALUES
('Alice', 'AA', '1/1/1990', 1),
('Alice', 'AA', '1/1/1990', 2),
('Bob' , 'BB', '1/1/1990', 3),
('Carol', 'CC', '1/1/1990', 4),
('Alice', 'AA', '1/1/1990', 5),
('Kate' , 'KK', '1/1/1990', 6),
('Kate' , 'KK', '1/1/1990', 7)
;
CREATE TABLE person_membership
([personid] int, [personstatus] varchar(1), [memberid] int);
INSERT INTO person_membership
([personid], [personstatus], [memberid])
VALUES
(1, 'A', 10),
(2, 'A', 20),
(3, 'A', 30),
(3, 'A', 40),
(4, 'A', 50),
(4, 'A', 60),
(5, 'T', 70),
(6, 'A', 80),
(7, 'A', 90);
CREATE TABLE membership
([membershipid] int, [memstatus] varchar(1));
INSERT INTO membership
([membershipid], [memstatus])
VALUES
(10, 'A'),
(20, 'A'),
(30, 'A'),
(40, 'A'),
(50, 'T'),
(60, 'A'),
(70, 'A'),
(80, 'A'),
(90, 'T');
There are three tables (as per the fiddle above). Person table contains duplicates, same people entered more than once, for the purpose of this exercise we assume that a combination of the first name, surname and DoB is enough to uniquely identify a person.
I am trying to build a query which will show duplicates of people (first name+surname+Dob) with two or more active entries in the Person table (person_membership.person_status=A) AND two or more active memberships (membership.mestatus=A).
Using the example from SQL Fiddle, the result of the query should be just Alice (two active person IDs, two active membership IDs).
I think I'm making progress with the following effort but it looks rather cumbersome and I need to remove Katie from the final result - she doesn't have a duplicate membership.
SELECT q.firstname, q.surname, q.dob, p1.personid, m.membershipid
FROM
(SELECT
p.firstname,p.surname,p.dob, count(*) as cnt
FROM
person p
GROUP BY
p.firstname,p.surname,p.dob
HAVING COUNT(1) > 1) as q
INNER JOIN person p1 ON q.firstname=p1.firstname AND q.surname=p1.surname AND q.dob=p1.dob
INNER JOIN person_membership pm ON p1.personid=pm.personid
INNER JOIN membership m ON pm.memberid = m.membershipid
WHERE pm.personstatus = 'A' AND m.memstatus = 'A'
Since you are using SQL Server windows function will be handy for this scenario. The following will give you the expected output.
SELECT firstname,surname,dob,personid,memberid
from(
SELECT firstname,surname,dob,p.personid,memberid
,Rank() over(partition by p.firstname,p.surname,p.dob order by p.personid) rnasc
,Rank() over(partition by p.firstname,p.surname,p.dob order by p.personid desc) rndesc
FROM [StagingGRG].[dbo].[person] p
INNER JOIN person_membership pm ON p.personid=pm.personid
INNER JOIN membership m ON pm.memberid = m.membershipid
where personstatus='A' and memstatus='A')a
where a.rnasc+rndesc>2
You have to add Group by and Having clause to return duplicate items only-
SELECT
person.firstname,person.surname,person.dob
FROM
person, person_membership, membership
WHERE
person.personid=person_membership.personid AND person_membership.memberid = membership.membershipid
AND
person_membership.personstatus = 'A' AND membership.memstatus = 'A'
GROUP BY
person.firstname,person.surname,person.dob
HAVING COUNT(1) > 1

Subqueries With Multiple Tables

Good day, need your help on my Vehicle Inspection Database. You can see below the structure, you can see it also in here http://sqlfiddle.com/#!3/4ab7e . What I need from this is to extract The Number of Vehicles With Atleast One (1) Defect or Violation Per PROJECT. In the schema below The Total for Project 4 = two (2) vehicles AND Project 9 = 1 vehicle.
Columns Needed are [Project_Name],[Vehicle_Type],[yy],[mm],[Total]
-- Vehicle Inspection Database --
-- Vehicle_Type Table
CREATE TABLE VehicleType
([VehicleTypeId] int,
[Type] varchar (36));
INSERT INTO VehicleType ([VehicleTypeId],[Type])
VALUES (1, 'Light Vehicle'),
(2, 'Tanker'),
(3, 'Goods');
-- Car Table
CREATE TABLE Vehicle
([VehicleID] varchar(36),
[PlateNo] varchar(36),
[VehicleTypeId] int,
[Project] int);
INSERT INTO Vehicle ([VehicleID], [PlateNo],[VehicleTypeId], [Project])
VALUES('A57D4151-BD49-4B44-AF10-000F1C298E05', '8112AG', 1, 4),
('C7095628-AE88-4DD0-A4FD-00363EAB767F', '60070 AD2', 2, 9),
('E714CCD7-E56C-46A8-89D5-003CA5BF6094', '68823 AD1', 3, 9);
-- Event Table
CREATE TABLE Event
([EventID] int,
[VehicleID] varchar(36),
[EventTime] smalldatetime,
[TicketStatus] varchar (10)) ;
INSERT INTO Event([EventID], [VehicleID], [EventTime], TicketStatus)
VALUES (1, 'A57D4151-BD49-4B44-AF10-000F1C298E05', '20130701', 'Open'),
(2, 'A57D4151-BD49-4B44-AF10-000F1C298E05', '20130702', 'Close'),
(3, 'A57D4151-BD49-4B44-AF10-000F1C298E05', '20130703', 'Close'),
(4, 'C7095628-AE88-4DD0-A4FD-00363EAB767F','20130705', 'Open'),
(5, 'C7095628-AE88-4DD0-A4FD-00363EAB767F','20130710', 'Open');
-- Event_Defects Table
CREATE TABLE EventDefects
([EventDefectsID] int,
[EventID] int,
[Status] varchar(15),
[DefectID] int) ;
INSERT INTO EventDefects ([EventDefectsID], [EventID], [Status], [DefectID])
VALUES
-- 1st Inspection for PlateNo. 8112AG
(1, 1, 'YES', 1),
(2, 1, 'NO', 2),
(3, 1, 'YES',3),
(4, 1, 'N/A', 4),
(5, 1, 'N/A', 5),
-- 2nd Inspection for PlateNo. 8112AG
(6, 2, 'NO', 1),
(7, 2, 'NO', 2),
(8, 2, 'NO', 3),
(9, 2, 'N/A', 4),
(10,2, 'N/A', 5),
-- 3rd Inspection for PlateNo. 8112AG
(11, 3, 'NO', 1),
(12, 3, 'NO', 2),
(13, 3, 'NO', 3),
(14, 3, 'NO', 4),
(15, 3, 'NO', 5),
-- 1st Inspection for PlateNo. 60070 AD2
(16, 3, 'NO', 1),
(17, 3, 'NO', 2),
(18, 3, 'NO', 3),
(19, 3, 'N/A', 4),
(20, 3, 'N/A', 5);
-- Defects Table
CREATE TABLE Defects
([DefectID] int,
[DefectsName] varchar (36),
[DefectClassID] int) ;
INSERT INTO Defects ([DefectID], [DefectsName], [DefectClassID])
VALUES (1, 'TYRE', 1),
(2, 'BRAKING SYSTEM', 1),
(3, 'MIRRORS AND WINDSCREEN', 2),
(4, 'OVER SPEEDING', 3),
(5, 'NOT WEARING SEATBELTS', 3);
-- Defect_Class Table
CREATE TABLE DefectClass
([Description] varchar (15),
[DefectClassID] int) ;
INSERT INTO DefectClass ([DefectClassID], [Description])
VALUES (1, 'CATEGORY A'),
(2, 'CATEGORY B'),
(3, 'CATEGORY C');
Do all the joins as inner joins... and it'll eliminate records that are empty.
Can you check if this works for you?
SELECT Vehicle.VehicleID from Vehicle
INNER JOIN Event ON Vehicle.VehicleID = Event.VehicleID
INNER JOIN EventDefects ON EventDefects.EventID = Event.EventID
INNER JOIN Defects ON EventDefects.DefectID = Defects.DefectID
GROUP BY Vehicle.VehicleID;