Query for summation up to certain rows in a table - sql

I have a query like:
select
CONVERT(VARCHAR(7),[startdatetime],111) AS [year-month],
nm.nameLine1,
sum(datediff(hour, startdatetime, enddatetime)) as total
from srl
inner join sr on srl= sr.ServiceRequestId
inner join Name nm on(sr.clientCustomerId = nm.customerId and nm.nameTypeId = 'OFICE')
where (startdatetime >= '08-01-2011 00:00:00.000' and enddatetime <= '10-31-2011 00:00:00.000')
group by nm.nameLine1, [year-month]
order by nm.nameLine1, [year-month]
output of the above query is::
year-month nameLine1 total
---------- --------- -----------
2011/08 B 4
2011/09 B 7
2011/10 B 0
2011/08 E 167
2011/09 E 212
2011/10 E 131
2011/08 L 14
2011/09 L 23
2011/10 L 3
2011/08 O 18
2011/09 O 8
2011/10 O 7
2011/08 S 43
2011/09 S 60
2011/10 S 60
Now my question is, what should I do in the query to get the summation in a different column called nameLine1total for a single nameLine1. The output should be like this:
year-month nameLine1 total nameLine1total
---------- --------- ----------- ---------------
2011/08 B 4
2011/09 B 7
2011/10 B 0 11
2011/08 E 167
2011/09 E 212
2011/10 E 131 510
2011/08 L 14
2011/09 L 23
2011/10 L 3 40
2011/08 O 18
2011/09 O 8
2011/10 O 7 33
2011/08 S 43
2011/09 S 60
2011/10 S 60 163

what variety of sql are you using - sql server, mysql, etc. Also, it doesn't matter for your answer but do you really want <= 10-31-2011 00:00:00, so that data from october 31 isn't included in your query? Also, how are you going to use that query - if it's for a reporting tool, quite likely the reporting tool can calculate it more easily than sql can. If it would be acceptable to have the nameline total included on every row, that would be easier - e.g. if all three rows for "S" said "163" in the last column, is that okay?

Because of convert(.., .., 111) I assume that this is a SQL Server, if it's 2005+ I would use CTE:
with cte (ym, n, total, row) as (
select *, row_number() over(partition by nameLine1 order by nameLine1, [year-month]) from (
select
convert(varchar(7),[startdatetime],111) as [year-month],
nm.nameLine1,
sum(datediff(hour, startdatetime, enddatetime)) as total
from srl
inner join sr on srl= sr.ServiceRequestId
inner join Name nm on
sr.clientCustomerId = nm.customerId and
nm.nameTypeId = 'OFICE'
where startdatetime >= '08-01-2011 00:00:00.000' and enddatetime <= '10-31-2011 00:00:00.000'
group by nm.nameLine1, [year-month]
order by nm.nameLine1, [year-month]
) t
)
select c.ym as [year-month], c.n as [nameLine1], c.total as [total], g.total as [nameLine1total]
from cte c
left join (
select max(row) as row, sum(total) as total, n
from cte group by n
) g on c.n = g.n and c.row = g.row

Related

Join and max date with null values

I need to return the most recent (max)date for a patient_ID where a vital value exists - it's not simply the max. encounter date for the patient_ID, it must have a corresponding vital value. I also only want encounters where vitals value <>'' and Date >= '2020-01-01' and vital_ID = 232268.
Encounters (enc)
Patient_ID
Encounter_ID
Date
1
11
1/4/2020
1
12
1/15/2020
1
13
3/6/2020
2
14
1/12/2020
3
15
3/16/2020
3
16
4/19/2020
4
17
6/2/2020
4
18
6/12/2020
4
19
9/1/2020
Vitals
Encounter_ID
Vital_ID
Value
11
232268
4.8
12
232268
4.6
14
232268
3.1
16
232268
3.2
17
232268
4.1
18
232268
4.7
Desired Outcome
Patient_ID
Encounter_ID
Date
Value
1
12
3/6/2020
4.6
2
14
1/12/2020
3.1
3
16
4/19/2020
3.2
4
18
9/1/2020
4.7
I tried this, but it returned only the vitals_encounter_ID IF it = max(date) of the encounter for the patient_ID (so did not include patient_ID if vitals were not taken on the max(date) - for instance, it negates patient_ID 1 all together because vitals weren't taken on encounter_ID 13:
select v.encounterID, e.patientID, e.date, v.value, v.vitalID
from vitals v
left join enc e on
e.encounterID = v.encounterID and
v.vitalID = 232268 and
v.value <> '' and
e.date = (select max(date) from enc where patientID=e.patientID)
where e.date >= '2020-01-01'
Cognos 8. I'm new, so please don't eat me alive...
If I follow you correctly, you need the two tables in the correlated subquery:
select v.encounterid, e.patientid, e.date, v.value, v.vitalid
from enc e
inner join vitals v on v.encounterid = e.encounterid
where
v.vitalid = 232268
and v.value <>''
and e.date = (
select max(e1.date)
from enc e1
inner join vitals v1 on v1.encounterid = e1.encounterid
where
e1.patientid = e.patientid
and v1.vitalid = v.vitalid
and v1.value <> ''
and e.date >= '2020-01-01'
)
I don't know if Cognos supports window functions. But if it does, the query can be much simpler phrased:
select *
from (
select v.encounterid, e.patientid, e.date, v.value, v.vitalid,
row_number() over(partition by e.patientid order by e.date)
from enc e
inner join vitals v on v.encounterid = e.encounterid
where v.vitalid = 232268 and v.value <> ''
) t
where rn = 1

SQL query group by with null values is returning duplicates

I have following query
My #dates table has following records:
month year saledate
9 2020 2020-09-01
10 2020 2020-10-01
11 2020 2020-11-01
with monthlysalesdata as(
select month(salesdate) as salemonth, year(salesdate) as saleyear,salesrepid, salespercentage
from salesrecords r
join #dates d on d.saledate = r.salesdate
group by salesrepid, salesdate),
averagefor3months as(
select 0 as salemonth, 0 as saleyear, salesrepid, salespercentage
from monthlysalesdata
group by salesrepid)
finallist as(
select * from monthlysalesdata
union
select * from averagefor3months
This query returns following records which gives duplicate for a averagefor3months result set when there is null record in the first monthlyresultdata. how to achieve average for 3 months as one record instead of having duplicates?
salesrepid salemonth saleyear percentage
232 0 0 null -------------this is the duplicate record
232 0 0 90
232 9 2020 80
232 10 2020 null
232 11 2020 100
My first cte has this result:
salerepid month year percentage
---------------------------------------------
232 9 2020 80
232 10 2020 null
232 11 2020 100
My second cte has this result:
salerepid month year percentage
---------------------------------------------
232 0 0 null
232 0 0 90
How to avoid the duplicate record in my second cte,
I suspect that you want a summary row per sales rep based on some aggregation. Your question is not clear on what is needed for the aggregation, but something like this:
with ym as (
select r.salesrepid, d.year, d.month, sum(<something>) as whatever
from salesrecords r join
#dates d
on d.saledate = r.salesdate
group by r.salesrepid, d.year, d.month
)
select ym.*
from ym
union all
select salesrepid, null, null, avg(whatever)
from hm
group by salesrepid;
I updated to selected the group by from the table directly instead of the previous cte and got my results. Thank you all for helping
with ym as (
select r.salesrepid, d.year, d.month, sum(<something>) as whatever
from salesrecords r join
#dates d
on d.saledate = r.salesdate
group by r.salesrepid, d.year, d.month
),
threemonthsaverage as(
select r.salesrepid, r.year, r.month, sum(something) as whatever
from salesrecords as r
group by salesrepid)
select ym *
union
select threemonthsaverage*

How to get sum of unique items from current and previous rows but ignore repeating items Oracle SQL

How to write a query in Oracle SQL to get from the table I have to the table I want as below:
What I have:
Date Item Units
----------- -------- --------
05-NOV-2018 A 3
05-NOV-2018 E 4
09-NOV-2018 C 7
16-NOV-2018 B 9
16-NOV-2018 D 4
21-NOV-2018 A 5
29-NOV-2018 B 12
29-NOV-2018 C 10
29-NOV-2018 F 6
29-NOV-2018 A 8
What I want:
Date Total_Units_Per_Day
----------- --------------------
05-NOV-2018 7
09-NOV-2018 14
16-NOV-2018 27
21-NOV-2018 29
29-NOV-2018 44
How the Total_Units_Per_Day column was calculated:
Date Item Units Total_Unique_Items_Accumulated_Per_Day Total_Units_Per_Day
----------- -------- -------- ------------------------------- --------------------
05-NOV-2018 A 3 A, E 7
05-NOV-2018 E 4 A, E 7
09-NOV-2018 C 7 A, E, C 14
16-NOV-2018 B 9 A, E, C, B, D 27
16-NOV-2018 D 4 A, E, C, B, D 27
21-NOV-2018 A 5 A, E, C, B, D 29
29-NOV-2018 B 12 A, E, C, B, D, F 44
29-NOV-2018 C 10 A, E, C, B, D, F 44
29-NOV-2018 F 6 A, E, C, B, D, F 44
29-NOV-2018 A 8 A, E, C, B, D, F 44
As we progress through each day (from 5th to 29th Nov 2018), sum the units of each item in that day and all the previous day. However, do not take into account the units of the item from the previous(s) day if it is already present in the current day.
For example on 21th Nov 2018, Total_Units_Per_Day = 29. This is done by summing the units of all the previous items but use:
A=5 units from (21th Nov 2018) instead of A=3 units from (5th Nov 2018)
Is this type of query possible? Any help would be appreciated (: Thanks!
This is quite complicated. You seem to want the most recent value for each item.
If you have a finite list of items, then you can take a brute force approach:
select dte,
(lag(case when item = 'A' then units end ignore nulls, 1, 0) over (order by dte) +
lag(case when item = 'B' then units end ignore nulls, 1, 0) over (order by dte) +
lag(case when item = 'C' then units end ignore nulls, 1, 0) over (order by dte) +
lag(case when item = 'D' then units end ignore nulls, 1, 0) over (order by dte) +
lag(case when item = 'E' then units end ignore nulls, 1, 0) over (order by dte) +
lag(case when item = 'F' then units end ignore nulls, 1, 0) over (order by dte)
) as total_units_per_day
from t;
Here is a db<>fiddle.
EDIT:
Here is a more general approach:
select dte, sum(units) as total_units_per_day
from (select d.dte, t.item, t.units, row_number() over (partition by t.item, d.dte order by t.dte desc) as seqnum
from (select distinct dte from t) d join
t
on t.dte <= d.dte
) td
where seqnum = 1
group by dte
order by dte;
And a db<>fiddle for this.
This would be the classical aprpoach to solve your problem:
Simple sum the units per day and than calculate the cummulative sum using analytic function.
with tot as (
select trans_date, sum(units) total_unit
from tab
group by trans_date)
select trans_date,
sum(total_unit) over (order by trans_date) total_unit_cum
from tot
order by 1
;
TRANS_DATE TOTAL_UNIT_CUM
------------------- --------------
05.11.2018 00:00:00 7
09.11.2018 00:00:00 14
16.11.2018 00:00:00 27
21.11.2018 00:00:00 32
29.11.2018 00:00:00 68
But this provides a higher results that expected.
The reason is that within a item your units are already cummulated.
So in the first step clean up the units and calculate the real delta value
by subtracting the LAG value - from previous record, with default zero - lag(units,1,0) ...
select
trans_date, item, units units_orig,
units - lag(units,1,0) over (partition by item order by trans_date) units
from tab
order by 1,2;
TRANS_DATE I UNITS_ORIG UNITS
------------------- - ---------- ----------
05.11.2018 00:00:00 A 3 3
05.11.2018 00:00:00 E 4 4
09.11.2018 00:00:00 C 7 7
16.11.2018 00:00:00 B 9 9
16.11.2018 00:00:00 D 4 4
21.11.2018 00:00:00 A 5 2
29.11.2018 00:00:00 A 8 3
29.11.2018 00:00:00 B 12 3
29.11.2018 00:00:00 C 10 3
29.11.2018 00:00:00 F 6 6
Finaly simple combine the both queries
with clean as (
select
trans_date, item, units units_orig,
units - lag(units,1,0) over (partition by item order by trans_date) units
from tab),
tot as (
select trans_date, sum(units) total_unit
from clean
group by trans_date)
select trans_date,
sum(total_unit) over (order by trans_date) total_unit_cum
from tot
order by 1
;
TRANS_DATE TOTAL_UNIT_CUM
------------------- --------------
05.11.2018 00:00:00 7
09.11.2018 00:00:00 14
16.11.2018 00:00:00 27
21.11.2018 00:00:00 29
29.11.2018 00:00:00 44
This is not the shorted possible query, but is simple to understand...

Select Highest value against each record in SQL

I am new to SQL and the problem I am having is that I have the value for alot assets in a table.
I need to get the highest speed for each asset in that table.
I have tried searching google but I found the MAX() function of SQL.
I don't need the MAX() because that will only give me one record with the highest value. I need the highest for each asset:
e.g.
iAssetId fSpeedKPH
1 78
5 77
5 80
8 74
8 81
8 88
8 111
24 71
24 78
24 79
24 79
24 82
24 84
24 90
24 91
24 92
I have highlighted the highest row for each asset i.e. AssetId = 1, 5, 24 and 8
These are the rows I need to select.
What is the most efficient way?
Do I have to loop through this result-set returned by the SQL I have written?
EDIT:
My SQL:
DECLARE #dateMinusDay datetime = DateAdd(dd, -1, GetDate())
select vm.iAssetId, max(vm.fSpeedKPH), vm.dtUTCDateTime, ge.sGeofenceName from VehicleMonitoringLog vm
inner join Geofences ge on ge.iGeofenceId = vm.iGeofenceId
where vm.iGeofenceId != 1 AND vm.fSpeedKPH > 70 AND (vm.dtUTCDateTime > #dateMinusDay AND vm.dtUTCDateTime < GETDATE())
group by
vm.iAssetId,vm.fSpeedKPH, vm.dtUTCDateTime, ge.sGeofenceName
select iAssetId, max(fSpeedKPH)
from AssetsTable
group by iAssetId
SELECT iAssetId, fSpeedKPH
FROM (
SELECT iAssetId, fSpeedKPH
,ROW_NUMBER() OVER (PARTITION BY iAssetId ORDER BY fSpeedKPH DESC) AS RN
FROM Table_Name )Sub
WHERE RN = 1
UPDATE
DECLARE #dateMinusDay datetime = DateAdd(dd, -1, GetDate())
SELECT Q.iAssetId, Q.dtUTCDateTime, Q.sGeofenceName
FROM (
select vm.iAssetId
, vm.dtUTCDateTime
, ge.sGeofenceName
,ROW_NUMBER() OVER (PARTITION BY vm.iAssetId ORDER BY vm.fSpeedKPH DESC) AS RN
from VehicleMonitoringLog vm inner join Geofences ge
on ge.iGeofenceId = vm.iGeofenceId
where vm.iGeofenceId != 1 AND vm.fSpeedKPH > 70
AND (vm.dtUTCDateTime > #dateMinusDay --<-- Instead of variable you can use GETDATE() - 1
AND vm.dtUTCDateTime < GETDATE())
)Q
WHERE RN = 1

Group by in subquery?

I need some help with group by in subquery. Can you help me to solve this grouping problem:
select
DATEPART(wk, oh. ExportDate) as wk,
DATEPART(dw,oh.ExportDate) as day,
ro.Name,
Pallets = sum(oh.Pallets),
Box = (select count(Number) from OrderItem where ID_OrderHead = oh.Id)
from
OrderHeadPDAEvent ohpe
left outer join OrderHead oh on oh.Id = ohpe.ID_OrderHeader
left outer join Route ro on oh.ID_Route = ro.ID
where
ID_Route = '00000000-0000-0000-0000-000000000000'
and
oh.ExportDate > dbo.GetStartOfDay('2012-08-01 14:35:00.000' )
and
oh.ExportDate < dbo.GetEndOfDay('2013-08-08 14:35:00.000')
group by
oh.ExportDate, ro.Name, oh.ID
order by
DATEPART(wk, oh. ExportDate)
And data looks like this:
26 3 Standard - Uten rute 5 49
26 3 Standard - Uten rute 2 45
26 3 Standard - Uten rute 2 38
26 3 Standard - Uten rute 1 26
26 3 Standard - Uten rute 1 64
26 3 Standard - Uten rute 2 45
26 3 Standard - Uten rute 3 64
I want to sum all rows to get them in 1 row only.
Try this one -
SELECT
DATEPART(wk, oh.ExportDate) AS wk
, DATEPART(dw, oh.ExportDate) AS day
, ro.name
, Pallets = SUM(oh.Pallets)
, Box = SUM(n)
FROM dbo.OrderHeadPDAEvent ohpe
LEFT JOIN dbo.OrderHead oh ON oh.ID = ohpe.ID_OrderHeader
LEFT JOIN dbo.[Route] ro ON oh.ID_Route = ro.ID
LEFT JOIN (
SELECT n = COUNT(number), ID_OrderHead
FROM dbo.OrderItem
GROUP BY ID_OrderHead
) t ON t.ID_OrderHead = oh.ID
WHERE ID_Route = '00000000-0000-0000-0000-000000000000'
AND oh.ExportDate BETWEEN
dbo.GetStartOfDay('2012-08-01 14:35:00.000')
AND
dbo.GetEndOfDay('2013-08-08 14:35:00.000')
GROUP BY
oh.ExportDate
, ro.name
ORDER BY wk