T- Sql aggregate query - sql-server

can anyone help me figure out what I am doing wrong with this query.I am trying to filter some records from a table that contains emails sent out to clients with the status of the emails.I need to eliminate all EmailIds that has a status of Sent(1) and Bounced(0). Anything other than these two statuses are considered as Delivered(4). So the output contains only EmailId with a status of Delivered(4) for all those EmailIds that doesnt have statuses of 1 and 0.In the example below,I should see EmailId 4 too with a Status of Delivered
This is my sample set up.Really appreciate any help you guys can provide me with
create table #status
(
Id int,
Name varchar(100)
)
insert into #status (Id, Name)
values (0, 'Bounced'), (1, 'Sent'), (2, 'Clicked'),
(3, 'Opened'), (4, 'Delivered')
create table #email
(
EmailId int ,
Email varchar(100),
StatusId int
)
insert into #email (EmailId, email, StatusId)
values (1, 'rjoseph#gmail.com', 1), (1, 'rjoseph#gmail.com', 0),
(2, 'nathan#comcast.net', 1), (2, 'nathan#comcast.net', 2),
(2, 'nathan#comcast.net', 3), (3, 'nora#comcast.net', 1),
(3, 'nora#comcast.net', 2), (3, 'nora#comcast.net', 3),
(4, 'neha#comcast.net', 1)
select
e.EmailId
into
#temp
from
#email e
inner join #status st
on st.Id = e.StatusId
where
(e.StatusId not in (1,0))
group by
e.EmailId
drop table #temp
drop table #email
drop table #status

This is kind of a kludgy way to get to this (you can do this without the temporary tables, but I'm doing that here to follow your own syntax). The first query grabs the rows which match 1 AND 0. The second query returns the email IDs which do not exist in the first query:
SELECT EmailID
INTO #temp
FROM #email
WHERE StatusID = 0
AND EXISTS (SELECT 1 FROM #email WHERE StatusID = 1)
SELECT DISTINCT e.EmailID
FROM #email AS e LEFT JOIN #temp AS t
ON e.EmailID = t.EmailID
WHERE t.EmailID IS NULL
BTW: The SELECT 1 FROM ... does not have anything to do with the StatusID #1. It may seem confusing because I used SELECT 1, but it could have been SELECT 5 or SELECT 'Z'. It's mostly meaningless.
Here's the same query without the temporary table:
SELECT DISTINCT e.EmailID
FROM #email AS e
WHERE e.EmailID NOT IN (
SELECT EmailID
FROM #email
WHERE StatusID = 0
AND EXISTS (SELECT 1 FROM #email WHERE StatusID = 1)
)

Related

Ignoring rows within daterange

I have the following data:
CREATE TABLE SampleData
(
orderid int,
[name] nvarchar(1),
[date] date
);
INSERT INTO SampleData
VALUES
(1, 'a', '2017-01-01'),
(2, 'a', '2017-01-05'),
(3, 'a', '2017-02-01'),
(4, 'a', '2017-04-01'),
(5, 'a', '2017-10-01'),
(6, 'b', '2017-04-01');
I need to retrieve each new order according to the following rules:
The first date for a name is the 'current order' for that name
Orders with the same name, but less than 3 months difference with the 'current order' is considered the same order and needs to be ignored
3 months or more difference with the 'current' order is considered a new order and is now the 'current order' (in the SampleData orderid 1 and 4 need to be compared instead of 3 and 4, because 3 is not the current order)
If the name and date are the same, then the row with the lowest orderid is the superior order
So with the sample data I need the following result:
id name, date
1 a 2017-01-01
4 a 2017-04-01
5 a 2017-10-01
6 b 2017-04-01
I tried several approaches, but without success. Any idea's on how I can achieve this?
Below is a quick fix solution that can be built upon if your code scales beyond the sample data provided. I will state beforehand that this isn't the prettiest solution but it does return the result set you indicated you were after.
If anything, you may want to consider looking into T-SQL Window Functions as well as Analytic Functions. I will advice that that they don't play well with all datatypes.
My goal with the solution below was to rank the rows while partitioning by name and order by the date field. Thus you have something similar to your order id but the rank is specific to the customer who placed the order.
I'll do my best to answer any questions:
if object_id('tempdb..#tmp_SampleData','u') is not null
drop table #tmp_SampleData
CREATE TABLE #tmp_SampleData
(
orderid int,
[name] nvarchar(1),
[date] date
);
INSERT INTO #tmp_SampleData
VALUES
(1, 'a', '2017-01-01'),
(2, 'a', '2017-01-05'),
(3, 'a', '2017-02-01'),
(4, 'a', '2017-04-01'),
(5, 'a', '2017-10-01'),
(6, 'b', '2017-04-01');
if object_id('tempdb..#tmp_iter','u') is not null
drop table #tmp_iter
select
orderid
,name
,date
,rank() over (partition by name order by date) [Rank]
,lag(orderid,1,0) over (partition by name order by date) [LagRank]
--,rank() over (partition by name order by date desc) [ReverseRank]
into #tmp_Iter
from #tmp_SampleData
if object_id('tempdb..#tmp_final','u') is not null
drop table #tmp_final
select
i.orderid
,i.name
,i.date
,datediff(month,i.date,i2.date) [MonthsPassed]
into #tmp_final
from #tmp_Iter i
left join #tmp_Iter i2
on i.Rank = i2.LagRank
select *
from #tmp_final
where 1=1
and MonthsPassed > 3
or MonthsPassed = 0
or MonthsPassed < 0
or MonthsPassed is null
#SQLUser44, thanks for your input. Unfortunately your code is not working. The result for the table below should be orderid's 1,6,7,8 and 9. Yours results in 1,2,3,5,6,7,8 and 9.
INSERT INTO #tmp_SampleData
VALUES
(1,'a','2017-01-01'),
(2,'a','2017-01-08'),
(3,'a','2017-05-01'),
(4,'a','2017-01-05'),
(5,'a','2017-02-01'),
(6,'b','2017-01-01'),
(7,'b','2017-09-01'),
(8,'c','2017-10-01'),
(9,'a','2017-04-01');
I came up with the following that works, but I think it will lack performance...
if object_id('tempdb..#tmp_SampleData','u') is not null
drop table #tmp_SampleData
CREATE TABLE #tmp_SampleData
(
orderid int,
[name] nvarchar(1),
[date] date
);
INSERT INTO #tmp_SampleData
VALUES
(1,'a','2017-01-01'),
(2,'a','2017-01-08'),
(3,'a','2017-05-01'),
(4,'a','2017-01-05'),
(5,'a','2017-02-01'),
(6,'b','2017-01-01'),
(7,'b','2017-09-01'),
(8,'c','2017-10-01'),
(9,'a','2017-04-01');
DECLARE Test_Cursor CURSOR FOR
SELECT * FROM #tmp_SampleData ORDER BY [name], [date];
OPEN Test_Cursor;
DECLARE #orderid int;
DECLARE #name nvarchar(255);
DECLARE #date date;
FETCH NEXT FROM Test_Cursor INTO #orderid, #name, #date;
DECLARE #current_date date = #date;
DECLARE #current_name nvarchar(255) = #name;
DECLARE #listOfIDs TABLE (orderid int);
INSERT #listOfIDs values(#orderid);
WHILE ##FETCH_STATUS = 0
BEGIN
IF(#name = #current_name AND DATEDIFF(MONTH, #current_date, #date) >= 3)
BEGIN
SET #current_date = #date
INSERT #listOfIDs values(#orderid)
END
IF(#name != #current_name)
BEGIN
SET #current_name = #name
SET #current_date = #date
INSERT #listOfIDs values(#orderid)
END
FETCH NEXT FROM Test_Cursor INTO #orderid, #name, #date;
END;
CLOSE Test_Cursor;
DEALLOCATE Test_Cursor;
SELECT * FROM #tmp_SampleData WHERE orderid IN (SELECT orderid FROM #listOfIDs);
Better performing alternatives are very welcome!

How to get id's of parent ids for inserting children

I have Parent and Child table.
The goal is to duplicate the records, except with new primary keys.
Original Tables
Parent(id)
1
Child(id,parentId, data)
1,1
2,1
After insert:
Parent
1
2
Child
1,1
2,1
3,2
4,2
How do I do that? The part I am having trouble with is getting the new parent key for use with the child records.
This is what I have come up with so far.
--DECLARE VARS
declare #currentMetadataDocumentSetId int = 1, --Ohio
#newMetadataDocumentSetid int = 3; --PA
--CLEANUP
IF OBJECT_ID('tempdb..#tempFileRowMap') IS NOT NULL
/*Then it exists*/
DROP TABLE #tempFileRowMap
--Remove existing file row maps.
delete from file_row_map where metadata_document_set_id = #newMetadataDocumentSetid;
--Create a temptable to hold data to be copied.
Select [edi_document_code],
[functional_group],
[description],
3 as [metadata_document_set_id],
[document_name],
[incoming_file_row_subtype],
[metadata_document_id],
[document_subcode],
[outgoing_file_row_subtype],
[asi_type_code],
[asi_action_code],
[metadata_document_set],
file_row_map_id as orig_file_row_map_id
into #tempFileRowMap
from file_row_map fileRowMap
where metadata_document_set_id = #currentMetadataDocumentSetId;
--Select * from #tempFileRowMap;
Insert into file_row_map select
[edi_document_code],
[functional_group],
[description],
[metadata_document_set_id],
[document_name],
[incoming_file_row_subtype],
[metadata_document_id],
[document_subcode],
[outgoing_file_row_subtype],
[asi_type_code],
[asi_action_code],
[metadata_document_set]
from #tempFileRowMap
--Show Results
Select * from file_row_map fileRowMap where fileRowMap.metadata_document_set_id = #newMetadataDocumentSetid
--Update Detail
Select
[file_row_map_id],
[file_row_column],
[element_code],
[element_metadata_id],
[col_description],
[example],
[translate],
[is_used],
[is_mapped],
[page_num],
[subcode],
[qualifier],
[loop_code],
[loop_subcode],
[default_value],
[delete_flag]
into #tempFileRowMapDetail
from [dbo].[file_row_map_detail] d
left join #tempFileRowMap m
on m.orig_file_row_map_id = d.file_row_map_id
select * from #tempFileRowMapDetail
Simply use OUTPUT clause for getting exact Parent Table Primary Key values.
Lets build Example Schema for your case
--For Capturing inserted ID
CREATE TABLE #ID_CAPTURE (PARENT_ID INT,ORDER_NME VARCHAR(20));
--Your Intermidiate Data To insert into Actual Tables
CREATE TABLE #DUMMY_TABLE (ORDER_NME VARCHAR(20), ITEM_NME VARCHAR(20));
--Actual Tables
CREATE TABLE #ORDER_PARENT (ORDER_ID INT IDENTITY,ORDER_NME VARCHAR(20))
CREATE TABLE #ORDER_CHILD (CHILD_ID INT IDENTITY ,ORDER_ID INT, ORDER_NME VARCHAR(20))
INSERT INTO #DUMMY_TABLE
SELECT 'BILL1','Oil'
UNION ALL
SELECT 'BILL1', 'Gas'
UNION ALL
SELECT 'BILL2', 'Diesel'
Now do Inserts in Parent & Child Tables
INSERT INTO #ORDER_PARENT
OUTPUT inserted.ORDER_ID, inserted.ORDER_NME into #ID_CAPTURE
SELECT DISTINCT ORDER_NME FROM #DUMMY_TABLE
INSERT INTO #ORDER_CHILD
SELECT C.PARENT_ID, ITEM_NME FROM #DUMMY_TABLE D
INNER JOIN #ID_CAPTURE C ON D.ORDER_NME = C.ORDER_NME
SELECT * FROM #ID_CAPTURE
SELECT * FROM #ORDER_CHILD
There are other ways to get Inserted Identity values.
See documentation ##IDENTITY (Transact-SQL) , SCOPE_IDENTITY
Try following approach:
DECLARE #Table1 TABLE (
ID INT NOT NULL PRIMARY KEY,
ParentID INT NULL, -- FK
[Desc] VARCHAR(50) NOT NULL
);
INSERT #Table1 (ID, ParentID, [Desc])
VALUES
(1, NULL, 'A'),
(2, 1, 'AA.1'),
(3, 1, 'AA.2'),
(4, NULL, 'B'),
(5, 4, 'BB.1'),
(6, 4, 'BB.2'),
(7, 4, 'BB.3'),
(8, 7, 'BBB.1');
DECLARE #ParentID INT = 4;
DECLARE #LastID INT = (SELECT TOP(1) ID FROM #Table1 x ORDER BY x.ID DESC)
IF #LastID IS NULL
BEGIN
RAISERROR('Invalid call', 16, 1)
--RETURN ?
END
SELECT #LastID AS LastID;
/*
LastID
-----------
8
*/
DECLARE #RemapIDs TABLE (
OldID INT NOT NULL PRIMARY KEY,
[NewID] INT NOT NULL UNIQUE
);
WITH CteRecursion
AS (
SELECT 1 AS Lvl, crt.ID, crt.ParentID --, crt.[Desc]
FROM #Table1 crt
WHERE crt.ID = #ParentID
UNION ALL
SELECT cld.Lvl + 1 AS Lvl, crt.ID, crt.ParentID --, crt.[Desc]
FROM #Table1 crt
JOIN CteRecursion cld ON crt.ParentID = cld.ID
)
INSERT #RemapIDs (OldID, [NewID])
SELECT r.ID, #LastID + ROW_NUMBER() OVER(ORDER BY r.Lvl) AS [NewID]
FROM CteRecursion r;
--INSERT #Table1 (ID, ParentID, [Desc])
SELECT nc.[NewID] AS ID, np.[NewID] AS ParentID, o.[Desc]
FROM #Table1 o -- old
JOIN #RemapIDs nc /*new child ID*/ ON o.ID = nc.OldID
LEFT JOIN #RemapIDs np /*new parent ID*/ ON o.ParentID = np.OldID
/*
ID ParentID Desc
----------- ----------- --------------------------------------------------
9 NULL B
10 9 BB.1
11 9 BB.2
12 9 BB.3
13 12 BBB.1
*/
Note: with some minor changes should work w. many ParentIDs values.

SQL or Tableau Replace values with existing one with reference to another field

My problem with the data is
Id Name
--------
1 a
2 b
1 a
1 a
1 NULL
2 b
2 b
2 NULL
2 b
2 NULL
ID is unique to the name
I would like the NULL values to be replaced with existing Records.
Or if there are any other alternatives
I have about 2000 IDs so it needs to be automated.
The below query can use to replace the NULLs with its Id's Name, since Id is unique to the Name.
UPDATE T1 SET T1.Name = T2.ValidName
FROM TestTable T1
JOIN ( SELECT Id, MAX(Name) AS ValidName
FROM TestTable
WHERE Name IS NOT NULL
GROUP BY Id ) T2 ON T2.Id = T1.Id
WHERE T1.Name IS NULL
Sample execution in SQL-Server:
DECLARE #TestTable TABLE (Id INT, Name VARCHAR (20));
INSERT INTO #TestTable (Id, Name) VALUES
(1, 'a'),
(2, 'b'),
(1, 'a'),
(1, 'a'),
(1, NULL),
(2, 'b'),
(2, 'b'),
(2, NULL),
(2, 'b'),
(2, NULL);
UPDATE T1 SET T1.Name = T2.ValidName
FROM #TestTable T1
JOIN ( SELECT Id, MAX(Name) AS ValidName
FROM #TestTable
WHERE Name IS NOT NULL
GROUP BY Id ) T2 ON T2.Id = T1.Id
WHERE T1.Name IS NULL
SELECT * FROM #TestTable
try this: replace with your tablename
declare #id int, #name varchar(10)
declare c cursor for
select distinct id,name from <table> where id is not null and name is not null
open c
fetch next from c into #id,#name
while ##FETCH_STATUS=0
begin
update <table> set name=#name where id=#id
fetch next from c into #id,#name
end
close c
deallocate c

SQLServer : Grouping and Replacing a COLUMN value with the DATA from other table, without UDF

I would like to replace the numbers in #CommentsTable column "Comments" with the equivalent text from #ModTable table, without using UDF in a single SELECT. May with a CTE. Tried STUFF with REPLACE, but no luck.
Any suggestions would be a great help!
Sample:
DECLARE #ModTable TABLE
(
ID INT,
ModName VARCHAR(10),
ModPos VARCHAR(10)
)
DECLARE #CommentsTable TABLE
(
ID INT,
Comments VARCHAR(100)
)
INSERT INTO #CommentsTable
VALUES (1, 'MyFirst 5 Comments with 6'),
(2, 'MySecond comments'),
(3, 'MyThird comments 5')
INSERT INTO #ModTABLE
VALUES (1, '[FIVE]', '5'),
(1, '[SIX]', '6'),
(1, '[ONE]', '1'),
(1, '[TWO]', '2')
SELECT T1.ID, <<REPLACED COMMENTS>>
FROM #CommentsTable T1
GROUP BY T1.ID, T1.Comments
**Expected Result:**
ID Comments
1 MyFirst [FIVE] Comments with [SIX]
2 MySecond comments
3 MyThird comments [FIVE]
Create a cursor, span over the #ModTable and do each replacement a time
DECLARE replcursor FOR SELECT ModPos, ModName FROM #ModTable;
OPEN replcursor;
DECLARE modpos varchar(100) DEFAULT "";
DECLARE modname varchar(100) DEFAULT "";
get_loop: LOOP
FETCH replcursor INTO #modpos, #modname
SELECT T1.ID, REPLACE(T1.Comments, #modpos, #modname)
FROM #CommentsTable T1
GROUP BY T1.ID, T1.Comments
END LOOP get_loop;
Of course, you can store the results in a temp table and get the results altogether in the end of loop.
You can use a while loop to iterate over the records and the mods. I slightly modified your #ModTable to have unique values for ID. If this is not your data structure, then you can use a window function like ROW_NUMBER() to get a unique value over which you can iterate.
Revised script example:
DECLARE #ModTable TABLE
(
ID INT,
ModName VARCHAR(10),
ModPos VARCHAR(10)
)
DECLARE #CommentsTable TABLE
(
ID INT,
Comments VARCHAR(100)
)
INSERT INTO #CommentsTable
VALUES (1, 'MyFirst 5 Comments with 6'),
(2, 'MySecond comments'),
(3, 'MyThird comments 5')
INSERT INTO #ModTABLE
VALUES (1, '[FIVE]', '5'),
(2, '[SIX]', '6'),
(3, '[ONE]', '1'),
(4, '[TWO]', '2')
declare #revisedTable table (id int, comments varchar(100))
declare #modcount int = (select count(*) from #ModTable)
declare #commentcount int = (select count(*) from #CommentsTable)
declare #currentcomment varchar(100) = ''
while #commentcount > 0
begin
set #modcount = (select count(*) from #ModTable)
set #currentcomment = (select Comments from #CommentsTable where ID = #commentcount)
while #modcount > 0
begin
set #currentcomment = REPLACE( #currentcomment,
(SELECT TOP 1 ModPos FROM #ModTable WHERE ID = #modcount),
(SELECT TOP 1 ModName FROM #ModTable WHERE ID = #modcount))
set #modcount = #modcount - 1
end
INSERT INTO #revisedTable (id, comments)
SELECT #commentcount, #currentcomment
set #commentcount = #commentcount - 1
end
SELECT *
FROM #revisedTable
order by id
I think the will work even though I generally avoid recursive queries. It assumes that you have consecutive ids though:
with Comments as
(
select ID, Comments, 0 as ConnectID
from #CommentsTable
union all
select ID, replace(c.Comments, m.ModPos, m.ModName), m.ConnectID
from Comments c inner join #ModTable m on m.ConnectID = c.ConnectID + 1
)
select * from Comments
where ConnectID = (select max(ID) from #ModTable)
=> CLR Function()
As I have lot of records in "CommentsTable" and the "ModTable" would have multiple ModName for each comments, finally decided to go with CLR Function. Thanks all of you for the suggestions and pointers.

T-SQL SELECT query to return combined result of multiple tables

I am wondering if it is possible and more efficient to do something that I am presently doing in code, to do in T-SQL instead.
I have a database with courses. Each course can have different offerings which are variations of the course at different locations and at different awards.
Here's my (simplified) database structure and some sample data:
CREATE TABLE tblCourse (CourseId int, CourseName varchar(50))
CREATE TABLE tblOffering (OfferingId int, CourseId int, LocationId int, AwardId int)
CREATE TABLE tblLocation (LocationId int, LocationName varchar(50))
CREATE TABLE tblAward (AwardId int, AwardName varchar(50))
INSERT INTO tblCourse VALUES (1, 'Course A')
INSERT INTO tblCourse VALUES (2, 'Course B')
INSERT INTO tblOffering VALUES (1, 1, 1, 1)
INSERT INTO tblOffering VALUES (2, 1, 2, 1)
INSERT INTO tblOffering VALUES (3, 1, 3, 1)
INSERT INTO tblOffering VALUES (4, 1, 1, 2)
INSERT INTO tblOffering VALUES (5, 2, 3, 1)
INSERT INTO tblLocation VALUES (1, 'Location A')
INSERT INTO tblLocation VALUES (2, 'Location B')
INSERT INTO tblLocation VALUES (3, 'Location C')
INSERT INTO tblAward VALUES (1, 'Award A')
INSERT INTO tblAward VALUES (2, 'Award B')
What I want to retrieve from SQL is a single row for each course/award combination. Each row would have columns for each location and whether a course of that CourseId/AwardId combination was available. There would be now rows for course/award combinations that have no offerings.
The required result, from the sample data, would be a recordset like this:
CourseId | CourseName | AwardId | AwardName | LocationA | LocationB | LocationC
---------+------------+---------+-----------+-----------+-----------+----------
1 | Course A | 1 | Award A | True | True | True
1 | Course A | 2 | Award B | True | NULL | NULL
2 | Course B | 1 | Award A | NULL | NULL | True
(NULL could also be False)
At present I am doing a simple SELECT statement with various JOINS which gives me multiple rows for each course/award combination, then I loop through all rows in my code and build the required result. However, I don't think this is so efficient as I also need to page results.
I think I could do this fairly easily in a stored procedure by creating a temporary table and a bunch of separate queries, but I don't think that would be too efficient. Wondering if there is a better way of doing it in T-SQL???
So to clarify, what I am looking for is a T-SQL query or stored procedure that will produce the above sample recordset, and which I could adapt paging to.
NB. I am using SQL Server 2008
For Dynamic columns:
DECLARE #COLUMNS VARCHAR(max)
,#query varchar(1024)
,#True varchar(6)
SELECT #COLUMNS =
COALESCE(
#Columns + ',[' + L.LocationName + ']',
'[' + L.LocationName +']'
)
FROM tblLocation L
SELECT #True = '''True'''
SELECT #QUERY = 'SELECT C.CourseName
,A.AwardName
, pvt.*
FROM (SELECT O.OfferingID AS OID
,O.AwardID AS AID
,O.CourseID AS CID
,L.LocationName AS LID
FROM tblOffering O Inner Join tblLocation L on L.LocationID = O.LocationID) AS S
PIVOT
(
count(oID) For LID IN (' +#COLUMNS+ ')
) As pvt
inner join tblCourse C on C.CourseID = CID
inner join tblAward A on A.AwardID = pvt.AID'
EXEC (#QUERY)
GO
This will produce a paginated version of your example results:
declare #tblCourse as table (CourseId int, CourseName varchar(50))
declare #tblOffering as table (OfferingId int, CourseId int, LocationId int, AwardId int)
declare #tblLocation as table (LocationId int, LocationName varchar(50))
declare #tblAward as table (AwardId int, AwardName varchar(50))
INSERT INTO #tblCourse VALUES (1, 'Course A')
INSERT INTO #tblCourse VALUES (2, 'Course B')
INSERT INTO #tblOffering VALUES (1, 1, 1, 1)
INSERT INTO #tblOffering VALUES (2, 1, 2, 1)
INSERT INTO #tblOffering VALUES (3, 1, 3, 1)
INSERT INTO #tblOffering VALUES (4, 1, 1, 2)
INSERT INTO #tblOffering VALUES (5, 2, 3, 1)
INSERT INTO #tblLocation VALUES (1, 'Location A')
INSERT INTO #tblLocation VALUES (2, 'Location B')
INSERT INTO #tblLocation VALUES (3, 'Location C')
INSERT INTO #tblAward VALUES (1, 'Award A')
INSERT INTO #tblAward VALUES (2, 'Award B') -- This had id 1 in your example.
-- Set the following parameters to control paging:
declare #PageSize as Int = 5
declare #PageNumber as Int = 1
; with CourseAwardSummary as (
select distinct C.CourseId, C.CourseName, A.AwardId, A.AwardName,
case when exists ( select 42 from #tblOffering where CourseId = C.CourseId and AwardId = A.AwardId and LocationId = 1 ) then 'True' end as LocationA,
case when exists ( select 42 from #tblOffering where CourseId = C.CourseId and AwardId = A.AwardId and LocationId = 2 ) then 'True' end as LocationB,
case when exists ( select 42 from #tblOffering where CourseId = C.CourseId and AwardId = A.AwardId and LocationId = 3 ) then 'True' end as LocationC
from #tblCourse as C inner join
#tblOffering as O on O.CourseId = C.CourseId inner join
#tblAward as A on A.AwardId = O.AwardId
),
CourseAwardSummaryRows as (
select *, Row_Number() over ( order by CourseName, AwardName ) as RowNumber
from CourseAwardSummary
)
select CourseId, CourseName, AwardId, AwardName, LocationA, LocationB, LocationC
from CourseAwardSummaryRows
where ( #PageNumber - 1 ) * #PageSize + 1 <= RowNumber and RowNumber <= #PageNumber * #PageSize
order by CourseName, AwardName
The following query does this by joining and aggregating the offering table, and then joining the result to the course and award tables:
select c.CourseId, c.CourseName, oa.AwardId, oa.AwardName,
oa.LocationA, oa.LocationB, oa.LocationC
from tblCourse c left outer join
(select o.CourseId, o.AwardId, a.awardName
max(case when LocationName = 'Location A' then 'true' end) as LocationA,
max(case when LocationName = 'Location B' then 'true' end) as LocationB,
max(case when LocationName = 'Location C' then 'true' end) as LocationC
from tblOffering o join
tblLocation l
on o.LocationId = l.LocationId join
tblAward a
on a.awardID = o.AwardId
group by o.CourseId, o.AwardId, a.awardName
) oa
on oa.CourseId = c.CourseId

Resources