Compute average for three periods of times - sql

I have the following three queries which get the average price for a product for three specific period of times: all data, last 7 days and last 30 days.
SELECT AVG(Price) AS AggregatedPrice, COUNT(*) AS PCount
FROM dbo.Products
WHERE Id = #id
SELECT AVG(Price) AS AggregatedPrice, COUNT(*) AS PCount
FROM dbo.Products
WHERE Id = #id AND DATEDIFF(day, UpdatedDatetime, getdate()) < 7
SELECT AVG(Price) AS AggregatedPrice, COUNT(*) AS PCount
FROM dbo.Products
WHERE Id = #id AND DATEDIFF(day, UpdatedDatetime, getdate()) < 30
Those three queries give me the right data, but not in the form I want. Is there a way to combine those three queries into one. My final goal is to create a view with all this data on one row.
In addition, it appears to me that the average computed for the 7 days could be reused for the 30 days and all the list. Is there an optimization that I can do?

SELECT #Id
, t1.AggregatedPrice AggregatedPrice_ALL, t1.PCount PCount_ALL
, t2.AggregatedPrice AggregatedPrice_7, t2.PCount PCount_7
, t3.AggregatedPrice AggregatedPrice_30, t3.PCount PCount_30
FROM
(SELECT Id, AVG(Price) AS AggregatedPrice, COUNT(*) AS PCount
FROM dbo.Products
WHERE Id = #id) t1
JOIN
(SELECT Id, AVG(Price) AS AggregatedPrice, COUNT(*) AS PCount
FROM dbo.ApplicationPrice
WHERE Id = #id AND DATEDIFF(day, UpdatedDatetime, getdate()) < 7) t2
ON t1.Id = t2.id
JOIN
(SELECT Id, AVG(Price) AS AggregatedPrice, COUNT(*) AS PCount
FROM dbo.ApplicationPrice
WHERE Id = #id AND DATEDIFF(day, UpdatedDatetime, getdate()) < 30) t3
ON t1.Id = t3.id

Something like this. Make sure to test properly
SELECT
AVG(Price) AS AggregatedPrice
, COUNT(*) AS PCount
, AVG(CASE WHEN DATEDIFF(day, UpdatedDatetime, getdate()) < 7 THEN Price ELSE Null End) AS AggregatedPrice7Days
, SUM(CASE WHEN DATEDIFF(day, UpdatedDatetime, getdate()) < 7 THEN 1 ELSE 0 End) AS AggregatedPrice7Days
, AVG(CASE WHEN DATEDIFF(day, UpdatedDatetime, getdate()) < 30 THEN Price ELSE Null End) AS AggregatedPrice30Days
, SUM(CASE WHEN DATEDIFF(day, UpdatedDatetime, getdate()) < 30 THEN 1 ELSE 0 End) AS AggregatedPrice30Days
FROM
dbo.Products
WHERE
Id = #id

I don't have T-SQL handy to test the syntax, but it should be something like this:
SELECT t1.AggregatedPrice, t1.PCount, t2.AggregatedPrice, t2.PCount, t3.AggregatedPrice, t3.PCount
FROM
(SELECT AVG(Price) AS AggregatedPrice, COUNT(*) AS PCount FROM dbo.Products WHERE Id = #id) t1,
(SELECT AVG(Price) AS AggregatedPrice, COUNT(*) AS PCount FROM dbo.Products WHERE Id = #id AND DATEDIFF(day, UpdatedDatetime, getdate()) < 7) t2,
(SELECT AVG(Price) AS AggregatedPrice, COUNT(*) AS PCount FROM dbo.Products WHERE Id = #id AND DATEDIFF(day, UpdatedDatetime, getdate()) < 30) t3

Try this:
SELECT AVG(Price) AS AggregatedPrice,
COUNT(*) AS PCount,
AVG(CASE WHEN DATEDIFF(day, UpdatedDatetime, getdate()) < 7 THEN Price ELSE NULL END) AS AggregatedWeekPrice,
COUNT(CASE WHEN DATEDIFF(day, UpdatedDatetime, getdate()) < 7 THEN 1 ELSE NULL END) AS PWeekCount,
AVG(CASE WHEN DATEDIFF(day, UpdatedDatetime, getdate()) < 30 THEN Price ELSE NULL END) AS AggregatedMonthPrice,
COUNT(CASE WHEN DATEDIFF(day, UpdatedDatetime, getdate()) < 30 THEN 1 ELSE NULL END) AS PMonthCount,
FROM dbo.Products
WHERE Id = #id

Related

How can I include more columns other than just percentage column in my sql query result?

When I use the below query I only get percentage column but I want the (buyer_id, buyer_name, created_date,total_work_orders_with_gtv_first_60_days, total_gtv_first_60_days, total_net_amount_first_60_days) to show as columns too. Would really appreciate your help please.
WITH results_cte AS (
SELECT
b.buyer_id,
b.buyer_name,
CAST(b.created_date AS DATE) AS created_date,
COALESCE(wo.total_work_orders_with_gtv_first_60_days, 0) AS total_work_orders_with_gtv_first_60_days,
COALESCE(wo.total_gtv_first_60_days, 0) AS total_gtv_first_60_days,
COALESCE(wo.total_net_amount_first_60_days, 0) AS total_net_amount_first_60_days
FROM dw.buyer b
LEFT JOIN (SELECT wo.buyer_id,
COUNT(CASE WHEN wo.gtv_date < DATEADD(DAY, 60, b.created_date) THEN wo.work_order_id ELSE NULL END) AS total_work_orders_with_gtv_first_60_days,
SUM(CASE WHEN wo.gtv_date < DATEADD(DAY, 60, b.created_date) THEN wo.gtv ELSE NULL END) AS total_gtv_first_60_days,
SUM(CASE WHEN wo.gtv_date < DATEADD(DAY, 60, b.created_date) THEN wo.net_amount ELSE NULL END) AS total_net_amount_first_60_days
FROM dw.work_order wo
JOIN dw.buyer b
ON wo.buyer_id = b.buyer_id
WHERE wo.gtv > 0
GROUP BY wo.buyer_id) wo
ON b.buyer_id = wo.buyer_id
WHERE b.buyer_segmentation = 'S - Self-Service'
AND b.status = 'Active'
AND b.created_date >= DATEADD(YEAR, -1, GETDATE())
)
SELECT (SELECT CAST(count(DISTINCT buyer_id) AS float) FROM results_cte WHERE total_work_orders_with_gtv_first_60_days > 0)
/ (SELECT CAST(count(DISTINCT buyer_id) AS float) FROM results_cte ) AS percentage

Group By in sql subquery

So i have a query where i am trying to accumulate the count of order for phone and website however i realise you cant use a GROUP BY clause in the subquery when it is directly within a select as it returns more than one value. Therefore my question is where do i put the subquerys to achieve the same result.
SELECT CAST(a.DateCreated AS DATE),
(SELECT count(CAST(DateCreated AS DATE))
FROM [Sterlingbuild].[dbo].[CustomerOrder]
WHERE BookingSourceId = 1
GROUP BY CAST(DateCreated AS DATE)) AS 'Website',
(SELECT count(CAST(DateCreated AS DATE))
FROM [Sterlingbuild].[dbo].[CustomerOrder]
WHERE BookingSourceId = 2
GROUP BY CAST(DateCreated AS DATE)) AS 'Phone'
, count(CAST(a.DateCreated AS DATE)) AS 'Total Orders'
FROM [Sterlingbuild].[dbo].[CustomerOrder] a
WHERE CustomerOrderStatusId = 7
AND DepartmentId = 1
GROUP BY CAST(a.DateCreated AS DATE)
ORDER BY CAST(a.DateCreated AS DATE)
When i run this query it works therefore i know the data is correct:
SELECT count(CAST(DateCreated AS DATE))
FROM [Sterlingbuild].[dbo].[CustomerOrder]
WHERE BookingSourceId = 1
AND CustomerOrderStatusId = 7 AND DepartmentId = 1
GROUP BY CAST(DateCreated AS DATE)
I am guessing you want conditional aggregation:
SELECT CAST(co.DateCreated AS DATE),
SUM(CASE WHEN BookingSOurceId = 1 THEN 1 ELSE 0 END) as Website,
SUM(CASE WHEN BookingSOurceId = 2 THEN 1 ELSE 0 END) as Phone,
COUNT(*) as Total_Orders
FROM [Sterlingbuild].[dbo].[CustomerOrder] co
WHERE CustomerOrderStatusId = 7 AND DepartmentId = 1
GROUP BY CAST(a.DateCreated AS DATE)
ORDER BY CAST(a.DateCreated AS DATE)

SQL Server issue with select and group by with calculated columns

I have this SQL I am trying to build:
select
a.Name,
(SELECT COUNT(b.PlannedCollectionDate) WHERE b.PlannedCollectionDate < GETDATE()) AS Due,
(SELECT COUNT(b.PlannedCollectionDate) WHERE b.PlannedCollectionDate = GETDATE()) AS Today,
(SELECT COUNT(b.PlannedCollectionDate) WHERE b.PlannedCollectionDate = DATEADD(DAY, 1, GETDATE())) AS Expected,
(SELECT COUNT(b.PlannedCollectionDate) WHERE b.PlannedCollectionDate > DATEADD(DAY, 1, GETDATE())) AS Planned
from Centers AS a
INNER JOIN Collections AS b
ON a.Id = b.CenterId
GROUP BY a.Name
But I get an error:
Column 'Collections.PlannedCollectionDate' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I know I could do something like this:
select
a.Name,
(SELECT COUNT(Id) FROM Collections WHERE CenterId = a.Id AND PlannedCollectionDate < GETDATE()) AS Due,
(SELECT COUNT(Id) FROM Collections WHERE CenterId = a.Id AND PlannedCollectionDate = GETDATE()) AS Today,
(SELECT COUNT(Id) FROM Collections WHERE CenterId = a.Id AND PlannedCollectionDate = DATEADD(DAY, 1, GETDATE())) AS Expected,
(SELECT COUNT(Id) FROM Collections WHERE CenterId = a.Id AND PlannedCollectionDate > DATEADD(DAY, 1, GETDATE())) AS Planned
from Centers AS a
But I assume that is slower because I have to do multiple selects from the same table (Collections).
So, my question is, what can I do to make my first query work? I don't think grouping by PlannedCollectionDate is right, because it will mess up my count
I think you want conditional aggregation:
select ce.Name,
SUM(CASE WHEN co.PlannedCollectionDate < GETDATE() THEN 1 ELSE 0 END) AS Due,
SUM(CASE WHEN co.PlannedCollectionDate = GETDATE() THEN 1 ELSE 0 END) AS Today,
SUM(CASE WHEN co.PlannedCollectionDate = DATEADD(DAY, 1, GETDATE()) THEN 1 ELSE 0 END) AS Expected,
SUM(CASE WHEN co.PlannedCollectionDate > DATEADD(DAY, 1, GETDATE()) THEN 1 ELSE 0 END) AS Planned
from Centers ce join
Collections co
on ce.Id = co.CenterId
group by ce.Name;
This implements what you have written. Do note the use of meaningful table aliases.
However, it will not do what you want, because GETDATE() has a time component. To fix that, convert it to a date:
select ce.Name,
SUM(CASE WHEN co.PlannedCollectionDate < CONVERT(DATE, GETDATE()) THEN 1 ELSE 0 END) AS Due,
SUM(CASE WHEN co.PlannedCollectionDate = CONVERT(DATE, GETDATE()) THEN 1 ELSE 0 END) AS Today,
SUM(CASE WHEN co.PlannedCollectionDate = DATEADD(DAY, 1, CONVERT(DATE, GETDATE())) THEN 1 ELSE 0 END) AS Expected,
SUM(CASE WHEN co.PlannedCollectionDate > DATEADD(DAY, 1, CONVERT(DATE, GETDATE())) THEN 1 ELSE 0 END) AS Planned
from Centers ce join
Collections co
on ce.Id = co.CenterId
group by ce.Name;
Note that this assumes that PlannedCollectionDate does not have a time component.

Revenue for two months date wise

I am trying to get data for last 2 month ...but the query does not give perfect result....
SELECT DAY(table_A.PaymentDate) as date1 ,
(case when MONTH(table_A.PaymentDate) = MONTH(CURRENT_TIMESTAMP) - 1
then CAST(SUM(table_A.Total_Amount) AS INT)
else 0
end) AS last_month_CNT,
(case when MONTH(table_A.PaymentDate) = MONTH(CURRENT_TIMESTAMP)
then CAST(SUM(table_A.Total_Amount) As INT)
else 0
end) as This_month_CNT
FROM Tbl_Pan_Paymentdetails table_A
FULL OUTER JOIN Tbl_Pan_Paymentdetails table_B
ON table_A.PaymentDate=table_B.PaymentDate
WHERE YEAR(table_A.PaymentDate) = YEAR(CURRENT_TIMESTAMP)
AND
table_A.PaymentDate >= DATEADD(MONTH, -1, GETDATE())
GROUP BY
DAY(table_A.PaymentDate) ,MONTH(table_A.PaymentDate)
order by
DAY(table_A.PaymentDate);
Move the entire case expression inside the sum function and don't include the month in the group by. Also, the full outer join seems unnecessary so I removed it.
This should be what you are looking for:
SELECT
DAY(PaymentDate) as date1 ,
SUM(CASE WHEN MONTH(PaymentDate) = MONTH(CURRENT_TIMESTAMP)-1 THEN CAST(Total_Amount AS INT) ELSE 0 END) AS last_month_CNT,
SUM(CASE WHEN MONTH(PaymentDate) = MONTH(CURRENT_TIMESTAMP) THEN CAST(Total_Amount AS INT) ELSE 0 END) AS This_month_CNT
FROM Tbl_Pan_Paymentdetails
WHERE YEAR(PaymentDate) = YEAR(CURRENT_TIMESTAMP)
AND PaymentDate >= DATEADD(MONTH, -1, GETDATE())
GROUP BY DAY(PaymentDate)
ORDER BY DAY(PaymentDate);

sql join and group by generated date range

I have Table1 and I need a query to populate Table2:
Problem here is with Date column. I want to know the process of location/partner combination per day. Main issue here is that I can't pick DateCreated and make it as default date since it doesn't necessarily cover whole date range, like in this example where it doesn't have 2015-01-07 and 2015-01-09. Same case with other dates.
So, my idea is to first select dates from some table which contains needed date range and then perform calculation for each day/location/partner combination from cte but in that case I can't figure out how to make a join for LocationId and PartnerId.
Columns:
Date - CreatedItems - number of created items where Table1.DateCreated = Table2.Date
DeliveredItems - number of delivered items where Table1.DateDateOut = Table2.Date
CycleTime - number of days delivered item was in the location (DateOut - DateIn + 1)
I started with something like this but it's very like that I completely missed the point with it:
with d as
(
select date from DimDate
where date between DATEADD(DAY, -365, getdate()) and getdate()
),
cr as -- created items
(
select
DateCreated,
LocationId,
PartnerId,
CreatedItems = count(*)
from Table1
where DateCreated is not null
group by DateCreated,
LocationId,
PartnerId
),
del as -- delivered items
(
select
DateOut,
LocationId,
ParnerId,
DeliveredItems = count(*),
CycleTime = DATEDIFF(Day, DateOut, DateIn)
from Table1
where DateOut is not null
and Datein is not null
group by DateOut,
LocationId,
PartnerId
)
select
d.Date
from d
LEFT OUTER JOIN cr on cr.DateCreated = d.Date -- MISSING JOIN PER LocationId and PartnerId
LEFT OUTER JOIN del on del.DateCompleted = d.Date -- MISSING JOIN PER LocationId and PartnerId
with range(days) as (
select 0 union all select 1 union all select 2 union all
select 3 union all select 4 union all select 5 union all
select 6 /* extend as necessary */
)
select dateadd(day, r.days, t.DateCreated) as "Date", locationId, PartnerId,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.days between 0 and datediff(day, t.DateCreated, t.DateOut)
group by dateadd(day, r.days, t.DateCreated), LocationId, PartnerId;
If you only want the end dates (rather than all the dates in between) this is probably a better approach:
with range(dt) as (
select distinct DateCreated from T union
select distinct DateOut from T
)
select r.dt as "Date", locationId, PartnerId,
sum(
case
when r.dt = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when r.dt = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when r.dt = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.dt in (t.DateCreated, t.DateOut)
group by r.dt, LocationId, PartnerId;
If to specify WHERE clause? Something Like that:
WHERE cr.LocationId = del.LocationId AND
cr.PartnerId = del.PartnerId