Referencing the current row outer apply column within separate outer join - sql-server

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

Related

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)

Group by a set of Values (Check-In or Check-Out)

I have a simple table which records people clocking-in and clocking out like so.
Id | EmployeeNumber | InOutId | InOutDateTime
-----------------------------------------------------
1 | 505 | IN | 2015-03-24 08:32:42:000
1 | 506 | IN | 2015-03-24 08:35:47:000
1 | 507 | IN | 2015-03-24 08:46:12:000
1 | 505 | OUT | 2015-03-24 16:59:00:000
1 | 506 | OUT | 2015-03-24 17:05:00:000
I want to show the total people currently IN and those currently OUT. In other words:
- Total IN means those that do not have a corresponding OUT for that given day. - Total OUT means those that do have an IN and an OUT for that given day.
So, based on my table above, I want to get the following results:
TotalCurrentlyIn | TotalCurrentlyOut
-----------------------------------------
1 | 2
This is what I have so far:
DECLARE #d date;
set #d = cast('2015-03-24 15:02:42.000' as date)
select EmployeeNumber, InOutId, InOutDateTime from MyAttendance
where
InOutDateTime >= DATEADD(day, DATEDIFF(day, 0, #d), 0)
and InOutDateTime < DATEADD(day, DATEDIFF(day, 0, #d) +1, 0)
order by
EmployeeNumber, InOutId
I need to be able to sum and group by - any ideas?
try,
DECLARE #d date;
set #d = cast('2015-03-24 15:02:42.000' as date)
;with cte as(
select t.EmployeeNumber,t.InOutId as in1,
t1.InOutId out1,t.InOutDateTime from #t t
left join (select EmployeeNumber,InOutId,InOutDateTime from #t
where InOutId='OUT' and cast(InOutDateTime as date)=cast(#d as date) ) t1
on t.EmployeeNumber=t1.EmployeeNumber and
cast(t.InOutDateTime as date)=cast(t1.InOutDateTime as date)
where t.InOutId='IN' and cast(t.InOutDateTime as date)=cast(#d as date))
select count(in1) Totalin,count(out1) Totalout, sum(case when out1 is null then 1 else 0 end) TotalCurrentlyIn
,count(out1) TotalCurrentlyOut from cte
data
declare #t table (Id int,EmployeeNumber int, InOutId varchar(3), InOutDateTime datetime)
insert into #t(Id, EmployeeNumber,InOutId, InOutDateTime) values
(1 , 505 , 'IN' , '2015-03-24 08:32:42:000'),
(1 , 506 , 'IN' , '2015-03-24 08:35:47:000'),
(1 , 507 , 'IN' , '2015-03-24 08:46:12:000'),
(1 , 505 , 'OUT' , '2015-03-24 16:59:00:000'),
(1 , 506 , 'OUT' , '2015-03-24 17:05:00:000')
CheckIn = 1 and CheckOut = 2 so you need to check last entry of all the uses.
Select EmployeeId, ActionType, Max(ActionDateTime)
From AttendanceLog
Where
ActionDateTime >= DATEADD(day, DATEDIFF(day, 0, #d), 0)
and ActionDateTime < DATEADD(day, DATEDIFF(day, 0, #d) +1, 0)
Group by
EmployeeId, ActionType
Order by
EmployeeId,ActionType
If I understand the question. you need to know how much person is in the office right now:
the first query return the max date for any employee, than you join it with the actionType
select
EmployeeId , max(ActionDateTime) as MaxActionDateTime into #temptable
from table
group by EmployeeId
select count (EmployeeId), ActionType
from table inner join #temptable
on table.EmployerId == #temptable.EmployerId
and table.ActionDateTime == #temptable.MaxActionDateTime
group by ActionType
Using a windowing function you can get the last action for every employee and count those
With data As (
Select id, EmployeeNumber, InOutId
, lastAction = ROW_NUMBER() OVER (PARTITION BY EmployeeNumber
ORDER BY InOutDateTime DESC)
From table1
)
Select Count(CASE InOutId WHEN 'IN' THEN 1 END) TotalCurrentlyIn
, Count(CASE InOutId WHEN 'OUT' THEN 1 END) TotalCurrentlyOut
From data
Where lastAction = 1

sql query to find the Items with the highest difference

I have my database table ABC as shown below :
ItemId Month Year Sales
1 1 2013 333
1 2 2013 454
2 1 2013 434
and so on .
I would like to write a query to find the top 3 items that have had the highest increase in sales from last month to this month , so that I see somethinglike this in the output.
Output :
ItemId IncreaseInSales
1 +121
9 +33
6 +16
I came up to here :
select
(select Sum(Sales) from ABC where [MONTH] = 11 )
-
(select Sum(Sales) from ABC where [MONTH] = 10)
I cannot use a group by as it is giving an error . Can anyone point me how I can
proceed further ?
Assuming that you want the increase for a given month, you can also do this with an aggregation query:
select top 3 a.ItemId,
((sum(case when year = #YEAR and month = #MONTH then 1.0*sales end) /
sum(case when year = #YEAR and month = #MONTH - 1 or
year = #YEAR - 1 and #Month = 1 and month = 12
then sales end)
) - 1
) * 100 as pct_increase
from ABC a
group by a.ItemId
order by pct_increase desc;
You would put the year/month combination you care about in the variables #YEAR and #MONTH.
EDIT:
If you just want the increase, then do a difference:
select top 3 a.ItemId,
(sum(case when year = #YEAR and month = #MONTH then 1.0*sales end) -
sum(case when year = #YEAR and month = #MONTH - 1 or
year = #YEAR - 1 and #Month = 1 and month = 12
then sales
end)
) as difference
from ABC a
group by a.ItemId
order by difference desc;
Here is the SQL Fiddle that demonstrates the below query:
SELECT TOP(3) NewMonth.ItemId,
NewMonth.Month11Sales - OldMonth.Month10Sales AS IncreaseInSales
FROM
(
SELECT s1.ItemId, Sum(s1.Sales) AS Month11Sales
FROM ABC AS s1
WHERE s1.MONTH = 11
AND s1.YEAR = 2013
GROUP BY s1.ItemId
) AS NewMonth
INNER JOIN
(
SELECT s2.ItemId, Sum(s2.Sales) AS Month10Sales
FROM ABC AS s2
WHERE s2.MONTH = 10
AND s2.YEAR = 2013
GROUP BY s2.ItemId
) AS OldMonth
ON NewMonth.ItemId = OldMonth.ItemId
ORDER BY NewMonth.Month11Sales - OldMonth.Month10Sales DESC
You never mentioned if you could have more than one record for an ItemId with the same Month, so I made the query to handle it either way. Obviously you were lacking the year = 2013 in your query. Once you get past this year you will need that.
Another option could be something on these lines:
SELECT top 3 a.itemid, asales-bsales increase FROM
(
(select itemid, month, sum(sales) over(partition by itemid) asales from ABC where month=2
and year=2013) a
INNER JOIN
(select itemid, month, sum(sales) over(partition by itemid) bsales from ABC where month=1
and year=2013) b
ON a.itemid=b.itemid
)
ORDER BY increase desc
if you need to cater for months without sales then you can do a FULL JOIN and calculate increase as isnull(asales,0) - isnull(bsales,0)
You could adapt this solution based on PIVOT operator:
SET NOCOUNT ON;
DECLARE #Sales TABLE
(
ItemID INT NOT NULL,
SalesDate DATE NOT NULL,
Amount MONEY NOT NULL
);
INSERT #Sales (ItemID, SalesDate, Amount)
VALUES
(1, '2013-01-15', 333), (1, '2013-01-14', 111), (1, '2012-12-13', 100), (1, '2012-11-12', 150),
(2, '2013-01-11', 200), (2, '2012-12-10', 150), (3, '2013-01-09', 900);
-- Parameters (current year & month)
DECLARE #pYear SMALLINT = 2013,
#pMonth TINYINT = 1;
DECLARE #FirstDayOfCurrentMonth DATE = CONVERT(DATE, CONVERT(CHAR(4), #pYear) + '-' + CONVERT(CHAR(2), #pMonth) + '-01');
DECLARE #StartDate DATE = DATEADD(MONTH, -1, #FirstDayOfCurrentMonth), -- Begining of the previous month
#EndDate DATE = DATEADD(DAY, -1, DATEADD(MONTH, 1, #FirstDayOfCurrentMonth)) -- End of the current month
SELECT TOP(3) t.ItemID,
t.[2]-t.[1] AS IncreaseAmount
FROM
(
SELECT y.ItemID, y.Amount,
DENSE_RANK() OVER(ORDER BY y.FirstDayOfSalesMonth ASC) AS MonthNum -- 1=Previous Month, 2=Current Month
FROM
(
SELECT x.ItemID, x.Amount,
DATEADD(MONTH, DATEDIFF(MONTH, 0, x.SalesDate), 0) AS FirstDayOfSalesMonth
FROM #Sales x
WHERE x.SalesDate BETWEEN #StartDate AND #EndDate
) y
) z
PIVOT( SUM(z.Amount) FOR z.MonthNum IN ([1], [2]) ) t
ORDER BY IncreaseAmount DESC;
SQLFiddle demo
Your sample data seems to be incomplete, however, here is my try. I assume that you want to know the three items with the greatest sales-difference from one month to the next:
WITH Increases AS
(
SELECT a1.itemid,
a1.sales - (SELECT a2.sales
FROM dbo.abc a2
WHERE a1.itemid = a2.itemid
AND ( ( a1.year = a2.year
AND a1.month > 1
AND a1.month = a2.month + 1 )
OR ( a1.year = a2.year + 1
AND a1.month = 1
AND a2.month = 12 ) ))AS IncreaseInSales
FROM dbo.abc a1
)
SELECT TOP 3 ItemID, MAX(IncreaseInSales) AS IncreaseInSales
FROM Increases
GROUP BY ItemID
ORDER BY MAX(IncreaseInSales) DESC
Demo
SELECT
cur.[ItemId]
MAX(nxt.[Sales] - cur.[Sales]) AS [IncreaseInSales]
FROM ABC cur
INNER JOIN ABC nxt ON (
nxt.[Year] = cur.[Year] + cur.[month]/12 AND
nxt.[Month] = cur.[Month]%12 + 1
)
GROUP BY cur.[ItemId]
I'd do this this way. It should work in all the tagged versions of SQL Server:
SELECT TOP 3 [ItemId],
MAX(CASE WHEN [Month] = 2 THEN [Sales] END) -
MAX(CASE WHEN [Month] = 1 THEN [Sales] END) [Diff]
FROM t
WHERE [Month] IN (1, 2) AND [Year] = 2013
GROUP BY [ItemId]
HAVING COUNT(*) = 2
ORDER BY [Diff] DESC
Fiddle here.
The reason why I'm adding the HAVING clause is that if any item is added in only one of the months then the numbers will be all wrong. So I'm only comparing items that are only present in both months.
The reason of the WHERE clause would be to filter in advance only the needed months and improve the efficiency of the query.
An SQL Server 2012 solution could also be:
SELECT TOP 3 [ItemId], [Diff] FROM (
SELECT [ItemId],
LEAD([Sales]) OVER (PARTITION BY [ItemId] ORDER BY [Month]) - [Sales] Diff
FROM t
WHERE [Month] IN (1, 2) AND [Year] = 2013
) s
WHERE [Diff] IS NOT NULL
ORDER BY [Diff] DESC

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.

how to fetch record monthly total in select query in sql server 2005

I am having following output of query
Query:
SELECT DATENAME(mm, date) [Month], sum(braekTime) [TotalBreakTime],
sum(DATEPART(hh,totalTime) * 60 + DATEPART(mi,totalTime) + DATEPART(ss,totalTime) * 0.017) [Minute],firstName
FROM employeeAttendance,employee
where FK_employeeId = employee.employeeId
GROUP BY DATENAME(mm, date),firstName
ORDER BY [Month]
but I want each n every month record with null/ 0 value
like June and July record is not available then it should display like following
Month TotalBreakTime Minute firstName
----- -------------- ------ ---------
January 0 0 NULL
February 0 0 NULL
March 0 0 NULL
April 0 0 NULL
May 50 1015.000 foramaa
June 0 0 NULL
July 0 0 NULL
.... Like till Dec
You should create a virtual table or subquery for the months, and left join it to the totals query.
eg
select * from
(
select number, datename(m,DATEADD(m, number-1, 0)) as monthname
from master..spt_values
where type='p' and number between 1 and 12
) months
left join
(your totals query) totals
on months.monthname = totals.month
try this:
;with cte as(
select 1 as rn union all select 2 union all select 3),
cte1 as (select ROW_NUMBER() over(order by c1.rn) as row_num
from cte cross join cte c1 cross join cte c2)
select * from cte1
left join
(SELECT DATENAME(mm, date) [Month],
sum(braekTime) [TotalBreakTime],
sum(DATEPART(hh,totalTime) * 60 + DATEPART(mi,totalTime) + DATEPART(ss,totalTime) * 0.017) [Minute],
firstName
FROM employeeAttendance join employee
on FK_employeeId = employee.employeeId
GROUP BY DATENAME(mm, date),firstName
ORDER BY [Month])B
on B.[Month]=DateName( month , DateAdd( month ,cte1.row_num , 0 ) - 1 )
and cte1.row_num <=12

Resources