Running total SQL Server query - sql-server

So far, I have the following SQL Server 2005 query:
WITH D AS (
SELECT CONVERT(VARCHAR, '2020.11.01', 102) AS d_y_m, CAST('2020-11-01' AS DATETIME) AS dt
UNION ALL
SELECT CONVERT(VARCHAR, DATEADD(dd, 1, z.dt), 102) AS d_y_m, DATEADD(dd, 1, z.dt)
FROM D AS z
WHERE DATEADD(dd, 1, z.dt) <= '2020-11-30')
SELECT x.d_y_m, ISNULL(SUM(y.Total), 0) AS [Invoiced], ISNULL(SUM(FEI.Total), 0) AS [Paid] FROM D x
LEFT JOIN Invoices y ON CONVERT(VARCHAR, y.InvoiceDate, 102) = x.d_y_m
LEFT JOIN Payments AS FEI ON CONVERT(VARCHAR, FEI.PaymentDate, 102) = x.d_y_m
GROUP BY x.d_y_m
ORDER BY x.d_y_m OPTION (MAXRECURSION 0)
How do I add another column (RunningTotal) to the query which sums up the (Invoiced-Paid) result from the previous day to the one for today
Example:
d_y_m | Invoiced | Paid | RunningTotal
2020.11.01 | 24 | 5 | 19
2020.11.02 | 45 | 2 | 62
2020.11.03 | 10 | 20 | 52
2020.11.04 | 5 | 0 | 57
2020.11.05 | 0 | 10 | 47

Couple remarks on your current solution:
Do not use "random" table aliases. D for "dates" makes sense. y for "Invoices" does not. d_y_m does not match your date format either. Keep the table and column aliases meaningful.
Do not drag the date conversion through your entire solution. Work with date values as date types and convert the values once in the final select.
Do not group the sums of the invoiced and paid amounts in one query. If you have multiple invoices or payments on a single day, then the sums will be incorrect! See the "Extra" section at the bottom for an explanation.
Make it easy for us to help you. Next time, please provide sample data that we can copy-paste instead of having to invent our own.
SQL Server 2005 is officially unsupported as of April 12, 2016. Time to look for a new version!
Sample data
create table Invoices
(
InvoiceDate date,
Total money
);
insert into Invoices (InvoiceDate, Total) values
('2020-11-01', 20),
('2020-11-01', 4),
('2020-11-02', 40),
('2020-11-02', 5),
('2020-11-03', 10),
('2020-11-04', 3),
('2020-11-04', 2);
create table Payments
(
PaymentDate date,
Total money
);
insert into Payments (PaymentDate, Total) values
('2020-11-01', 5),
('2020-11-02', 2),
('2020-11-03', 10),
('2020-11-03', 10),
('2020-11-05', 10);
Solution
with DateRange as
(
select convert(date, '2020-11-01') as DateValue
union all
select dateadd(day, 1, dr.DateValue)
from DateRange dr
where dr.DateValue < '2020-11-30'
),
InvoicedTotal as
(
select dr.DateValue,
isnull(sum(i.Total), 0) as Invoiced
from DateRange dr
left join Invoices i
on i.InvoiceDate = dr.DateValue
group by dr.DateValue
),
PaidTotal as
(
select dr.DateValue,
isnull(sum(p.Total), 0) as Paid
from DateRange dr
left join Payments p
on p.PaymentDate = dr.DateValue
group by dr.DateValue
)
select convert(varchar(10), dr.DateValue, 102) as [YYYY.MM.DD],
it.Invoiced as [Invoiced],
sum(it.Invoiced) over(order by it.DateValue
rows between unbounded preceding and current row) as [CumInvoiced],
pt.Paid as [Paid],
sum(pt.Paid) over(order by pt.DateValue
rows between unbounded preceding and current row) as [CumPaid],
sum(it.Invoiced) over(order by it.DateValue
rows between unbounded preceding and current row) -
sum(pt.Paid) over(order by pt.DateValue
rows between unbounded preceding and current row) as [RunningTotal]
from DateRange dr
join InvoicedTotal it
on it.DateValue = dr.DateValue
join PaidTotal pt
on pt.DateValue = dr.DateValue
order by dr.DateValue;
Result
Only listing the first 10 of the 30 rows for November.
YYYY.MM.DD Invoiced CumInvoiced Paid CumPaid RunningTotal
---------- -------- ----------- ------- ------- ------------
2020.11.01 24.0000 24.0000 5.0000 5.0000 19.0000
2020.11.02 45.0000 69.0000 2.0000 7.0000 62.0000
2020.11.03 10.0000 79.0000 20.0000 27.0000 52.0000
2020.11.04 5.0000 84.0000 0.0000 27.0000 57.0000
2020.11.05 0.0000 84.0000 10.0000 37.0000 47.0000
2020.11.06 0.0000 84.0000 0.0000 37.0000 47.0000
2020.11.07 0.0000 84.0000 0.0000 37.0000 47.0000
2020.11.08 0.0000 84.0000 0.0000 37.0000 47.0000
2020.11.09 0.0000 84.0000 0.0000 37.0000 47.0000
2020.11.10 0.0000 84.0000 0.0000 37.0000 47.0000
Fiddle to see it in action.
Extra: why not to count both totals in one query.
Using the same sample data you can run this query to zoom in a particular date, here: 2020-11-01. On this date the sample data has 2 invoices and 1 payment.
with DateRange as
(
select '2020-11-01' as DateValue -- filtering data to explain
)
select dr.DateValue,
isnull(sum(i.Total), 0) as Invoiced,
isnull(sum(p.Total), 0) as Paid
from DateRange dr
left join Invoices i
on i.InvoiceDate = dr.DateValue
left join Payments p
on p.PaymentDate = dr.DateValue
group by dr.DateValue
order by dr.DateValue;
Just executing the joins would give you the result below. Because of the combined left join the payment row is listed twice!
dr.DateValue | i.Total | p.Total
------------ | ------- | -------
2020-11-01 | 20 | 5
2020-11-01 | 4 | 5 --> payment row got joined TWICE
Summing up those rows gives an invalid payment sum for that day.
group by dr.DateValue | sum(i.Total) | sum(p.Total)
--------------------- | ------------ | ------------
2020-11-01 | 24 | 10 --> last sum is WRONG !
Edit: SQL Server 2005 version with cross apply. But a SQL Server version update is still recommended!
with DateRange as
(
select convert(date, '2020-11-01') as DateValue
union all
select dateadd(day, 1, dr.DateValue)
from DateRange dr
where dr.DateValue < '2020-11-30'
),
InvoicedTotal as
(
select dr.DateValue,
isnull(sum(i.Total), 0) as Invoiced
from DateRange dr
left join Invoices i
on i.InvoiceDate = dr.DateValue
group by dr.DateValue
),
PaidTotal as
(
select dr.DateValue,
isnull(sum(p.Total), 0) as Paid
from DateRange dr
left join Payments p
on p.PaymentDate = dr.DateValue
group by dr.DateValue
)
select convert(varchar(10), dr.DateValue, 102) as [YYYY.MM.DD],
it1.Invoiced as [Invoiced],
it3.Invoiced as [CumInvoiced],
pt1.Paid as [Paid],
pt3.Paid as [CumPaid],
it3.Invoiced - pt3.Paid as [RunningTotal]
from DateRange dr
join InvoicedTotal it1
on it1.DateValue = dr.DateValue
join PaidTotal pt1
on pt1.DateValue = dr.DateValue
cross apply ( select sum(it2.Invoiced) as Invoiced
from InvoicedTotal it2
where it2.DateValue <= dr.DateValue ) it3
cross apply ( select sum(pt2.Paid) as Paid
from PaidTotal pt2
where pt2.DateValue <= dr.DateValue ) pt3
order by dr.DateValue;
Updated fiddle.

Related

Locate items with no changes over a period of time

Is there a T-SQL query that would allow me to see products that have had no changes in quantity for the past 4 days?
Product
Date
Quantity
Coke
2022-04-06
0
Coke
2022-04-07
0
Coke
2022-04-08
0
Coke
2022-04-09
0
Pepsi
2022-04-06
0
Pepsi
2022-04-07
1
Pepsi
2022-04-08
1
Pepsi
2022-04-09
1
Sprite
2022-04-06
1
Sprite
2022-04-07
0
Sprite
2022-04-08
0
Sprite
2022-04-09
1
Tango
2022-04-05
2
Tango
2022-04-06
1
Tango
2022-04-07
1
Tango
2022-04-08
1
Tango
2022-04-09
1
Result
Product
Quantity
Coke
0
Edit: this is how I started off my code
DECLARE #CurrentDate date = GETDATE();
DECLARE #PreviousDate date = DATEADD (Day, -4, #CurrentDate)
DECLARE #Quantity AS Decimal(8,5)
DECLARE #Count AS int
SELECT
[Date], [Product], [Quantity]
FROM
Table1
WHERE
[Date] = #PreviousDate
AND [Product] IN (SELECT [Product]
FROM Table1
WHERE [DATE] BETWEEN #PreviousDate AND #CurrentDate)
If I understand correctly you could use first_value() analytic function to partition the products accordingly and check the first and last quantities match,
select distinct product, quantity from (
select *,
First_Value(quantity) over(partition by product order by date) f,
First_Value(quantity) over(partition by product order by date desc) l
from t
where date >= DateAdd(day,-4,GetDate())
)t
where f = l;
Demo Fiddle
Edit
Seems I didn't quite understand but do now (I hope) so how about an approach using not exists?
with cv as (
select *,
First_Value(quantity) over(partition by product order by date desc) currentValue,
Count(*) over(partition by product) qty
from t
where date >= DateAdd(day,-4,GetDate())
)
select distinct product, quantity
from cv t
where qty=4 and not exists (
select * from cv t2
where t2.product = t.product
and t2.quantity != currentValue
);
Demo Fiddle (2)
You select all records from #PreviousDate (WHERE [Date] = #PreviousDate) and which have modifications (AND [Product] IN (...))
SELECT
t1.[Date], t1.[Product], t1.[Quantity], t2.Quantity "PreviousQuantity"
FROM
Table1 t1
LEFT JOIN Table1 t2 On t1.Product = t2.Product and t2.[Date] = #PreviousDate
WHERE t1.[Date] = #CurrentDate
and t1.Quantity = t2.Quantity
But this will show nothing because #PreviousDate has the value 2022-04-05 (when #CurrentDate=2022-04-09)
So, you might need to change #PreviousDate to: DECLARE #PreviousDate date = DATEADD (Day, -3, #CurrentDate)
see: DBFIDDLE

Referencing the current row outer apply column within separate outer join

Recently I've been tasked with creating a report that outputs sales information by Date of Business and Hour of the Day.
Here is the query I have currently written.
WITH CTE AS
(
SELECT 0 AS Count
UNION ALL
SELECT Count + 1
FROM CTE
WHERE Count + 1 <= 23
),
ALLDATES AS
(
SELECT CONVERT(datetime, #startDate) AS [DOB]
UNION ALL
SELECT DATEADD(DAY, 1, [DOB])
FROM AllDates
WHERE [DOB] < #endDate
)
SELECT D.DOB, A.Count AS [Hour], CONCAT(A.Count, ':00') AS [DisplayHour]
, B.OrderModeName, COALESCE(B.Sales_Total, 0) AS [Sales]
, COALESCE(B.Comps, 0) AS Comps, COALESCE(B.Promos, 0) AS Promos
FROM CTE AS A
OUTER APPLY (SELECT DOB FROM ALLDATES) D
LEFT OUTER JOIN (
SELECT DATEPART(HH, ItemDetail.TransactionTime) AS [Hour]
, OrderMode.OrderModeName, SUM(ItemDetail.GrossPrice) Sales_Total
, SUM(CompAmount) AS Comps, SUM(PromoAmount) AS Promos
FROM ItemDetail
INNER JOIN OrderMode ON OrderMode.OrderModeID = ItemDetail.OrderModeID
WHERE ItemDetail.DOB = D.DOB /*NEED HELP HERE*/ AND LocationID IN (
SELECT LocationID
FROM LocationGroupMember
WHERE LocationGroupID = '#locationGroupID'
)
GROUP BY ItemDetail.DOB, DATEPART(HH, ItemDetail.TransactionTime), OrderMode.OrderModeName
) AS B
ON A.Count = B.Hour
ORDER BY D.DOB, A.Count
Where I am struggling is being able to reference the current row's DOB column that is coming from the OUTER APPLY.
I have tried WHERE ItemDetail.DOB = D.DOB, however I receive an error that the identifier can't be bound. Am I correct that in understanding that the outer applied data is not visible to the subquery within the join?
Here is an example of the output I'm expecting:
DOB | Hour | Display Hour | OrderModeName | Sales | Comps | Promos
1/8/2020 | 17 | 17:00 | Order | 163.17 | 0 | 0 <-- Sales for Hour and Order Mode present
1/8/2020 | 23 | 23:00 | | 0 | 0 | 0 <-- No sales at all for a given hour
Thanks in advance for any direction and advice!
The basic pattern here is to CROSS JOIN to define the result "grain" and then LEFT JOIN the fact table to populate the rows for which data exists. EG
WITH CTE AS
(
SELECT 0 AS Count
UNION ALL
SELECT Count + 1
FROM CTE
WHERE Count + 1 <= 23
),
ALLDATES AS
(
SELECT CONVERT(datetime, #startDate) AS [DOB]
UNION ALL
SELECT DATEADD(DAY, 1, [DOB])
FROM AllDates
WHERE [DOB] < #endDate
),
ALLHOURS as
(
SELECT D.DOB, A.Count AS [Hour], CONCAT(A.Count, ':00') AS [DisplayHour]
FROM CTE AS A
CROSS JOIN ALLDATES D
),
ITEM_SUMMARY as
(
SELECT DOB, DATEPART(HH, ItemDetail.TransactionTime) AS [Hour], OrderMode.OrderModeName, SUM(ItemDetail.GrossPrice) Sales_Total, SUM(CompAmount) AS Comps, SUM(PromoAmount) AS Promos
FROM ItemDetail
INNER JOIN OrderMode ON OrderMode.OrderModeID = ItemDetail.OrderModeID
AND LocationID IN (SELECT LocationID FROM LocationGroupMember WHERE LocationGroupID = #locationGroupID)
where DOB >= #startDate
and DOB < #endDate
GROUP BY ItemDetail.DOB, DATEPART(HH, ItemDetail.TransactionTime), OrderMode.OrderModeName
)
select ALLHOURS.DOB,
ALLHOURS.Count AS [Hour],
CONCAT(ALLHOURS.Count, ':00') AS [DisplayHour],
ITEM_SUMMARY.OrderModeName,
COALESCE(ITEM_SUMMARY.Sales_Total, 0) AS [Sales],
COALESCE(ITEM_SUMMARY.Comps, 0) AS Comps,
COALESCE(ITEM_SUMMARY.Promos, 0) AS Promos
from ALLHOURS
LEFT OUTER JOIN ITEM_SUMMARY
on ITEM_SUMMARY.DOB = ALLHOURS.DOB
and ITEM_SUMMARY.Hour = ALLHOURS.Hour

How to group by week even when the count is 0

My below example works fine, the only challenge i am facing is that weeks with 0 results do not show.
Here is a sample of my code:
SELECT
DATENAME (WK, DATE) AS WEEK,
COUNT (DISTINCT COMPANY_ID) AS AMOUNT
FROM
(
SELECT COMPANY, DATE = MIN(DATE)
FROM TABLE1 A INNER JOIN TABLE2 B
ON A.ID = B.ID
WHERE YEAR(A.DATE) = '2019' AND COMPANY_ID NOT IN(SELECT COMPANY_ID FROM
TABLE1 A INNER JOIN TABLE2 B ON A.ID = B.ID AND DATE < '2019-01-01') GROUP
BY COMPANY_ID) d
GROUP BY dateadd(wk, datediff(wk, 0, DATE), 0), DATENAME(WK, DATE)
ORDER BY dateadd(wk, datediff(wk, 0, DATE), 0)
My current output looks like this:
week | amount
4 | 354
6 | 222
7 | 144
8 | 354
9 | 45
10 | 55
11 | 76
12 | 98
13 | 45
14 | 344
The result above is missing many weeks (1,2,3 and 15,16,17 etc.)
How do i get to show those with 0 count?
My desired output:
week | amount
1 | 0
2 | 0
3 | 0
4 | 354
6 | 222
7 | 144
8 | 354
9 | 45
10 | 55
11 | 76
12 | 98
13 | 45
14 | 344
15 | 0
16 | 0
17 | 0
Couple of things to note -
1) Your current query is not correct (Possibly, you have removed some portion of it to hide confidential stuff).
Ex. In the subquery named "d", the GROUP BY is on "company_id" column but "company" has been SELECT-ed.
SELECT DATENAME (WK, DATE) AS WEEK,
COUNT (DISTINCT COMPANY_ID) AS AMOUNT
FROM
(
SELECT COMPANY /*Different from group_by clause*/, DATE = MIN(DATE)
FROM TABLE1 A INNER JOIN TABLE2 B ON (A.ID = B.ID)
WHERE YEAR(A.DATE) = '2019'
AND COMPANY_ID NOT IN
(
SELECT COMPANY_ID
FROM TABLE1 A INNER JOIN TABLE2 B ON A.ID = B.ID AND DATE < '2019-01-01'
)
GROUP BY COMPANY_ID
) d
GROUP BY dateadd(wk, datediff(wk, 0, DATE), 0), DATENAME(WK, DATE)
ORDER BY dateadd(wk, datediff(wk, 0, DATE), 0)
2) I hope while editing you have not remove any clauses mistakenly.
3) Could you please post some input data to understand the output better.
(Apologies for posting here, as I don't have privilege to comment.)
First Create a temp table that has all weeks numbers
then join it with your query
DECLARE #Weeks AS Table(ID int)
DECLARE #i int = 1
WHILE #i < 53
BEGIN
INSERT INTO #Weeks (ID)
VALUES(#i)
SET #i = #i + 1
END
SELECT * FROM #Weeks
SELECT
DATENAME (WK, DATE) AS WEEK,
COUNT (DISTINCT COMPANY_ID) AS AMOUNT
FROM
(
SELECT COMPANY, DATE = MIN(DATE)
FROM TABLE1 A INNER JOIN TABLE2 B
ON A.ID = B.ID
RIGHT OUTER JOIN #Weeks W ON W.ID = DATENAME (WK, DATE)
WHERE YEAR(A.DATE) = '2019' AND COMPANY_ID NOT IN(SELECT COMPANY_ID FROM
TABLE1 A INNER JOIN TABLE2 B ON A.ID = B.ID AND DATE < '2019-01-01') GROUP
BY COMPANY_ID) d
GROUP BY dateadd(wk, datediff(wk, 0, DATE), 0), DATENAME(WK, DATE)
ORDER BY dateadd(wk, datediff(wk, 0, DATE), 0)

SQL Server Two Column Pivot table

I'm trying to create a PIVOT TSQL statment that totals the products by date and state/province and provides the AVG Transit Time. Here is what I have so far:
select *
from (select createdate [Date Processed],
stateprovince as [Province],
count(*) as [Total],
avg(datediff(day,createdate,t.eventdate)) as [AVG Delivery],
product
from recipient C left outer join
(select delivid, product, eventdesc, eventdate, eventcode
from deliverystatus
where delivid in (select max(deliv_id)
from deliverystatus
where eventcode = 'DELIVERED'
group by product)) as t ON c.product = t.product
where account = 3519 and consol <>'' and trknum <> '' and C.createdate between '2/4/2016' and '2/4/2016'
group by C.createdate, c.stateprovince, c.product
) as Q
pivot (
count(product)
for [Province] in (NY, IL, GA)
) as PVT
My Result is:
Date Processed Total AVG Transit NY IL GA
2016-02-04 00:00:00.000 1 8 0 0 1
2016-02-04 00:00:00.000 1 11 2 4 1
2016-02-04 00:00:00.000 1 12 0 0 0
2016-02-04 00:00:00.000 1 15 0 0 0
I need the result to be:
Date Processed Total AVG Transit NY IL GA
2016-02-04 00:00:00.000 8 11.5 2 4 2
The ultimate goal is to have the AVG Transit listed by State/Province like this:
Date Processed Total Total AVG NY AVG IL AVG GA AVG
2016-02-04 00:00:00.000 8 11.5 2 8 4 11 2 15
Thanks in advance.
You need to add a GROUP BY clause after the pivot and either take the AVG SUM or MAX value for each output column:
select [Date Processed], SUM(NY+IL+GA) AS [Total], AVG([AVG Delivery]) AS [AVG Delivery], SUM(NY) AS NY, SUM(IL) AS IL, SUM(GA) AS GA
from (select createdate [Date Processed],
stateprovince as [Province],
count(*) as [Total],
avg(datediff(day,createdate,t.eventdate)) as [AVG Delivery],
product
from recipient C left outer join
(select delivid, product, eventdesc, eventdate, eventcode
from deliverystatus
where delivid in (select max(deliv_id)
from deliverystatus
where eventcode = 'DELIVERED'
group by product)) as t ON c.product = t.product
where account = 3519 and consol <>'' and trknum <> '' and C.createdate between '2/4/2016' and '2/4/2016'
group by C.createdate, c.stateprovince, c.product
) as Q
pivot (
count(product)
for [Province] in (NY, IL, GA)
) as PVT
group by [Date Processed]

GROUP BY in subquery T-SQL

I am trying to make a table like this:
ProductName | SalesByDate | TotalSalesUntilDate
A | 5 | 15
B | 10 | 30
C | 20 | 25
D | 18 | 43
SalesByDate means the number of product sold for each product on the input date and TotalSalesUntilDate indicates the number of product sold for each product from the first date of the month until the input date (example of input date: 17 March 2010)
I wrote this query using subquery:
select p.ProductName, A.SalesByDate,
(select(SUM(case when (pd.Date between '01' and #Date)
then s.SalesByDate else 0 end))
from Period pd
inner join Sales s on pd.TimeID = s.TimeID
full join Product p on s.ProductID = p.ProductID) as TotalSalesUntilDate
from Product p join
(select s.ProductID, pd.Date, s.SalesByDate
from Period pd join Sales s on pd.TimeID = s.TimeID) A on
p.ProductID = A.ProductID where #Date = A.Date
but I got the result:
ProductName | SalesByDate | TotalSalesUntilDate
A | 5 | 113
B | 10 | 113
C | 20 | 113
D | 18 | 113
which the TotalSalesUntilDate shows the number of product sold from the first date of the month until the input date but for all product without separation for each product.
So when I tried to change the query to like this (adding GROUP BY p.ProductID before "as TotalSalesUntilDate"):
select p.ProductName, A.SalesByDate,
(select(SUM(case when (pd.Date between '01' and #Date)
then s.SalesByDate else 0 end))
from Period pd
inner join Sales s on pd.TimeID = s.TimeID
full join Product p on s.ProductID = p.ProductID
group by p.ProductID) as TotalSalesUntilDate
from Product p join
(select s.ProductID, pd.Date, s.SalesByDate
from Period pd join Sales s on pd.TimeID = s.TimeID) A on
p.ProductID = A.ProductID where #Date = A.Date
and when I execute this query, I got this error message:
"Msg 512, Level 16, State 1, Procedure SalesMTDSubQuery, Line 7
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression."
Since I'm new in SQL and still learning, but I don't understand how to solve this. Any help will be appreciated. Thank you.
In the #Date variable we are storing the date:
SELECT DISTINCT PT.[ProductName]
,SUM(IIF(PD.[Date] = #Date, SL.[SalesByDate], 0))
,SUM(IIF(PD.[Date] BETWEEN '01' AND #Date, SL.[SalesByDate], 0))
FROM #Product PT
INNER JOIN #Sales SL
ON PT.[ProductID] = SL.[ProductID]
INNER JOIN #Period PD
ON SL.[TimeID] = PD.[TimeID]
GROUP BY PT.[ProductName]
Result:
Full code:
DECLARE #Period TABLE
(
[TimeID] TINYINT
,[Date] CHAR(2)
)
INSERT INTO #Period([TimeID], [Date])
VALUES (1,'01')
,(2,'02')
,(3,'03')
,(4,'04')
,(5,'05')
,(6,'06')
,(7,'07')
,(8,'08')
,(9,'09')
,(10,'10')
,(11,'11')
,(12,'12')
,(13,'13')
,(14,'14')
,(15,'15')
DECLARE #Product TABLE
(
[ProductID] TINYINT
,[ProductName] CHAR(1)
)
INSERT INTO #Product( [ProductID], [ProductName])
VALUES (1,'A')
,(2,'B')
,(3,'C')
,(4,'D')
DECLARE #Sales TABLE
(
[TimeID] TINYINT
,[ProductID] TINYINT
,[SalesByDate] TINYINT
)
INSERT INTO #Sales ([TimeID], [ProductID], [SalesByDate])
VALUES (1, 1, 10)
,(1, 4, 20)
,(7, 2, 10)
,(7, 3, 5)
,(15, 1, 5)
,(15, 2, 10)
,(15, 3, 15)
,(15, 4, 18)
,(19, 2, 15)
,(20, 3, 2)
,(22, NULL, 2)
,(1, 4, 5)
,(7, 2, 10)
,(15, 3, 5)
DECLARE #Date CHAR(2) = '15'
SELECT DISTINCT PT.[ProductName]
,SUM(IIF(PD.[Date] = #Date, SL.[SalesByDate], 0))
,SUM(IIF(PD.[Date] BETWEEN '01' AND #Date, SL.[SalesByDate], 0))
FROM #Product PT
INNER JOIN #Sales SL
ON PT.[ProductID] = SL.[ProductID]
INNER JOIN #Period PD
ON SL.[TimeID] = PD.[TimeID]
GROUP BY PT.[ProductName]
EDIT:
If you need to use sub-query, this is how your example can works:
SELECT PT.[ProductName]
,SUM(SL.[SalesByDate])
,DataSource.[TotalSalesByDate]
FROM #Product PT
INNER JOIN #Sales SL
ON PT.[ProductID] = SL.[ProductID]
INNER JOIN #Period PD
ON SL.[TimeID] = PD.[TimeID]
INNER JOIN
(
SELECT S.[ProductID]
,SUM(S.[SalesByDate]) AS [TotalSalesByDate]
FROM #Sales S
INNER JOIN #Period P
ON S.[TimeID] = P.[TimeID]
WHERE P.[Date] BETWEEN '01' AND #Date
GROUP BY S.[ProductID]
) AS DataSource
ON PT.[ProductID] = DataSource.[ProductID]
WHERE PD.[Date] = #Date
GROUP BY PT.[ProductName]
,DataSource.[TotalSalesByDate]
First, in the Table Period you must have dates, not '01','02' so you can use BETWEEN. Or you can use 1,2,3 ... but they have to be numbers.
So, we suppose that in table Table Period you have numbers for dates (I make this remark, because you use 01, instead of 1 which assumes string value. The query itself is relatively easy:
SELECT
p.ProductName,
SUM(CASE WHEN s.TimeID = 10 THEN s.SalesByDate ELSE 0 END) as SalesByDate,
SUM(CASE WHEN s.TimeID = 10 THEN 0 ELSE s.SalesByDate END) as TotalSalesUntilDate
FROM
Product p
INNER JOIN Salse s ON p.ProductID = s.ProductID
WHERE
s.TimeID BETWEEN 1 AND 10
GROUP BY p.ProductName;
You take Sales for each date. If this is a selected date then add sales to column SalesByDate, else add then to column TotalSalesUntilDate. You group by ProductName to calculate SUM. And select only dates which are in the desired period in WHERE clause. We assume that this query is started only for a specific month (because we use only date element - i.e. 1,2,... not month).
This will show only Products with sales. If you want to see all Products list use LEFT JOIN instead of INNER JOIN.

Resources