Aggregate function at Subquery And Performance Issue - sql

I have query which i should select records from 1 table to insert another table once everyday(Select Into). It's big and enough complicated query for me because of i want to do it it 1 query. Here is piece of query which i want to change(it has many inner join and group by variables) :
Select RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1)
THEN CAST(EventDate as datetime) ELSE NULL END),(Select Cast([Start] as time)
as Start FROM [dbo].[Period] Where strDay=DATEPART(dw,CAST('2017-01-30' as date))
And Timezone=[dbo].[Timezone].Timezone))/60),2) +':'+
RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1)
THEN CAST(EventDate as datetime) ELSE NULL END),(Select Cast([Start] as time) as Start
FROM [dbo].[Period] Where strDay=DATEPART(dw,CAST('2017-01-30' as date))
And Timezone=[Timezone].Timezone))%60),2) AS WorkingHours from [dbo].[Events]
This query works and it give returns such answer 01:25 hours. When i want to change [EnteranceDevice] IN(1) to [EnteranceDevice] IN(Select ReaderInput from [SPS].[dbo].Users) it gives "Cannot perform an aggregate function on an expression containing an aggregate or a subquery" error
How can i solve this problem?
ReaderInput(nvarchar(50)) has such values(at 1 record) '1,3,5' because of it should store more than 1 device ID but EnteranceDevice is int. So Casting(Cast(EnteranceDevice as nvarchar(50)) IN(...) ) will work?
I'm sure that you see many performance issues at my code. I want to hear recommendations about query.

remember... saying something is "in" is the same same as saying left join and target is not null, so the refactoring is easy as follows:
could not read your code had to reformat it to have some sort of logic....
Select
RIGHT(
'0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1) THEN CAST(EventDate as datetime) ELSE NULL END),
(Select Cast([Start] as time) as Start FROM [dbo].[Period] Where strDay=DATEPART(dw,CAST('2017-01-30' as date)) And Timezone=[dbo].[Timezone].Timezone))/60),
2
) +
':'+
RIGHT(
'0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1) THEN CAST(EventDate as datetime) ELSE NULL END),
(Select Cast([Start] as time) as Start FROM [dbo].[Period] Where strDay=DATEPART(dw,CAST('2017-01-30' as date)) And Timezone=[Timezone].Timezone))%60),
2) AS WorkingHours
from [dbo].[Events]
First of all you have the same sub-query twice. Pull that out into a cross join
Select
RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1) THEN CAST(EventDate as datetime) ELSE NULL END),x.start)/60),2) +
':'+
RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1) THEN CAST(EventDate as datetime) ELSE NULL END),x.start)%60),2) AS WorkingHours
from [dbo].[Events]
cross join (
Select Cast([Start] as time) as Start
FROM [dbo].[Period]
Where strDay=DATEPART(dw,CAST('2017-01-30' as date))
And Timezone=[Timezone].Timezone
) x
Now join to users and check for null
Select
RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN u.ReaderInput is not null THEN CAST(EventDate as datetime) ELSE NULL END),x.start)/60),2) +
':'+
RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN u.ReaderInput is not null THEN CAST(EventDate as datetime) ELSE NULL END),x.start)%60),2) AS WorkingHours
from [dbo].[Events]
left join [SPS].[dbo].Users u on [EnteranceDevice] = u.ReaderInput
cross join (
Select Cast([Start] as time) as Start
FROM [dbo].[Period]
Where strDay=DATEPART(dw,CAST('2017-01-30' as date))
And Timezone=[Timezone].Timezone
) x

Related

Select the first row returned by CASE WHEN

This is for SQL Server. I have a query that's trying to find the total balance in an account at a certain point in time (30 days before most recent transaction date, 90 days before most recent transaction date, etc). The table I'm querying keeps 'snapshots' of account balances over time with the amount and the time in which the transaction occurred.
PERSON
TOTALBALANCE
RCNTTRANS
Sarah
$5000
6/1/2021
Sarah
$4500
9/29/2021
Sarah
$7000
11/30/2021
Joe
$90
1/5/2020
Joe
$8000
1/17/2020
Joe
$2100
2/28/2021
I figured I could use a case statement to get the total balance at any date less than the most recent transaction date minus however many days away I'm looking for. However, this returns a row for every previous RCNTTRANS date. Is there a way to select only the first row that's returned?
SELECT
,T.PERSON
,CASE WHEN T.TRANSACTIONDATE <= DATEADD(DAY, -30, T.RCNTTRANS) THEN T.TOTALBALANCE ELSE 0 END AS TEST
,CASE WHEN T.TRANSACTIONDATE <= DATEADD(DAY, -90, T.RCNTTRANS) THEN T.TOTALBALANCE ELSE 0 END AS TEST2
,CASE WHEN T.TRANSACTIONDATE <= DATEADD(DAY, -180, T.RCNTTRANS) THEN T.TOTALBALANCE ELSE 0 END AS TEST3
FROM #TEMP T
I tried COALESCE, but that didn't seem to work. I also tried FIRST_VALUE, but that didn't seem to work either. I could possibly have been using them incorrectly, though.
Expected Results
PERSON
TEST1
TEST2
TEST3
Sarah
$4500
$5000
NULL
Joe
$8000
NULL
NULL
Aggregate and SUM each CASE WHEN, and within date ranges.
create table #TEMP (
PERSON VARCHAR(30),
TOTALBALANCE MONEY,
RCNTTRANS DATE
)
insert into #TEMP (PERSON, TOTALBALANCE, RCNTTRANS) values
('Sarah', $5000, '2021-11-29'),
('Sarah', $4500, '2021-12-01'),
('Sarah', $7000, '2021-12-30'),
('Joe', $90, '2020-08-28'),
('Joe', $8000, '2021-02-01'),
('Joe', $2100, '2021-02-28');
SELECT T.PERSON
, SUM(
CASE
WHEN T.RCNTTRANS >= DATEADD(DAY, -30, M.TRANSACTIONDATE)
AND T.RCNTTRANS < M.TRANSACTIONDATE
THEN T.TOTALBALANCE END) AS BALANCE1
, SUM(
CASE
WHEN T.RCNTTRANS >= DATEADD(DAY, -60, M.TRANSACTIONDATE)
AND T.RCNTTRANS < DATEADD(DAY, -30, M.TRANSACTIONDATE)
THEN T.TOTALBALANCE END) AS BALANCE2
, SUM(
CASE
WHEN T.RCNTTRANS >= DATEADD(DAY, -180, M.TRANSACTIONDATE)
AND T.RCNTTRANS < DATEADD(DAY, -60, M.TRANSACTIONDATE)
THEN T.TOTALBALANCE END) AS BALANCE3
FROM #TEMP T
LEFT JOIN (
SELECT PERSON, MAX(RCNTTRANS) AS TRANSACTIONDATE
FROM #TEMP
GROUP BY PERSON
) M ON M.PERSON = T.PERSON
GROUP BY T.PERSON
ORDER BY T.PERSON DESC;
PERSON
BALANCE1
BALANCE2
BALANCE3
Sarah
4500.0000
5000.0000
null
Joe
8000.0000
null
null
db<>fiddle here
IF OBJECT_ID('tempdb..#TMP') IS NOT NULL
DROP TABLE #TMP;
CREATE TABLE #TMP (
PERSON VARCHAR(30),
TOTALBALANCE MONEY,
RCNTTRANS DATE
)
INSERT INTO #TMP (PERSON, TOTALBALANCE, RCNTTRANS) VALUES
('Sarah', $5000, '2021-01-06'),
('Sarah', $4500, '2021-09-29'),
('Sarah', $7000, '2021-11-30'),
('Joe', $90, '2020-01-05'),
('Joe', $8000, '2020-01-17'),
('Joe', $2100, '2021-02-28');
DECLARE #TRANSACTIONDATE DATE = DATEFROMPARTS(2022,1,29); -- FOR TESTING
WITH CTE_BasicData
AS(
SELECT
T.PERSON,
T.TOTALBALANCE,
T.RCNTTRANS,
DATEDIFF(day, #TRANSACTIONDATE, T.RCNTTRANS) AS [DAYS],
CASE
WHEN DATEDIFF(day, #TRANSACTIONDATE, T.RCNTTRANS) <= -180 THEN '180 days'
WHEN DATEDIFF(day, #TRANSACTIONDATE, T.RCNTTRANS) <= -90 THEN '90 days'
WHEN DATEDIFF(day, #TRANSACTIONDATE, T.RCNTTRANS) <= -30 THEN '30 days'
ELSE NULL
END AS [Period]
FROM #TMP AS T
)
,CTE_Data
AS(
SELECT
d.PERSON,
d.TOTALBALANCE,
d.RCNTTRANS,
d.[DAYS],
d.[Period],
DENSE_RANK() OVER(PARTITION BY d.PERSON, d.[Period] ORDER BY d.[DAYS] DESC) AS [Ranking]
FROM CTE_BasicData AS d
)
,CTE_Pivot
AS(
SELECT
P.[PERSON]
,P.[30 days]
,P.[90 days]
,P.[180 days]
FROM (
SELECT
d.PERSON,
d.TOTALBALANCE,
d.RCNTTRANS,
d.[DAYS],
d.[Period]
FROM CTE_Data AS d
WHERE (1=1)
AND d.Ranking = 1
) AS D
PIVOT(SUM(D.TOTALBALANCE) FOR D.[Period] IN([30 days],[90 days],[180 days])) AS P
)
--SELECT * FROM CTE_Data ORDER BY PERSON, RCNTTRANS; RETURN;
--SELECT * FROM CTE_Pivot ORDER BY PERSON; RETURN;
SELECT
d.[PERSON]
,SUM(d.[30 days]) AS [30 days]
,SUM(d.[90 days]) AS [90 days]
,SUM(d.[180 days]) AS [180 days]
FROM CTE_Pivot AS d
GROUP BY
d.[PERSON]

SQL - result fom weekday(time) and from week(without-Time)

I'm trying to solve the problem. I need to get results from a working week (working hours are specified, for example, from 7:00 am to 8:00 pm), and I need to include weekends results (without specified time). But I dont know how to solve problem with weekday and include to result
select
c.PatientId
,g.PatientId
,c.StartDate
,g.TransferOrdinalNumber
from ClinicalEvent C
join PatientIncomming G on C.PatientId=G.PatientId
where
(DATEPART(dw, StartDate) in (1,2,3,4,5))
and (
(CAST(StartDate as Time) between '20:00' and '23:59')
or
(CAST(StartDate as Time) between '00:01' and '07:00')
)
and StartDate between '2020.02.01' and '2020.2.28'
and EventTypeId = '5365f53c-583b-4b53-bf50-2ec7c002e53c'
and g.TransferOrdinalNumber=1
group by C.PatientId, G.PatientId, c.StartDate, g.TransferOrdinalNumber
order by C.PatientId, G.PatientId
You can use UNION ALL statement can be helpful in the very basic method.
select
c.PatientId
,g.PatientId
,c.StartDate
,g.TransferOrdinalNumber
from ClinicalEvent C
join PatientIncomming G on C.PatientId=G.PatientId
where
(DATEPART(dw, StartDate) in (1,2,3,4,5))
and (
(CAST(StartDate as Time) between '20:00' and '23:59')
or
(CAST(StartDate as Time) between '00:01' and '07:00')
)
and StartDate between '2020.02.01' and '2020.2.28'
and EventTypeId = '5365f53c-583b-4b53-bf50-2ec7c002e53c'
and g.TransferOrdinalNumber=1
group by C.PatientId, G.PatientId, c.StartDate, g.TransferOrdinalNumber
UNION ALL
select
c.PatientId
,g.PatientId
,c.StartDate
,g.TransferOrdinalNumber
from ClinicalEvent C
join PatientIncomming G on C.PatientId=G.PatientId
where
(DATEPART(dw, StartDate) in (6,7))
--and (
--(CAST(StartDate as Time) between '20:00' and '23:59')
--or
--(CAST(StartDate as Time) between '00:01' and '07:00')
--)
and StartDate between '2020.02.01' and '2020.2.28'
and EventTypeId = '5365f53c-583b-4b53-bf50-2ec7c002e53c'
and g.TransferOrdinalNumber=1
group by C.PatientId, G.PatientId, c.StartDate, g.TransferOrdinalNumber
order by C.PatientId, G.PatientId

SQL Server: view is slow

I have a SQL Server view to show an overview of account statements, first we calculate the latest closing balances of the user accounts to know what the latest balance was from their account. This is the LATEST_CB_DATES part.
Than we calculate the next business days, meaning the 2 next days where we are expecting to receive a balance in the database. This happens in NEXT_B_DAYS
Finally we calculate if the account is expecting a closing balance, received one or received one too late. Note that we use a window reception ending for this.
IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = 'VIEW_AS_AS_ACCT_STAT')
DROP VIEW VIEW_AS_AS_ACCT_STAT
GO
CREATE VIEW VIEW_AS_AS_ACCT_STAT AS
WITH LATEST_CB_DATES AS (
SELECT * FROM (
SELECT row_number() over (partition by SD_ACCT.ID order by (AS_ACCT_STAT.CBAL_BAL_DATE) DESC) RN,SD_ACCT.ID, SD_ACCT.ACCT_NBR, AS_ACCT_STAT.CBAL_BAL_DATE AS BAL_DATE, SD_ACCT.CODE, SD_ACCT.CCY, SD_ACCT_GRP.ID AS GRP_ID, SD_ACCT_GRP.CODE AS ACCT_GRP_CODE, SD_ACCT.DATA_OWNER_ID, AS_ACCT_STAT.STATIC_DATA_BNK AS BANK_CODE, AS_ACCT_STAT.STATIC_DATA_HLD AS HOLDER_CODE
FROM SD_ACCT
LEFT JOIN AS_ACCT on SD_ACCT.ID = AS_ACCT.STATIC_DATA_ACCT_ID
LEFT JOIN AS_ACCT_STAT on AS_ACCT.ID = AS_ACCT_STAT.ACCT_ID
JOIN SD_ACCT_GRP_MEMBER ON SD_ACCT.ID = SD_ACCT_GRP_MEMBER.ACCT_ID
JOIN SD_ACCT_GRP on SD_ACCT_GRP_MEMBER.GRP_ID = SD_ACCT_GRP.ID
JOIN SD_ACCT_GRP_ROLE on SD_ACCT_GRP_ROLE.ID = SD_ACCT_GRP.ROLE_ID
WHERE SD_ACCT_GRP_ROLE.CODE = 'AccountStatementsToReceive' AND (AS_ACCT_STAT.VALID = 1 OR AS_ACCT_STAT.VALID IS NULL)
) LST_STMT
WHERE RN = 1
),
NEXT_B_DAYS AS (
SELECT VIEW_BUSINESS_DATES.CAL_ID, VIEW_BUSINESS_DATES.BUSINESS_DATE,
LEAD(VIEW_BUSINESS_DATES.BUSINESS_DATE, 1) OVER (PARTITION BY VIEW_BUSINESS_DATES.CAL_CODE ORDER BY VIEW_BUSINESS_DATES.BUSINESS_DATE) AS NEXT_BUSINESS_DATE,
LEAD(VIEW_BUSINESS_DATES.BUSINESS_DATE, 2) OVER (PARTITION BY VIEW_BUSINESS_DATES.CAL_CODE ORDER BY VIEW_BUSINESS_DATES.BUSINESS_DATE) AS SECOND_BUSINESS_DATE
FROM VIEW_BUSINESS_DATES
)
SELECT LATEST_CB_DATES.ID AS ACCT_ID,
LATEST_CB_DATES.CODE AS ACCT_CODE,
LATEST_CB_DATES.ACCT_NBR,
LATEST_CB_DATES.CCY AS ACCT_CCY,
LATEST_CB_DATES.BAL_DATE AS LATEST_CLOSING_BAL_DATE,
LATEST_CB_DATES.DATA_OWNER_ID,
LATEST_CB_DATES.BANK_CODE,
LATEST_CB_DATES.HOLDER_CODE,
LATEST_CB_DATES.ACCT_GRP_CODE,
CASE
WHEN LATEST_CB_DATES.BAL_DATE IS NULL THEN 'Expecting'
WHEN NEXT_B_DAYS.NEXT_BUSINESS_DATE IS NULL OR NEXT_B_DAYS.SECOND_BUSINESS_DATE IS NULL THEN 'Late'
WHEN AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_END IS NOT NULL AND GETDATE() >= TODATETIMEOFFSET(CAST(NEXT_B_DAYS.SECOND_BUSINESS_DATE AS DATETIME) + CAST(CAST(AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_END AS TIME) AS DATETIME), SEC_TIMEZONE.UTC_TIME_TOTAL_OFFSET) THEN 'Late'
WHEN AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_END IS NULL AND GETDATE() >= TODATETIMEOFFSET(CAST(NEXT_B_DAYS.SECOND_BUSINESS_DATE AS DATETIME) + CAST(CAST(AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_START AS TIME) AS DATETIME), SEC_TIMEZONE.UTC_TIME_TOTAL_OFFSET) AND CAST(AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_END AS TIME) >= CAST(AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_START AS TIME) THEN 'Expecting'
WHEN AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_END IS NULL AND GETDATE() >= TODATETIMEOFFSET(CAST(NEXT_B_DAYS.NEXT_BUSINESS_DATE AS DATETIME) + CAST(CAST(AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_START AS TIME) AS DATETIME), SEC_TIMEZONE.UTC_TIME_TOTAL_OFFSET) AND CAST(AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_END AS TIME) < CAST(AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_START AS TIME) THEN 'Expecting' -- overnight
WHEN AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_END IS NULL AND CAST (GETDATE() AS DATE) > NEXT_B_DAYS.SECOND_BUSINESS_DATE THEN 'Expecting'
ELSE 'Received'
END AS STAT,
CASE
WHEN LATEST_CB_DATES.BAL_DATE IS NULL THEN NULL
WHEN NEXT_B_DAYS.NEXT_BUSINESS_DATE IS NULL OR NEXT_B_DAYS.SECOND_BUSINESS_DATE IS NULL THEN NULL
WHEN AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_END IS NOT NULL THEN CAST(NEXT_B_DAYS.SECOND_BUSINESS_DATE AS DATETIME) + CAST(CAST(AS_AS_RECEPTION_CONF.RECEPTION_WINDOW_END AS TIME) AS DATETIME)
ELSE NULL
END AS DEADLINE,
SEC_TIMEZONE.UTC_TIME_TOTAL_OFFSET AS TIME_ZONE
FROM AS_AS_RECEPTION_CONF
JOIN LATEST_CB_DATES ON AS_AS_RECEPTION_CONF.ACCT_GRP_ID = LATEST_CB_DATES.GRP_ID
JOIN SEC_TIMEZONE ON SEC_TIMEZONE.ID = AS_AS_RECEPTION_CONF.TIME_ZONE_ID
LEFT JOIN NEXT_B_DAYS ON AS_AS_RECEPTION_CONF.CALENDAR_ID = NEXT_B_DAYS.CAL_ID AND LATEST_CB_DATES.BAL_DATE = NEXT_B_DAYS.BUSINESS_DATE
GO
SELECT * FROM VIEW_AS_AS_ACCT_STAT
What is the issue? Nothing, this works fine, but it's slow. We created a graphical report to display the data for our customers, but it takes 1minute, 30 seconds to load this SQL when you have 5000 accounts, which is too slow.
I guess the reason is the last line, but I didn't manage to refactor it well
LEFT JOIN NEXT_B_DAYS ON AS_AS_RECEPTION_CONF.CALENDAR_ID =
NEXT_B_DAYS.CAL_ID AND LATEST_CB_DATES.BAL_DATE =
NEXT_B_DAYS.BUSINESS_DATE
The exeuction plan of my sql can be found here
How can I refactor this to make my view still work but much more performant?

Display Month Gaps for Each location

I have the following query which takes in the opps and calculates the duration, and revenue for each month. However, for some locations, where there is no data, it is missing some months. Essentially, I would like all months to appear for each of the location and record type. I tried a left outer join on the calendar but that didn't seem to work either.
Here is the query:
;With DateSequence( [Date] ) as
(
Select CAST(#fromdate as DATE) as [Date]
union all
Select CAST(dateadd(day, 1, [Date]) as Date)
from DateSequence
where Date < #todate
)
INSERT INTO CalendarTemp (Date, Day, DayOfWeek, DayOfYear, WeekOfYear, Month, MonthName, Year)
Select
[Date] as [Date],
DATEPART(DAY,[Date]) as [Day],
DATENAME(dw, [Date]) as [DayOfWeek],
DATEPART(DAYOFYEAR,[Date]) as [DayOfYear],
DATEPART(WEEK,[Date]) as [WeekOfYear],
DATEPART(MONTH,[Date]) as [Month],
DATENAME(MONTH,[Date]) as [MonthName],
DATEPART(YEAR,[Date]) as [Year]
from DateSequence option (MaxRecursion 10000)
;
DELETE FROM CalendarTemp WHERE DayOfWeek IN ('Saturday', 'Sunday');
SELECT
AccountId
,AccountName
,Office
,Stage = (CASE WHEN StageName = 'Closed Won' THEN 'Closed Won'
ELSE 'Open'
END)
,Id
,Name
,RecordType= (CASE
WHEN recordtypeid = 'LAS1' THEN 'S'
END)
,Start_Date
,End_Date
,Probability
,Estimated_Revenue_Won = ISNULL(Amount, 0)
,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Name) AS Row
--,Revenue_Per_Day = CAST(ISNULL(Amount/NULLIF(dbo.CalculateNumberOFWorkDays(Start_Date, End_Date),0),0) as money)
,YEAR(c.Date) as year
,MONTH(c.Date) as Month
,c.MonthName
--, ISNULL(CAST(Sum((Amount)/NULLIF(dbo.CalculateNumberOFWorkDays(Start_Date, End_Date),0)) as money),0) As RevenuePerMonth
FROM SF_Extracted_Opps o
LEFT OUTER JOIN CalendarTemp c on o.Start_Date <= c.Date AND o.End_Date >= c.Date
WHERE
Start_Date <= #todate AND End_Date >= #fromdate
AND Office IN (#Location)
AND recordtypeid IN ('LAS1')
GROUP BY
AccountId
,AccountName
,Office
,(CASE WHEN StageName = 'Closed Won' THEN 'Closed Won'
ELSE 'Open'
END)
,Id
,Name
,(CASE
WHEN recordtypeid = 'LAS1' THEN 'S'
END)
,Amount
--, CAST(ISNULL(Amount/NULLIF(dbo.CalculateNumberOFWorkDays(Start_Date, End_Date),0),0) as money)
,Start_Date
,End_Date
,Probability
,YEAR(c.Date)
,Month(c.Date)
,c.MonthName
,dbo.CalculateNumberOFWorkDays(Start_Date, End_Date)
ORDER BY Office
, (CASE
WHEN recordtypeid = 'LAS1' THEN 'S'
END)
,(CASE WHEN StageName = 'Closed Won' THEN 'Closed Won'
ELSE 'Open'
END)
, [Start_Date], Month(c.Date), AccountName, Row;
I tried adding another left outer join to this and using this a sub query and the join essentially on the calendar based on the year and month, but that did not seem to work either. Suggestions would be extremely appreciated.
--Date Calendar for each location:
;With DateSequence( [Date], Locatio) as
(
Select CAST(#fromdate as DATE) as [Date], oo.Office as location
union all
Select CAST(dateadd(day, 1, [Date]) as Date), oo.Office as location
from DateSequence dts
join Opportunity_offices oo on 1 = 1
where Date < #todate
)
--select result
INSERT INTO CalendarTemp (Location,Date, Day, DayOfWeek, DayOfYear, WeekOfYear, Month, MonthName, Year)
Select
location,
[Date] as [Date],
DATEPART(DAY,[Date]) as [Day],
DATENAME(dw, [Date]) as [DayOfWeek],
DATEPART(DAYOFYEAR,[Date]) as [DayOfYear],
DATEPART(WEEK,[Date]) as [WeekOfYear],
DATEPART(MONTH,[Date]) as [Month],
DATENAME(MONTH,[Date]) as [MonthName],
DATEPART(YEAR,[Date]) as [Year]
from DateSequence option (MaxRecursion 10000)
;
you have your LEFT JOIN backwards if you want all records from CalendarTemp and only those that match from SF_Extracted_Opps then you the CalendarTemp should be the table on the LEFT. You can however switch LEFT JOIN to RIGHT JOIN and it should be fixed. The other issue will be your WHERE statement is using columns from your SF_Extracted_Opps table which will just make that an INNER JOIN again.
here is one way to fix.
SELECT
.....
FROM
CalendarTemp c
LEFT JOIN SF_Extracted_Opps o
ON o.Start_Date <= c.Date AND o.End_Date >= c.Date
AND o.Start_Date <= #todate AND End_Date >= #fromdate
AND o.Office IN (#Location)
AND o.recordtypeid IN ('LAS1')
The other issue you might run into is because you remove weekends from your CalendarTemp Table not all dates are represented I would test with the weekends still in and out and see if you get different results.
this line:
AND o.Start_Date <= #todate AND End_Date >= #fromdate
should not be needed either because you are already limiting the dates from the line before and values in your CalendarTempTable
A note about your CalendarDate table you don't have to go back and delete those records simply add the day of week as a WHERE statement on the select that populates that table.
Edit for All Offices you can use a cross join of your offices table with your CalendarTemp table to do this do it in your final query not the cte that builds the calendar. The problem with doing it in the CTE calendar definition is that it is recursive so you would have to do it in both the anchor and the recursive member definition.
SELECT
.....
FROM
CalendarTemp c
CROSS JOIN Opportunity_offices oo
LEFT JOIN SF_Extracted_Opps o
ON o.Start_Date <= c.Date AND o.End_Date >= c.Date
AND o.Start_Date <= #todate AND End_Date >= #fromdate
AND oo.office = o.Office
AND o.recordtypeid IN ('LAS1')

Return all distinct values for one column including null, and then count based on a condition

This is piece of a larger query I have. I want to return a list of all profit centers, with the incident count for those within the date range. I want the profit center listed, even if no incidents were reported in the date range. This has been working fine.
However now some records have been introduced where the profit center is NULL. I would like for those to show up in the list as 'N/A' with the incident count for the NULLs.
SELECT DISTINCT cast(plants.profit_ctr as varchar(50)) + ' - ' + cast(plants.profit_ctr_name as varchar(50)) AS Profit_Center, COALESCE(h.Incident_Count, 0) AS Incident_Count
FROM JMART.[safety].vwHELPForms AS plants
LEFT OUTER JOIN
(SELECT profit_ctr, COUNT(*) AS Incident_Count
FROM JMART.[safety].vwHELPForms
WHERE cast(rec_date as date) >= cast(#startdate as date)
AND cast(rec_date as date) <= cast(#enddate as date) AND ratings > 0
GROUP BY profit_ctr) AS h
ON h.profit_ctr = plants.profit_ctr
How can I accomplish this?
EDIT
If I run
SELECT profit_ctr, COUNT(*) AS Incident_Count
FROM JMART.[safety].vwHELPForms
WHERE cast(rec_date as date) >= cast(#startdate as date)
AND cast(rec_date as date) <= cast(#enddate as date) AND ratings > 0
GROUP BY profit_ctr
I get
NULL 295
101100 7483
101150 116
101200 445
101400 3784
I've tried
SELECT DISTINCT cast(plants.profit_ctr as varchar(50)) + ' - ' + cast(plants.profit_ctr_name as varchar(50)) AS Profit_Center,
COALESCE((SELECT COUNT(*) AS Incident_Count FROM JMART.[safety].vwHELPForms AS h
WHERE profit_ctr = plants.profit_ctr AND cast(rec_date as date) >= cast(#startdate as date)
AND cast(rec_date as date) <= cast(#enddate as date) AND ratings > 0
GROUP BY profit_ctr), 0) AS Incident_Count
FROM JMART.[safety].vwHELPForms AS plants
order by Profit_Center
which gives me (same as what I'm currently getting)
NULL 0
101100 7483
101150 116
101200 445
101400 3784
I want
N/A 295
101100 7483
101150 116
101200 445
101400 3784
The NULL values are going into the second table in your join, rather than the first. So, they are lost by a left outer join. Instead, you want to use a full outer join:
SELECT DISTINCT
coalesce(cast(plants.profit_ctr as varchar(50)) + ' - ' + cast(plants.profit_ctr_name as varchar(50)), 'N/A') AS Profit_Center,
COALESCE(h.Incident_Count, 0) AS Incident_Count
FROM JMART.[safety].vwHELPForms plants full OUTER JOIN
(SELECT profit_ctr, COUNT(*) as Incident_Count
FROM JMART.[safety].vwHELPForms
WHERE cast(rec_date as date) >= cast(#startdate as date) AND
cast(rec_date as date) <= cast(#enddate as date) AND ratings > 0
GROUP BY profit_ctr
) h
ON h.profit_ctr = plants.profit_ctr
Assuming that profit_ctr is not repeated in the plants table, you can dispense with the distinct. It adds unnecessary processing and may not be needed.
Since you are using LEFT JOIN, null profit centers will show up in result as NULL. And since you are using DISTINCT they will be collapsed to one row. And for the Incident_Count column you can use coalesce(cast(h.Incident_Count as varchar(50)), 'N/A')
PS. I assumed you are using MS SQL Server 2012.