SQL Server : find dates between two separate row entries - sql

I am trying to pull a list of AccountNumbers that have 2 specific charges (by code) that are at least 2 days apart. These are the columns of my table:
AccountNumber
ServiceDate
Code
Example: if there is AccountNumber for Code = 33967 on ServiceDate 12/11/2018 and an AccountNumber for Code = 33968 on ServiceDate 12/15/2018, the AccountNumber will be output to the results window because these two instances show up on DIFFERENT ServiceDates and are at least 2 days apart.
Example 2: if there is an AccountNumber for Code = 33967 on ServiceDate 12/11/2018 and an AccountNumber for Code = 33968 on ServiceDate 12/11/2018, the AccountNumber will NOT be output to the results window because these two instances show up on the same ServiceDate.
Example 3: if there is an AccountNumber for Code = 33967 on ServiceDate 12/11/2018 and an AccountNumber for Code = 33968 on ServiceDate 12/12/2018, the AccountNumber will NOT be output to the results window because there are no dates between the two ServiceDate's. However if it were 12/11 and 12/13 it would be acceptable because there is a day in-between.
I am only concerned about Code 33967 and 33968, all other codes should not be considered. Right now, I am able to pull all Accounts with both these codes on file but cannot figure out how to go further. Any ideas?
My code is as follows:
SELECT AccountNumber, ServiceDate
FROM dbo.table
WHERE Code = '33968'
INTERSECT
SELECT AccountNumber, ServiceDate
FROM dbo.table
WHERE Code = '33967'

here you go with some sample data. Feel free to add some more rows and test it up
create table #Temp_table
(
AccountNumber int null
, ServiceDate date null
,Code int null
)
insert into #Temp_table values
(1,'12/11/2018',33967)
,(2,'12/15/2018',33968)
,(3,'12/11/2018',33967)
,(4,'12/12/2018',33968)
,(5,'12/17/2018',33968)
,(6,'12/16/2018',33967)
;with CTE_MinDate as (
select --Code ,
MinServiceDate = min(Servicedate)
from #Temp_table
--group by Code
)
--select * from CTE_MinDate
select *
from (
select *
,Days_Diff = datediff(day,MinServiceDate, Servicedate)
from (
select a.*
,MinServiceDate = (select MinServiceDate from CTE_MinDate)
from #Temp_table a
where a.Code in ( 33967,33968)
) a
) b where Days_Diff >= 2

Does this do what you want?
SELECT DISTINCT t.AccountNumber
FROM dbo.table t
WHERE t.Code = '33968' AND
EXISTS (SELECT 1
FROM dbo.table t2
WHERE t2.AccountNumber = t.AccountNumber AND
t2.Code = '33967' AND
t2.ServiceDate <> t.ServiceDate
);

If it's only the Account Number you want then how about:
SELECT AccountNumber
FROM dbo.[Table]
GROUP BY AccountNumber
HAVING COUNT(CASE CODE WHEN 33968 THEN 1 END) > 0
AND COUNT(CASE CODE WHEN 33967 THEN 1 END) > 0;

Related

max latest null value sql

I am experiencing the following problem. I have a table in this table I same history. Due to an error, I'm interested in finding the following information.
The latest record for a user where column1 value is null and the modifiedon date is the newest for this user. The problem is the table contains more records where the modifiedon is not null for this user and mutated after the date I'm looking for.
Can someone please point me in the right direction?
Sample data:
personid FreeField01 ModifiedOn
1 0004998 15-10-2019 11:48:19
1 NULL 20-10-2019 01:53:39
1 0004998 22-10-2019 14:58:44
1 0004998 22-10-2019 14:58:44
1 NULL 23-10-2019 07:52:46
1 0004998 23-10-2019 17:16:45
So for this user, I'm not interested in any record and should be excluded from the result because the modified on datetime should be before 29-10 and before that date the freefield01 value should be null and modifiedon should be the latest.
Three conditions:
There is no newer entry for the person.
The entry value is NULL.
The date is before 2019-10-29.
The query:
select *
from mytable
where not exists
(
select *
from mytable newer
where newer.personid = mytable.personid
and newer.modifiedon > mytable.modifiedon
)
and freefield01 is null
and modifiedon < date '2019-10-29'
order by personid;
You can use this below script-
WITH CTE
AS(
SELECT personid,MAX(ModifiedOn) MD
FROM your_table
GROUP BY personid
HAVING MAX(ModifiedOn) < '30-10-2019'
)
SELECT * FROM your_table A
INNER JOIN CTE B ON A.personid = B.personid
AND A.ModifiedOn = B.MD
AND A.FreeField01 IS NULL
DEMO HERE
If I understand correctly, you are looking for persons where the FreeField01 has a value of NULL as of a certain date.
Here is one method:
select t.*
from t
where t.ModifiedOn = (select max(t2.ModifiedOn)
from t t2
where t2.personid = t.personid and
t2.ModifiedOn <= '2019-10-29'
) and
t.FreeField01 is null;
EDIT:
Based on your comment, you might just want an aggregation and having:
select personid
from t
where t.ModifiedOn <= '2019-10-29'
group by person_id
having sum(case when t.FreeField01 is null then 1 else 0 end) = 0
The simplest query that I found might be the following if I understand your request well :
SELECT t.personid, t.FreeField01, MAX(ModifiedOn) FROM test t
GROUP BY personid
HAVING MAX(ModifiedOn) < '29-10-2019' AND FreeField01 IS NULL
SEE EXAMPLE HERE
EDIT : Following below suggestions you can use this query instead :
SELECT t1.personid, t1.FreeField01, t1.ModifiedOn
FROM test t1
JOIN (
SELECT t.personid, MAX(ModifiedOn) AS MaxModifiedOn FROM test t
GROUP BY personid
HAVING MAX(ModifiedOn) < STR_TO_DATE('29-10-2019','%d-%m-%Y')
) t2 ON (t1.personid = t2.personid AND t1.ModifiedOn = t2.MaxModifiedOn)
WHERE FreeField01 IS NULL
SEE NEW DEMO HERE

Check whether an employee is present on three consecutive days

I have a table called tbl_A with the following schema:
After insert, I have the following data in tbl_A:
Now the question is how to write a query for the following scenario:
Put (1) in front of any employee who was present three days consecutively
Put (0) in front of employee who was not present three days consecutively
The output screen shoot:
I think we should use case statement, but I am not able to check three consecutive days from date. I hope I am helped in this
Thank you
select name, case when max(cons_days) >= 3 then 1 else 0 end as presence
from (
select name, count(*) as cons_days
from tbl_A, (values (0),(1),(2)) as a(dd)
group by name, adate + dd
)x
group by name
With a self-join on name and available = 'Y', we create an inner table with different combinations of dates for a given name and take a count of those entries in which the dates of the two instances of the table are less than 2 units apart i.e. for each value of a date adate, it will check for entries with its own value adate as well as adate + 1 and adate + 2. If all 3 entries are present, the count will be 3 and you will have a flag with value 1 for such names(this is done in the outer query). Try the below query:
SELECT Z.NAME,
CASE WHEN Z.CONSEQ_AVAIL >= 3 THEN 1 ELSE 0 END AS YOUR_FLAG
FROM
(
SELECT A.NAME,
SUM(CASE WHEN B.ADATE >= A.ADATE AND B.ADATE <= A.ADATE + 2 THEN 1 ELSE 0 END) AS CONSEQ_AVAIL
FROM
TABL_A A INNER JOIN TABL_A B
ON A.NAME = B.NAME AND A.AVAILABLE = 'Y' AND B.AVAILABLE = 'Y'
GROUP BY A.NAME
) Z;
Due to the complexity of the problem, I have not been able to test it out. If something is really wrong, please let me know and I will be happy to take down my answer.
--Below is My Approch
select Name,
Case WHen Max_Count>=3 Then 1 else 0 end as Presence
from
(
Select Name,MAx(Coun) as Max_Count
from
(
select Name, (count(*) over (partition by Name,Ref_Date)) as Coun from
(
select Name,adate + row_number() over (partition by Name order by Adate desc) as Ref_Date
from temp
where available='Y'
)
) group by Name
);
select name as employee , case when sum(diff) > =3 then 1 else 0 end as presence
from
(select id, name, Available,Adate, lead(Adate,1) over(order by name) as lead,
case when datediff(day, Adate,lead(Adate,1) over(order by name)) = 1 then 1 else 0 end as diff
from table_A
where Available = 'Y') A
group by name;

Selecting a group of dates in SQL Server

I have a history table that captures updates to a certain object and, in addition to other information, captures the time this update happened. What I would like to do is SELECT the MIN(LogDate) corresponding to a certain ActionTaken column.
More specifically, the history table may have other (more recent) rows where ActionTaken = 1, but I want to capture the date ActionTaken became 1.
Example:
SELECT MIN(LogDate) AS FirstActionDate
FROM HistoryTable
WHERE ID = 123
AND FirstActionTaken = 1
SELECT MIN(LogDate) AS SecondActionDate
FROM HistoryTable
WHERE ID = 123
AND SecondActionTaken = 1
SELECT MIN(LogDate) AS ThirdActionDate
FROM HistoryTable
WHERE ID = 123
AND ThirdActionTaken = 1
This works well, and I receive the proper dates without issue. Where I'm running into trouble is then going to select the MAX(LogDate) from this group:
SELECT MAX(LogDate) AS LastActionDate
FROM HistoryTable
WHERE ID = 123
AND LogDate IN
(
( SELECT MIN(LogDate) AS FirstActionDate
FROM HistoryTable
WHERE ID = 123
AND FirstActionTaken = 1 ),
( SELECT MIN(LogDate) AS SecondActionDate
FROM HistoryTable
WHERE ID = 123
AND SecondActionTaken = 1 ),
( SELECT MIN(LogDate) AS ThirdActionDate
FROM HistoryTable
WHERE ID = 123
AND ThirdActionTaken = 1 )
)
This also works, but I hate doing it this way. I could save out the previous statements into variables and just SELECT MAX() from those; it would certainly be more readable, but what would the JOIN syntax look like for this query?
Is there a way to combine the first three SELECT statements into one that returns all three dates and isn't an unreadable mess?
How can I grab the most recent LogDate (as a separate column) from this result set and without the (seemingly unnecessary) repeating SELECT statements?
EDIT:
Here are a few links I've found in relation to the answers that have been given so far:
Data Normalization
Using OUTER/CROSS APPLY
UNPIVOT (and others)
Hopefully these will help with others looking for solutions to similar problems!
This would be easier with a normalized data structure. Here is one method that uses conditional aggregation to calculate the three minimum dates. Then it takes the maximum of those values:
SELECT v.dt
FROM (SELECT MIN(CASE WHEN FirstActionTaken = 1 THEN LogDate END) AS d1,
MIN(CASE WHEN SecondActionTaken = 1 THEN LogDate END) AS d2,
MIN(CASE WHEN ThirdActionTaken = 1 THEN LogDate END) AS d3
FROM HistoryTable
WHERE ID = 123
) ht OUTER APPLY
(SELECT MAX(dt) as dt
FROM (VALUES (d1), (d2), (d3) ) v(dt)
) v;
EDIT 2
Based on new information that can be gleaned from OP's own answer (about how to define the latest action date), the query can be further simplified to simply this:
select coalesce(
min(case when ThirdActionTaken = 1 then LogDate end),
min(case when SecondActionTaken = 1 then LogDate end),
min(case when FirstActionTaken = 1 then LogDate end)
) as LastActionDate
from HistoryTable
where id = 123
Unpivot can also be used:
select max(ActionDate)
from (select min(case when FirstActionTaken = 1 then LogDate end) as FirstActionDate,
min(case when SecondActionTaken = 1 then LogDate end) as SecondActionDate,
min(case when ThirdActionTaken = 1 then LogDate end) as ThirdActionDate
from HistoryTable
where id = 123) t
unpivot (ActionDate for ActionDates in (FirstActionDate, SecondActionDate, ThirdActionDate)) unpvt
EDIT: Short explanation
This answer is very similar to Gordon's in that it uses conditional aggregation to get the 3 minimum dates in one query.
So the following part of the query:
select min(case when FirstActionTaken = 1 then LogDate end) as FirstActionDate,
min(case when SecondActionTaken = 1 then LogDate end) as SecondActionDate,
min(case when ThirdActionTaken = 1 then LogDate end) as ThirdActionDate
from HistoryTable
where id = 123
...might return something like...
FirstActionDate SecondActionDate ThirdActionDate
--------------- ---------------- ---------------
2015-01-01 2015-12-01 (null)
Then, the unpivot clause is what "unpivots" the 3 columns into a result set with 3 rows but a single column instead:
ActionDate
----------
2015-01-01
2015-12-01
(null)
Once the results are in this format, then a simple max aggregate function (select max(ActionDate)) can be applied to get the max value of the 3 rows.
You can use a UNION to join the 3 queries for your IN statement.
Something like
SELECT
MAX(ht1.LogDate) AS LastActionDate
FROM
HistoryTable ht1
WHERE
ht1.ID = 123
AND ht1.LogDate IN (SELECT
MIN(LogDate) AS FirstActionDate
FROM
HistoryTable ht2
WHERE
ht2.ID = ht1.ID
AND ht2.FirstActionTaken = 1
UNION
SELECT
MIN(LogDate) AS FirstActionDate
FROM
HistoryTable ht2
WHERE
ht2.ID = ht1.ID
AND ht2.SecondActionTaken = 1
UNION
SELECT
MIN(LogDate) AS FirstActionDate
FROM
HistoryTable ht2
WHERE
ht2.ID = ht1.ID
AND ht2.ThirdActionTaken = 1)
You can solve this problem without using PIVOT. The following code extends your initial code to store the MIN values into variables and then calculates the max value among them:
DECLARE #FirstActionDate DATETIME = NULL;
DECLARE #SecondActionDate DATETIME = NULL;
DECLARE #ThirdActionDate DATETIME = NULL;
DECLARE #LastActionDate DATETIME = NULL;
SELECT #FirstActionDate = MIN(LogDate)
FROM HistoryTable
WHERE ID = 123
AND FirstActionTaken = 1
SELECT #SecondActionDate = MIN(LogDate)
FROM HistoryTable
WHERE ID = 123
AND SecondActionTaken = 1
SELECT #ThirdActionDate = MIN(LogDate)
FROM HistoryTable
WHERE ID = 123
AND ThirdActionTaken = 1
-- calculate #LastActionDate as the greater from #FirstActionDate, #SecondActionDate and #ThirdActionDate.
SET #LastActionDate = #FirstActionDate;
IF (#SecondActionDate > #LastActionDate) SET #LastActionDate = #SecondActionDate;
IF (#ThirdActionDate > #LastActionDate) SET #LastActionDate = #ThirdActionDate;
SELECT #FirstActionDate AS [FirstActionDate]
, #SecondActionDate AS [SecondActionDate]
, #ThirdActionDate AS [ThirdActionDate]
, #LastActionDate AS [LastActionDate]
If you want the absolute last action date, you can change the original code to just a single statement, as follows:
SELECT MAX(LogDate) AS [LastActionDate]
, MIN(CASE WHEN FirstActionTaken = 1 THEN LogDate ELSE NULL END) AS [FirstActionDate]
, MIN(CASE WHEN SecondActionTaken = 1 THEN LogDate ELSE NULL END) AS [SecondActionDate]
, MIN(CASE WHEN ThirdActionTaken = 1 THEN LogDate ELSE NULL END) AS [ThirdActionDate]
FROM HistoryTable
WHERE ID = 123
My own attempt at refactoring the final SELECT statement:
SELECT MIN(ht2.LogDate) AS FirstActionDate,
MIN(ht3.LogDate) AS SecondActionDate,
MIN(ht4.LogDate) AS ThirdActionDate,
COALESCE (
MIN(ht4.LogDate),
MIN(ht3.LogDate),
MIN(ht2.LogDate)
) AS LastActionDate
FROM HistoryTable ht
INNER JOIN HistoryTable ht2
ON ht2.ID = ht.ID AND ht2.FirstActionTaken = 1
INNER JOIN HistoryTable ht3
ON ht3.ID = ht.ID AND ht3.SecondActionTaken = 1
INNER JOIN HistoryTable ht4
ON ht4.ID = ht.ID AND ht4.ThirdActionTaken = 1
WHERE ht.ID = 123
GROUP BY ht.ID
This JOINS back to HistoryTable for each xActionTaken column and SELECTS the MIN(LogDate) from each. Then, we walk backwards through the results (ThirdAction, SecondAction, FirstAction) and return the first one we find as LastActionTaken.
Admittedly this is a bit messy, but I thought it would be good to show another alternative to retrieving the same data.
Also worth noting for performance:
After running my answer against the UNPIVOT and OUTER APPLY methods, SSMS Execution Plan shows that UNPIVOT and OUTER APPLY are roughly equal (taking approx. 50% of the execution time each).
When comparing my method to either of these, my method takes approx. 88% of the execution time, where UNPIVOT/OUTER APPLY only take 12% - so both UNPIVOT and OUTER APPLY execute much faster (at least in this instance).
The reason that my method takes so much longer is that SQL does a table scan of HistoryTable for each time I join back to it, for a total of 4 scans. With the other two methods, this action is only performed once.

SQL Aggreate Functions

I have table which list a number of cases and assigned primary and secondary technicians. What I am trying to accomplish is to aggregate the number of cases a technician has worked as a primary and secondary tech. Should look something like this...
Technician Primary Secondary
John 4 3
Stacy 3 1
Michael 5 3
The table that I am pulling that data from looks like this:
CaseID, PrimaryTech, SecondaryTech, DOS
In the past I have used something like this, but now my superiors are asking for the number of secondary cases as well...
SELECT PrimaryTech, COUNT(CaseID) as Total
GROUP BY PrimaryTech
I've done a bit of searching, but cant seem to find the answer to my problem.
Select Tech,
sum(case when IsPrimary = 1 then 1 else 0 end) as PrimaryCount,
sum(case when IsPrimary = 0 then 1 else 0 end) as SecondaryCount
from
(
SELECT SecondaryTech as Tech, 0 as IsPrimary
FROM your_table
union all
SELECT PrimaryTech as Tech, 1 as IsPrimary
FROM your_table
) x
GROUP BY Tech
You can group two subqueries together with a FULL JOIN as demonstrated in this SQLFiddle.
SELECT Technician = COALESCE(pri.Technician, sec.Technician)
, PrimaryTech
, SecondaryTech
FROM
(SELECT Technician = PrimaryTech
, PrimaryTech = COUNT(*)
FROM Cases
WHERE PrimaryTech IS NOT NULL
GROUP BY PrimaryTech) pri
FULL JOIN
(SELECT Technician = SecondaryTech
, SecondaryTech = COUNT(*)
FROM Cases
WHERE SecondaryTech IS NOT NULL
GROUP BY SecondaryTech) sec
ON pri.Technician = sec.Technician
ORDER By Technician;
SELECT COALESCE(A.NAME, B.NAME) AS NAME, CASE WHEN A.CASES IS NOT NULL THEN A.CASES ELSE 0 END AS PRIMARY_CASES,
CASE WHEN B.CASES IS NOT NULL THEN B.CASES ELSE 0 END AS SECONDARY_CASES
FROM
(
SELECT COUNT(*) AS CASES, PRIMARYTECH AS NAME FROM YOUR_TABLE
GROUP BY PRIMARYTECH
) AS A
FULL OUTER JOIN
(
SELECT COUNT(*) AS CASES, SECONDARYTECH AS NAME FROM YOUR_TABLE
GROUP BY SECONDARYTECH
) AS B
ON A.NAME = B.NAME

SQL Optimize - From History table get value from two different dates

Not sure where to start... But basically I have a report table, an account table, and an account history table. The account history table will have zero or more records, where each record is the state of the account cancelled flag after it changed.
There is other stuff going on, but basically i am looking to return the account detail data, with the state of account cancelled bit on the start date and enddate as different columns.
What is the best way to do this?
I have the following working query below
(Idea) Should I do seperate joins on history table, 1 for each date?
I guess I could do it in three separate queries ( Get Begin Snapshot, End Snapshot, Normal Report query with a join to each snapshot)
something else?
Expected output:
AccountID, OtherData, StartDateCancelled, EndDateCancelled
Test Tables:
DECLARE #Report TABLE (ReportID INT, StartDate DATETIME, EndDate DATETIME)
DECLARE #ReportAccountDetail TABLE( ReportID INT, Accountid INT, Cancelled BIT )
DECLARE #AccountHistory TABLE( AccountID INT, ModifiedDate DATETIME, Cancelled BIT )
INSERT INTO #Report
SELECT 1,'1/1/2011', '2/1/2011'
--
INSERT INTO #ReportAccountDetail
SELECT 1 AS ReportID, 1 AS AccountID, 0 AS Cancelled
UNION
SELECT 1,2,0
UNION
SELECT 1,3,1
UNION
SELECT 1,4,1
--
INSERT INTO #AccountHistory
SELECT 2 AS CustomerID, '1/2/2010' AS ModifiedDate, 1 AS Cancelled
UNION--
SELECT 3, '2/1/2011', 1
UNION--
SELECT 4, '1/1/2010', 1
UNION
SELECT 4, '2/1/2010', 0
UNION
SELECT 4, '2/1/2011', 1
Current Query:
SELECT Accountid, OtherData,
MAX(CASE WHEN BeginRank = 1 THEN CASE WHEN BeginHistoryExists = 1 THEN HistoryCancelled ELSE DefaultCancel END ELSE NULL END ) AS StartDateCancelled,
MAX(CASE WHEN EndRank = 1 THEN CASE WHEN EndHistoryExists = 1 THEN HistoryCancelled ELSE DefaultCancel END ELSE NULL END ) AS EndDateCancelled
FROM
(
SELECT c.Accountid,
'OtherData' AS OtherData,
--lots of other data
ROW_NUMBER() OVER (PARTITION BY c.AccountID ORDER BY
CASE WHEN ch.ModifiedDate <= Report.StartDate THEN 1 ELSE 0 END DESC, ch.ModifiedDate desc) AS BeginRank,
CASE WHEN ch.ModifiedDate <= Report.StartDate THEN 1 ELSE 0 END AS BeginHistoryExists,
ROW_NUMBER() OVER ( PARTITION BY c.AccountID ORDER BY
CASE WHEN ch.ModifiedDate <= Report.EndDate THEN 1 ELSE 0 END DESC, ch.ModifiedDate desc) AS EndRank,
CASE WHEN ch.ModifiedDate <= Report.EndDate THEN 1 ELSE 0 END AS EndHistoryExists,
CAST( ch.Cancelled AS INT) AS HistoryCancelled,
0 AS DefaultCancel
FROM
#Report AS Report
INNER JOIN #ReportAccountDetail AS C ON Report.ReportID = C.ReportID
--Others joins related for data to return
LEFT JOIN #AccountHistory AS CH ON CH.AccountID = C.AccountID
WHERE Report.ReportID = 1
) AS x
GROUP BY AccountID, OtherData
Welcome input on writing stack overflow questions. Thanks!
ROW_NUMBER() often suprises me and out-performs my expectations. In this case, however, I'd be tempted to just use correlated sub-queries. At least, I'd test them against the alternatives.
Note: I would also use real tables, with real indexes, and a realistic volume of fake data. (If it's worth posting this question, I'm assuming that it's worth testing this realistically.)
SELECT
[Report].ReportID,
[Account].AccountID,
[Account].OtherData,
ISNULL((SELECT TOP 1 Cancelled FROM AccountHistory WHERE AccountID = [Account].AccountID AND ModifiedDate <= [Report].StartDate ORDER BY ModifiedDate DESC), 0) AS StartDateCancelled,
ISNULL((SELECT TOP 1 Cancelled FROM AccountHistory WHERE AccountID = [Account].AccountID AND ModifiedDate <= [Report].EndDate ORDER BY ModifiedDate DESC), 0) AS EndDateCancelled
FROM
Report AS [Report]
LEFT JOIN
ReportAccountDetail AS [Account]
ON [Account].ReportID = [Report].ReportID
ORDER BY
[Report].ReportID,
[Account].AccountID
Note: For whatever reason, I've found that TOP 1 and ORDER BY is faster than MAX().
In terms of your suggested answer, I'd modify it slightly to just use ISNULL instead of trying to make the Exists columns work.
I'd also join on the "other data" after all of the working out, rather than inside the inner-most query, so as to avoid having to group by all the "other data".
WITH
HistoricData AS
(
SELECT
Report.ReportID,
c.Accountid,
c.OtherData,
ROW_NUMBER() OVER (PARTITION BY c.ReportID, c.AccountID ORDER BY CASE WHEN ch.ModifiedDate <= Report.StartDate THEN 1 ELSE 0 END DESC, ch.ModifiedDate DESC) AS BeginRank,
ROW_NUMBER() OVER (PARTITION BY c.ReportID, c.AccountID ORDER BY ch.ModifiedDate DESC) AS EndRank,
CH.Cancelled
FROM
#Report AS Report
INNER JOIN
#ReportAccountDetail AS C
ON Report.ReportID = C.ReportID
LEFT JOIN
#AccountHistory AS CH
ON CH.AccountID = C.AccountID
AND CH.ModifiedDate <= Report.EndDate
)
,
FlattenedData AS
(
SELECT
ReportID,
Accountid,
OtherData,
ISNULL(MAX(CASE WHEN BeginRank = 1 THEN Cancelled END), 0) AS StartDateCancelled,
ISNULL(MAX(CASE WHEN EndRank = 1 THEN Cancelled END), 0) AS EndDateCancelled
FROM
[HistoricData]
GROUP BY
ReportID,
AccountID,
OtherData
)
SELECT
*
FROM
[FlattenedData]
LEFT JOIN
[OtherData]
ON Whatever = YouLike
WHERE
[FlattenedData].ReportID = 1
And a final possible version...
WITH
ReportStartHistory AS
(
SELECT
*
FROM
(
SELECT
[Report].ReportID,
ROW_NUMBER() OVER (PARTITION BY [Report].ReportID, [History].AccountID ORDER BY [History].ModifiedDate) AS SequenceID,
[History].*
FROM
Report AS [Report]
INNER JOIN
AccountHistory AS [History]
ON [History].ModifiedDate <= [Report].StartDate
)
AS [data]
WHERE
SequenceID = 1
)
,
ReportEndHistory AS
(
SELECT
*
FROM
(
SELECT
[Report].ReportID,
ROW_NUMBER() OVER (PARTITION BY [Report].ReportID, [History].AccountID ORDER BY [History].ModifiedDate) AS SequenceID,
[History].*
FROM
Report AS [Report]
INNER JOIN
AccountHistory AS [History]
ON [History].ModifiedDate <= [Report].EndDate
)
AS [data]
WHERE
SequenceID = 1
)
SELECT
[Report].ReportID,
[Account].*,
ISNULL([ReportStartHistory].Cancelled, 0) AS StartDateCancelled,
ISNULL([ReportEndHistory].Cancelled, 0) AS EndDateCancelled
FROM
Report AS [Report]
INNER JOIN
Account AS [Account]
LEFT JOIN
[ReportStartHistory]
ON [ReportStartHistory].ReportID = [Report].ReportID
AND [ReportStartHistory].AccountID = [Account].AccountID
LEFT JOIN
[ReportEndHistory]
ON [ReportEndHistory].ReportID = [Report].ReportID
AND [ReportEndHistory].AccountID = [Account].AccountID