SQL - Get Sum of Values with same Date - sql

I'm sure I've done this type of operation a 1000 times before but for some reason this is not working for me. I'm doing a report to determine if a patient receive medication on a day. So regardless if they get 1 does or 5 doses in a day the value should be 1. Staff also do corrections on the system, that come in as negative values. So I need to sum all of the dose value for each day, if it is a + value then its 1, otherwise its a 0.
All i want to accomplish at this point is to have 1 row for each date as either 1 or 0.
Here is my SQL Query to sum the values:
SELECT
DIM_DRUG_NAME_SHORT.Drug_Name_Short AS 'Med_Name_Short'
, SUM(Baseline.Doses) as 'DOT'
, Day(Baseline.Dispense_Date) as 'd_Date'
FROM
FACT_AMS_Baseline_Report Baseline
INNER JOIN DIM_DRUG_NAME_SHORT ON Baseline.Med_Name_ID = DIM_DRUG_NAME_SHORT.Drug_Name_Long
INNER JOIN DIM_Date tDate ON Baseline.Dispense_Date = tDate.Date
WHERE
Baseline.Encounter = '00000001/01'
GROUP BY
DIM_DRUG_NAME_SHORT.Drug_Name_Short
, Baseline.Dispense_Date
, Doses
Order By
Drug_Name_Short
For time being I'm just pulling one encounter out of the data set to test with.
This is the output i'm getting. I also included the Day in the select just to show that the same day is coming through twice and they are not getting Summed.
Here is a sample of the output I get:
Med_Name_Short DOT day of month
CEFTRIAXONE 1 15
CEFTRIAXONE 1 16
CEFTRIAXONE 4 16
CEFTRIAXONE 1 17
CEFTRIAXONE 1 18
CEFTRIAXONE 1 20
CEFTRIAXONE -3 21
CEFTRIAXONE 1 21
CEFTRIAXONE -1 23
PROPRANOLOL -1 24
PROPRANOLOL 3 24
PROPRANOLOL 1 25
PROPRANOLOL 2 26
PROPRANOLOL 2 27
What I was hoping to see in this was that Day 16 would be a 5, day 21 would be -2 and day 24 would be -2.
Any assistance would be greatly appreciated.
Thanks

Remove Doses from your Group By list. You are using an aggregate function on it (SUM) which is correct, so it should not be in the GROUP BY.

I don't think you should be grouping by doses. Without seeing your data, I can only guess that, for example, there are two doses of quantity 2 on the 16th.
So try:
SELECT
DIM_DRUG_NAME_SHORT.Drug_Name_Short AS 'Med_Name_Short'
, SUM(Baseline.Doses) as 'DOT'
, Day(Baseline.Dispense_Date) as 'd_Date'
FROM
FACT_AMS_Baseline_Report Baseline
INNER JOIN DIM_DRUG_NAME_SHORT ON Baseline.Med_Name_ID = DIM_DRUG_NAME_SHORT.Drug_Name_Long
INNER JOIN DIM_Date tDate ON Baseline.Dispense_Date = tDate.Date
WHERE
Baseline.Encounter = '00000001/01'
GROUP BY
DIM_DRUG_NAME_SHORT.Drug_Name_Short
, Baseline.Dispense_Date
Order By
Drug_Name_Short

Since you're aggregating on doses you should remove it from the group by, and to get either 1 or 0 for doses use a case expression:
SELECT
DIM_DRUG_NAME_SHORT.Drug_Name_Short AS 'Med_Name_Short'
, CASE WHEN SUM(Baseline.Doses) >= 1 THEN 1 ELSE 0 END AS 'DOT'
, Day(Baseline.Dispense_Date) as 'd_Date'
FROM
FACT_AMS_Baseline_Report Baseline
INNER JOIN DIM_DRUG_NAME_SHORT ON Baseline.Med_Name_ID = DIM_DRUG_NAME_SHORT.Drug_Name_Long
INNER JOIN DIM_Date tDate ON Baseline.Dispense_Date = tDate.Date
WHERE
Baseline.Encounter = '00000001/01'
GROUP BY
DIM_DRUG_NAME_SHORT.Drug_Name_Short
, Baseline.Dispense_Date
Order By
Drug_Name_Short
If the dispense_date is a datetime value you should probably use Day(Baseline.Dispense_Date) in the group by or remove the time part.
If you group by day and your data spans over more than one month you should either limit the range or include year and month as well so that data from different months/years don't get summed up.
With your sample data you should get a result like:
Med_Name_Short DOT day of month
CEFTRIAXONE 1 15
CEFTRIAXONE 1 16
CEFTRIAXONE 1 17
CEFTRIAXONE 1 18
CEFTRIAXONE 1 20
CEFTRIAXONE 0 21
CEFTRIAXONE 0 23
PROPRANOLOL 1 24
PROPRANOLOL 1 25
PROPRANOLOL 1 26
PROPRANOLOL 1 27

Related

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.

PL/SQL Calculate sum of start/end dates difference

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;

SQL Calculate weekly active users by device resolution

I want to calculate my apps weekly active users by device resolution.
I got dailyData table that looks like that:
raw_advertiser_id unique user id
screen_dimensions screen resolution information string
day_id an integer that counts how many days past from April 2017
It's unique on raw_advertiser_id and day_id
Here is a result of weekly active users by resolution for users who installed my app on April 25 2017, using PostgresSQL
WITH users AS (
SELECT raw_advertiser_id
FROM beep.dailydata
WHERE day_id < 25 + 7
GROUP BY 1
HAVING min(day_id) = 25
)
SELECT
screen_dimensions,
count(DISTINCT raw_advertiser_id) totalUsers,
count(DISTINCT CASE WHEN day_id > 25
THEN raw_advertiser_id END) weeklyActiveUsersCount,
round(count(DISTINCT CASE WHEN day_id > 25
THEN raw_advertiser_id END) :: NUMERIC / count(DISTINCT raw_advertiser_id) * 100,2) weeklyActiveUsersPercent
FROM beep.dailydata
JOIN users USING (raw_advertiser_id)
WHERE day_id < 25 + 7
GROUP BY 1
ORDER BY totalUsers DESC
Result:
resolution, totalUsers, weeklyActiveUsers, weeklyActiveUsersPercent
720x1280x2.00 10 2 20
320x1152x1.00 8 0 0
480x800x1.00 5 0 0
720x1280x0.00 3 1 33.33
480x854x1.00 3 0 0
720x1184x2.00 2 0 0
I would like to perform this query not only on April 25 but on all the next days as well and merge the result. In the above query to get the result of April 26, you simply need to replace the number 25 with 26. The question is how do I do this in a query?

Get datetime difference group by query results in one row

I have a table Master_History with structure as
Id_History Created_Date Subscription_Type rn
21 1/22/2016 16:31:29 1 1
22 1/22/2016 16:33:11 2 2
23 1/22/2016 16:33:37 1 3
24 1/22/2016 16:33:46 2 4
25 1/22/2016 16:33:53 1 5
26 1/22/2016 16:33:57 3 6
27 1/22/2016 16:34:01 2 7
28 1/22/2016 16:34:04 1 8
29 1/22/2016 16:34:08 3 9
I want to calculate date difference with adjacent rows which i have succeeded in calculating but results are getting distributed over multiple rows
Standard Plus Premium
122 NULL NULL
NULL 35 NULL
NULL NULL 3
I need
Results in one row like
Standard Plus Premium
122 35 3
For the last row (in this Subscription_Type is 3, the date difference should also get calculated on getdate() i.e. whenever i executed my query, seconds in Premium column should get reflected each time
Query :
WITH CTE
AS (
SELECT *
,ROW_NUMBER() OVER (
ORDER BY Created_Date
) AS rn
FROM Master_History
WHERE Client_ID = 11072
)
SELECT CASE
WHEN mc.Subscription_Type = 1
THEN Sum(DATEDIFF(second, mc.Created_Date, mp.Created_Date))
END AS [Standard]
,CASE
WHEN mc.Subscription_Type = 2
THEN Sum(DATEDIFF(second, mc.Created_Date, mp.Created_Date))
END AS Plus
,CASE
WHEN mc.Subscription_Type = 3
THEN Sum(DATEDIFF(second, mc.Created_Date, mp.Created_Date))
END AS Premium
FROM CTE mc
JOIN CTE mp ON mc.rn = mp.rn - 1
GROUP BY mc.Subscription_Type
try this
select
count(Standard.*) Standard_,
count(Plus.*) Plus_,
count(Premium.*) Premium_
from
Master_History master_
left outer join Master_History Standard on Standard.Subscription_Type = 1
and master_.Subscription_Type = Standard.Subscription_Type
left outer join Master_History Plus on Plus.Subscription_Type = 2
and master_.Subscription_Type = Plus.Subscription_Type
left outer join Master_History Premium on Premium.Subscription_Type = 3
and master_.Subscription_Type = Plus.Subscription_Type
where
convert(date,master_.Created_Date) < convert(date,getdate()) and
convert(date,master_.Created_Date) < convert(date,Standard.Created_Date) and
convert(date,master_.Created_Date) < convert(date,Plus.Created_Date) and
convert(date,master_.Created_Date) < convert(date,Premium.Created_Date)

Conditional SUM in SQL Server 2014

I am using SQL Server 2014. When I was testing my code I noticed a problem.
Assume that max personal hour is 80 hours.
SELECT
lsm.EmployeeName,
pd.absenceDate,
pd.amountInDays * 8 AS [HoursReported],
pd.status,
(SUM(CASE WHEN pd.[status]='App' THEN (pd.amountInDays * 8)
ELSE 0 END) OVER (partition by lsm.[EmployeeName] order by pd.absenceDate)) AS [TotalUsedHours]
( #maxPSHours ) - (sum(
CASE WHEN pd.[status]='App' THEN (pd.amountInDays * 8)
ELSE 0 END)
over (
partition by lsm.[EmployeeName] order by pd.absenceDate)) AS [TotalRemainingHours]
FROM
[LocationStaffMembers] lsm
INNER JOIN
[PersonalDays] pd ON lsm.staffMemberId = pd.staffMemberId
This query returns these results:
EmployeeName AbsenceDate HoursReported Status TotalUsdHrs TotalRemingHrs
X 11/11/2015 4 approved 4 76
X 11/15/2015 8 approved 12 68
X 11/20/2015 2 decline 14 66
X 11/20/2015 2 approved 14 66
So, query works fine for different status. First 2 rows are fine. But when an employee does more than one action in a day (decline, approved etc.), my query only shows the total used and total remaining for the day.
Here is the expected result.
EmployeeName AbsenceDate HoursReported Status TotalUsdHrs TotalRemingHrs
X 11/11/2015 4 approved 4 76
X 11/15/2015 8 approved 12 68
X 11/20/2015 2 decline 12 68
X 11/20/2015 2 approved 14 66
You are doing a cumulative sum that returns results based on the order of AbsenceDate (sum(...) over (partition by ... order by pd.absenceDate). But your last 2 records have the exact same date (11/20/2015) -- at least, according to what you are showing us. This creates an ambiguity.
So, it is absolutely conceivable, and legal, that SQL Server is processing the 2 approved hours row before the 2 declined hours row when calculating the cumulative sum --which would explain your current results--, despite the fact that rows themselves are returned to you in a different order (BTW, consider adding an order by clause to the query, otherwise, the order of the rows themselves are not guaranteed).
If the 2 rows do in fact share the exact same date, you'll have to find a 2nd column to remove the ambiguity and add that to the order by clause in the cumulative sum window function. Maybe you could add a timestamp field that you can order by.
Or maybe you always want the declined status to be considered ahead of the approved status when the AbsenceDate is the same. Here is an example of a query that would do exactly that (notice the changes in the order by clauses):
SELECT
lsm.EmployeeName,
pd.absenceDate,
pd.amountInDays * 8 AS [HoursReported],
pd.status,
(SUM(CASE WHEN pd.[status]='App' THEN (pd.amountInDays * 8)
ELSE 0 END) OVER (partition by lsm.[EmployeeName] order by pd.absenceDate,
case when pd.[status] = 'App' then 1 else 0 end)) AS [TotalUsedHours]
( #maxPSHours ) - (sum(
CASE WHEN pd.[status]='App' THEN (pd.amountInDays * 8)
ELSE 0 END)
over (
partition by lsm.[EmployeeName] order by pd.absenceDate,
case when pd.[status] = 'App' then 1 else 0 end)) AS [TotalRemainingHours]
FROM
[LocationStaffMembers] lsm
INNER JOIN
[PersonalDays] pd ON lsm.staffMemberId = pd.staffMemberId
ORDER BY lsm.[EmployeeName],
pd.absenceDate,
case when pd.[status] = 'App' then 1 else 0 end