SQL Server Pivot Multiple Tables - sql

We have the following tables:
Table 1: Student_Records
StudentID | CourseID | Period | Grade
12 6010 P1 90
23 6020 P1 80
12 6030 P2 ' ' Blank, there's no grade
15 6010 P1 70
12 6020 P1 80
15 6020 P1 90
Table 2: Course_Records
CourseID CourseDec Credits
6010 Math 3
6020 Biology 3
6030 English 3
Table 3: Student_Info
StudentID FirstName LastName ClassYear
12 Joe Smith 2013
15 Chak Li 2013
23 Pete Vo 2013
Result Desire:
ClassYear LastName FirstName StudentId Math Biology
2013 Smith Joe 12 90 80
2013 Li Chak 15 70 90
How can I achieve this result using the pivot command?

You can use PIVOT for this but it requires that you know which course descriptions you're interested in.
SELECT p.classyear,
p.lastname,
p.firstname,
p.studentid,
pvt.math,
pvt.biology
FROM (SELECT sr.grade,
si.classyear,
si.studentid,
si.firstname,
silastname
FROM student_info si
INNER JOIN student_records sr
ON si.studentid = sr.studentid
INNER JOIN course_records cr
ON sr.courseid = cr.courseid) p PIVOT ( AVG (grade) FOR
coursedec IN (
[Math], [Biology]) ) AS pvt
ORDER BY pvt.classyear;

Query out the numbers and courses with a join so you end up with
StudentID CourseDec Grade
1 Math 20
1 Woodwork 82
Pivot that you end up with
StudentID Math WoodWork
1 20 82
Then join Back to student to get First Name Alst Name etc.

https://data.stackexchange.com/stackoverflow/query/60493/http-stackoverflow-com-questions-9068600-sql-server-pivot-mulitple-tables
DECLARE #Student_Records AS TABLE (
studentid INT,
courseid INT,
period VARCHAR(2),
grade INT);
INSERT INTO #Student_Records
VALUES (12,
6010,
'P1',
90),
(23,
6020,
'P1',
80),
(12,
6030,
'P2',
NULL),
(15,
6010,
'P1',
70),
(12,
6020,
'P1',
80),
(15,
6020,
'P1',
90);
DECLARE #Course_Records AS TABLE (
courseid INT,
coursedec VARCHAR(50),
credits INT);
INSERT INTO #Course_Records
VALUES ( 6010,
'Math',
3),
( 6020,
'Biology',
3),
( 6030,
'English',
3);
DECLARE #Student_Info AS TABLE (
studentid INT,
firstname VARCHAR(50),
lastname VARCHAR(50),
classyear INT);
INSERT INTO #Student_Info
VALUES (12,
'Joe',
'Smith',
2013),
(15,
'Chak',
'Li',
2013),
(23,
'Pete',
'Vo',
2013);
SELECT DISTINCT coursedec
FROM #Course_Records AS cr
INNER JOIN #Student_Records sr
ON sr.courseid = cr.courseid
WHERE sr.grade IS NOT NULL;
SELECT classyear,
lastname,
firstname,
summary.studentid,
summary.math,
summary.biology
FROM (SELECT *
FROM (SELECT si.studentid,
coursedec,
grade
FROM #Course_Records AS cr
INNER JOIN #Student_Records sr
ON sr.courseid = cr.courseid
INNER JOIN #Student_Info si
ON si.studentid = sr.studentid
WHERE sr.grade IS NOT NULL) AS results PIVOT (AVG(grade) FOR
coursedec
IN (
[Math], [Biology])) AS pvt) AS summary
INNER JOIN #Student_Info si
ON summary.studentid = si.studentid
Note that you can use dynamic HTML to make the query adjust as more courses are added:
Pivot Table and Concatenate Columns

Related

How to aggregate and count avg value by groups from 2 tables in sql?

I have 2 tables , department and employee. I need to get department name and average age of it.
create table Dep(name_dep char, id_dep int);
insert into Dep values("econ", 1);
insert into Dep values("credit", 2);
insert into Dep values("energy", 3);
insert into Dep values("retail", 4);
insert into Dep values("manufactury", 5);
create table Emp(id_emp int, id_dep int, age int, person_name char, salary int );
insert into Emp values(1, 1, 23, 'john', 200);
insert into Emp values(3, 2, 3, 'dalbai', 100);
insert into Emp values(6, 3, 53, 'borat', 300);
insert into Emp values(7, 1, 63, 'erjan', 1600);
insert into Emp values(9, 2, 73, 'sergey', 1000);
insert into Emp values(8, 5, 83, 'lucy', 20);
insert into Emp values(90, 4, 93, 'mike', 1200);
How to select dept name and avg age of employees in that dept?
SELECT name_dep, average_age
FROM Emp e
INNER JOIN
(
SELECT name_dep, AVG(age) AS average_age
FROM Dep d
GROUP BY id_dep
) d
ON e.id_dep = d.id_dep
YOu can try below -
DEMO
select name_dep,avg(age)
from emp a inner join dep b on a.id_dep=b.id_dep
group by a.id_dep,name_dep
Join the tables, group by id_dep, name_dep and then get the average age:
select
d.name_dep, avg(age) average_age
from Dep d inner join Emp e
on e.id_dep = d.id_dep
group by d.id_dep, d.name_dep
See the demo.
Results:
| name_dep | average_age |
| ----------- | ----------- |
| econ | 43 |
| credit | 38 |
| energy | 53 |
| retail | 93 |
| manufactury | 83 |
you can do something like
SELECT
d.name_dep, AVG(age) as avg_age
FROM
Emp e, Dep d
WHERE
e.id_dep = d.id_dep
GROUP BY
d.name_dep
SELECT d.name_dep,AVG(e.age)
FROM Dep d JOIN Emp e ON d.id_dep = e.id_dep
GROUP BY d.name_dep;

Get all subordinates based on line manager marker

I have an employees table that holds an employeeID, Name...etc.... and intLineManager. intLine manager is the employeeID of your line manager. Something like this, with Dave being the top boss with no manager.
intEmpID |Name|intLineManager
1 |Dave| NULL
2 |Sue |1
3 |Mike|1
4 |Matt|2
5 |Sara|3
6 |Paul|4
I'm looking for an SQL script that will return all employees that are underneath a person.
So a search on Mike will return Sara, a search on Sue will return Matt + Paul and finally a search on Dave will return everyone.
You can use a recursive CTE like so:
CREATE TABLE #Employees(EmpID int, Name varchar(10), LineManager int NULL)
INSERT INTO #Employees VALUES
(1, 'Dave', NULL),
(2, 'Sue', 1),
(3, 'Mike', 1),
(4, 'Matt', 2),
(5, 'Sara', 3),
(6, 'Paul', 4)
;WITH CTE as
(
SELECT EmpID, Name, LineManager
FROM #Employees
WHERE EmpID = 2 --Sue
UNION ALL
SELECT e.EmpID, e.Name, e.LineManager
FROM #Employees AS E
INNER JOIN CTE AS C ON c.EmpID = e.LineManager
)
SELECT *
FROM CTE
where EmpID > (SELECT TOP 1 EmpID FROM CTE)
ORDER BY EmpID
Result for sue(EmpID 2)
EmpID Name LineManager
----------- ---------- -----------
4 Matt 2
6 Paul 4

SQL Server: matching two tables and results in the order of proper column

I have two tables JOB and EMP; structure and values are like this:
CREATE TABLE JOB
(
JOBID SMALLINT UNIQUE NOT NULL,
JOBNAME CHAR(15)
);
CREATE TABLE EMP
(
EMPID SMALLINT,
JOBID SMALLINT,
SAL SMALLINT,
CITYID SMALLINT,
YEAR SMALLINT,
STATUS CHAR(1)
);
INSERT INTO JOB(JOBID, JOBNAME)
VALUES (1, 'DEVELOPMENT'),
(2, 'DEVELOPMENT'),
(3, 'TESTING'),
(4, 'TESTING'),
(7, 'TESTING'),
(9, 'RESEARCH'),
(8, 'HR');
INSERT INTO EMP (EMPID , JOBID, SAL, CITYID, YEAR, STATUS)
VALUES (100, 1, 1000, 10, 2015, 'A'),
(200, 2, 2000, 10, 2015, 'A'),
(300, 1, 2500, 20, 2015, 'A'),
(400, 3, 1000, 10, 2016, 'A'),
(500, 6, 3000, 10, 2015, 'E'),
(600, 8, 1000, 30, 2015, 'A'),
(700, 8, 2000, 10, 2015, 'E'),
(800, 9, 1500, 10, 2015, 'A');
I want to display all jobname count and avg salaries; for the jobname if jobid's not exists then display 0
For the given input cityid , YEAR and STATUS ( Emp table), take all jobid for each jobname (from job table ) and match in Emp table, if exists display count (count of jobid present in Emp table) and avgsal else 0 for count and avgsal. And Sal is calculated based on Status. If Status is 'A' Sal goes to Status-A-Sal else Status-E-Sal. And for each matched i.e non zero record put 'X' in another field
Output should be like this for Cityid's 10 and 20 year 2015. Results should be displayed first for Status 'A' and then Status 'E'. Added status type field in result .
Cityid Status-type jobname count STATUS sal
--------------------------------------------------------------
10 STATUSA development 2 X 1500
10 STATUSA TESTING 0 0
10 STATUSA RESEARCH 1 X 1500
10 STATUSA HR 0 0
10 total 3 0
10 STATUSE development 0 0
10 STATUSE TESTING 0 0
10 STATUSE RESEARCH 0 0
10 STATUSE HR 1 X 2000
10 total 1 2000
20 STATUSA development 1 X 2500
20 STATUSA TESTING 0 0
20 STATUSA RESEARCH 0 0
20 STATUSA HR 0 0
20 total 1 2500
20 STATUSE development 0 0
20 STATUSE TESTING 0 0
20 STATUSE RESEARCH 0 0
20 STATUSE HR 0 0
20 total 0 0
How to bring the results one status after another ?
I've tried like this but its throwing
SELECT C.CITYID AS CITYID,
CASE WHEN P.STATUS ='A' THEN 'STATUSA' ELSE 'STATUSE' END AS STATUS_TYPE ,
COALESCE(J.JOBNAME, 'TOTAL') AS JOBNAME,
COUNT(CASE WHEN P.STATUS ='A' THEN P.CITYID END ) AS COUNT ,
COALESCE(AVG(CAST(CASE WHEN P.STATUS = 'A' THEN P.SAL END AS DECIMAL(13,2)))/12 , 0) AS "AVG SAL",
COUNT(CASE WHEN P.STATUS ='E' THEN P.CITYID END ) AS COUNT ,
COALESCE(AVG(CAST(CASE WHEN P.STATUS = 'E' THEN P.SAL END AS DECIMAL(13,2)))/12 , 0) AS "AVG SAL"
FROM JOB1 J
CROSS JOIN
(SELECT DISTINCT CITYID
FROM EMP1 B WHERE CITYID = 10
) C
LEFT JOIN EMP1 P ON P.JOBID = J.JOBID
AND P.CITYID = C.CITYID and
YEAR = 2015
GROUP BY ROLLUP(C.CITYID, J.JOBNAME );
ERROR:
Column 'EMP1.STATUS' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
IF OBJECT_ID('tempdb..#JOB') IS NOT NULL
DROP TABLE #JOB
CREATE TABLE #JOB
(
JOBID SMALLINT UNIQUE NOT NULL,
JOBNAME CHAR(15)
);
IF OBJECT_ID('tempdb..#EMP') IS NOT NULL
DROP TABLE #EMP
CREATE TABLE #EMP
(
EMPID SMALLINT,
JOBID SMALLINT,
SAL SMALLINT,
CITYID SMALLINT,
YEAR SMALLINT,
STATUS CHAR(1)
);
INSERT INTO #JOB(JOBID, JOBNAME)
VALUES (1, 'DEVELOPMENT'),
(2, 'DEVELOPMENT'),
(3, 'TESTING'),
(4, 'TESTING'),
(7, 'TESTING'),
(9, 'RESEARCH'),
(8, 'HR');
INSERT INTO #EMP (EMPID , JOBID, SAL, CITYID, YEAR, STATUS)
VALUES (100, 1, 1000, 10, 2015, 'A'),
(200, 2, 2000, 10, 2015, 'A'),
(300, 1, 2500, 20, 2015, 'A'),
(400, 3, 1000, 10, 2016, 'A'),
(500, 6, 3000, 10, 2015, 'E'),
(600, 8, 1000, 30, 2015, 'A'),
(700, 8, 2000, 10, 2015, 'E'),
(800, 9, 1500, 10, 2015, 'A');
;with cteJobDict as (
select
distinct
j.JOBNAME
from
#JOB j
)
,cteStatusDict as(
select
distinct STATUS
from
#EMP e
),cteCityDict as (
select
distinct CITYID
from
#EMP
)
,cteJobStatusCityMatrix as(
select
*
from
cteJobDict
cross apply cteStatusDict
cross apply cteCityDict
)
,cteEmpWithJobName as (
select
e.*
,j.JOBNAME
from
#EMP e
join #JOB j on j.JOBID=e.JOBID
), cteData as (
SELECT
m.CITYID
,CASE WHEN m.STATUS ='A' THEN 'STATUSA' ELSE 'STATUSE' end as [Status-type]
,CASE WHEN m.STATUS ='A' THEN 1 ELSE 3 end as [Status-order]
,m.JOBNAME
,count(distinct e.EMPID) count
,iif(count(distinct e.EMPID)>0,'X','') status
,isnull(avg(e.sal),0) sal
FROM
cteJobStatusCityMatrix m
left join cteEmpWithJobName e on e.CITYID=m.CITYID and e.STATUS=m.STATUS and e.JOBNAME=m.JOBNAME and e.YEAR=2015
where
m.CITYID in (10,20)
group by
m.CITYID
,m.STATUS
,m.JOBNAME
union
SELECT
m.CITYID
,'total' as [Status-type]
,CASE WHEN m.STATUS ='A' THEN 2 ELSE 4 end as [Status-order]
,null
,count(distinct e.EMPID) count
,iif(count(distinct e.EMPID)>0,'X','') status
,isnull(avg(e.sal),0) sal
FROM
cteJobStatusCityMatrix m
left join cteEmpWithJobName e
on e.CITYID=m.CITYID
and e.STATUS=m.STATUS
and e.JOBNAME=m.JOBNAME
and e.YEAR=2015 -- here goes year
where
m.CITYID in (10,20) -- here goes cityid
group by
m.CITYID
,m.STATUS)
select
CITYID
,[Status-type]
,JOBNAME
,count
,status
,sal
from
cteData
order by
CITYID
,[Status-order]

how to combine the result of one column to another

I am trying to create a student database but i am stuck up the given requirement.
I was given to create 2 tables one with students register number , subjects ans total and another table with student_name , total, rank. i can understand that here we need to use the student name separately and combine it later but the part that got me struck in the student subjects may vary means if student no :1 is having 3 subjects means student no : 2 may have 2 and student no: 3 may have 5 and based on this we have to put ranking order
CREATE TABLE Student
(StudentID int, StudentName varchar(6), Details varchar(1));
INSERT INTO Student
(StudentID, StudentName, Details)
VALUES
(1, 'John', 'X'),
(2, 'Paul', 'X'),
(3, 'George', 'X'),
(4, 'Paul', 'X');
CREATE TABLE Subject
(SubjectID varchar(1), SubjectName varchar(7));
INSERT INTO Subject
(SubjectID, SubjectName)
VALUES
('M', 'Math'),
('E', 'English'),
('H', 'History');
CREATE TABLE Mark
(StudentID int, SubjectID varchar(1), MarkRate int);
INSERT INTO Mark
(StudentID, SubjectID, MarkRate)
VALUES
(1, 'M', 90),
(1, 'E', 100),
(2, 'M', 95),
(2, 'E', 70),
(3, 'E', 95),
(3, 'H', 98),
(4, 'H', 90),
(4, 'E', 100);
I need 2 outputs
1st one is
ID |subjects |marks
----------------------------------------------------------
1 maths 98
1 science 87
1 social 88
2 maths 87
2 english 99
3 maths 96
3 evs 100
3 social 88
3 history 90
and the second table as
NO |name |total|rank
----------------------------------------------------------
1 xxx 123 1
2 yyy 456 2
3 zzz 789 3
I need output like this for n number of entries
1 st one
SELECT
A.StudentID AS [ID],
C.SubjectName AS [subjects],
B.MarkRate AS [marks]
FROM STUDENT A JOIN Mark B ON A.StudentID=B.StudentID JOIN Subject C ON C.SubjectID=B.SubjectID
2nd one
SELECT
A.StudentID AS [NO],
A.StudentName AS [name],
SUM(B.MarkRate) AS [total],
ROW_NUMBER() OVER(ORDER BY SUM(B.MarkRate) DESC) AS [rank]
FROM STUDENT A JOIN Mark B ON A.StudentID=B.StudentID JOIN Subject C ON C.SubjectID=B.SubjectID
GROUP BY A.StudentID,A.StudentName
ORDER BY [total] DESC
select StudentID ,SubjectName ,MarkRate from
#Mark a join #Subject b on a.SubjectID=b.SubjectID
output
StudentID SubjectName MarkRate
1 Math 90
1 English 100
2 Math 95
2 English 70
3 English 95
3 History 98
4 History 90
4 English 100
Second Query
with cte as
(
select a.StudentID,StudentName,sum(MarkRate)MarkRate from #Student a join #Mark B on a.StudentID=b.StudentID
group by a.StudentID,StudentName
)
select *,rank() over( order by MarkRate desc) as rn from cte
output
StudentID StudentName MarkRate rn
3 George 193 1
4 Paul 190 2
1 John 190 2
2 Paul 165 4
To get the Mark list with subject name is simply this :
you need to use JOIN :
SELECT M.StudentId
,SU.SubjectName
,M.MarkRate
FROM Mark M
INNER JOIN Subject SU ON M.SubjectID = SU.SubjectID
To get total marks with ranks you need to use GROUP BY and RANK() function :
SELECT M.StudentId
,ST.StudentName
,SUM(MarkRate) Total
,RANK() OVER(ORDER BY SUM(MarkRate) ) Rank
--,RANK() OVER(ORDER BY SUM(MarkRate) DESC) Rank
FROM Mark M
INNER JOIN Student ST ON M.StudentId = ST.StudentId
GROUP BY M.StudentId
,ST.StudentName
I understand that first query lists the marks of each student
SELECT
m.StudentID as ID,
s.SubjectName as subjects,
m.MarkRate as marks
FROM
Mark m
INNER JOIN Subject s on m.SubjectID = m.SubjectID
ORDER BY
m.StudentID,
s.SubjectName
Second query gives the total mark of Students with their rank.
SELECT
X.StudentID,
X.StudentName,
ROWNUMBER() OVER ( ORDER BY X.TotalMark desc) as Rank
FROM (
SELECT
m.StudentID,
s.StudentName,
sum(m.MarkRate) TotalMark
FROM
Mark m
INNER JOIN Student s on s.StudentID = m.StudentID
GROUP BY
m.StudentID,
s.StudentName
) X
ORDER BY X.TotalMark desc
You can try the following query for both output.
SELECT
Student.StudentID as ID,
[Subject].SubjectName as subjects,
Mark.MarkRate as marks
FROM
Student
INNER JOIN Mark on Student.StudentID = Mark.StudentID
INNER JOIN [Subject] on [Subject].SubjectID = Mark.SubjectID
ORDER BY
Student.StudentID,
SubjectName
Below query for second output. Here as you have said for those who has less mark got Rank 1 otherwise you can put Order By Total Desc in below query for higher marks rank 1.
SELECT ID, StudentName, Total, Row_number() Over (Order BY Total) Ranks FROM(
SELECT Id, StudentName, SUM(marks) as Total FROM (
SELECT
Student.StudentID as ID,
[Subject].SubjectName as subjects,
Mark.MarkRate as marks,
Student.StudentName
FROM
Student
INNER JOIN Mark on Student.StudentID = Mark.StudentID
INNER JOIN [Subject] on [Subject].SubjectID = Mark.SubjectID
)Tot
Group By Id, StudentName
)Ranks

SQL query to reconstruct inherited EAV model

I have 5 tables in my database representing an inherited EAV model:
CREATE TABLE AttributeNames
("ID" int, "Name" varchar(8))
;
INSERT INTO AttributeNames
("ID", "Name")
VALUES
(1, 'Color'),
(2, 'FuelType'),
(3, 'Doors'),
(4, 'Price')
;
CREATE TABLE MasterCars
("ID" int, "Name" varchar(10))
;
INSERT INTO MasterCars
("ID", "Name")
VALUES
(5, 'BMW'),
(6, 'Audi'),
(7, 'Ford')
;
CREATE TABLE MasterCarAttributes
("ID" int, "AttributeNameId" int, "Value" varchar(10), "MasterCarId" int)
;
INSERT INTO MasterCarAttributes
("ID", "AttributeNameId", "Value", "MasterCarId")
VALUES
(100, 1, 'Red', 5),
(101, 2, 'Gas', 5),
(102, 3, '4', 5),
(102, 4, '$100K', 5),
(103, 1, 'Blue', 6),
(104, 2, 'Diesel', 6),
(105, 3, '3', 6),
(106, 4, '$80k', 6),
(107, 1, 'Green', 7),
(108, 2, 'Diesel', 7),
(109, 3, '5', 7),
(110, 4, '$60k', 7)
;
CREATE TABLE LocalCars
("ID" int, "MasterCarId" int)
;
INSERT INTO LocalCars
("ID", "MasterCarId")
VALUES
(8, '5'),
(9, '6'),
(10, NULL)
;
CREATE TABLE LocalCarAttributes
("ID" int, "AttributeNameId" int, "Value" varchar(6), "LocalCarId" int)
;
INSERT INTO LocalCarAttributes
("ID", "AttributeNameId", "Value", "LocalCarId")
VALUES
(43, 1, 'Yellow', 8),
(44, 3, '6', 9),
(45, 1, 'Red', 10),
(46, 2, 'Gas', 10),
(47, 3, '2', 10),
(48, 4, '$60k', 10)
;
I can retrieve all of master car attributes as follows:
SELECT MC.ID, MCA.AttributeNameId, MCA.Value
FROM MasterCars MC
left join MasterCarAttributes MCA on MC.ID = MCA.MasterCarId
order by MC.ID;
Likewise, I can retrieve all of the local car attributes as follows:
SELECT LC.ID, LCA.AttributeNameId, LCA.Value
FROM LocalCars LC
left join LocalCarAttributes LCA on LC.ID = LCA.LocalCarId
order by LC.ID;
If LocalCars.MasterCarId is not NULL, then that local car can inherit the attributes of that master car. A local car attribute with the same AttributeNameId overrides any master attribute with the same AttributeNameId.
So given the data above, I have 3 local cars each with 4 attributes (color, fuelType, doors, price). Inherited attribute values in bold:
Local Car Id = 1 (Yellow, Gas, 4, $100K)
Local Car Id = 2 (Blue, Diesel, 6, $80k)
Local Car Id = 3 (Red, Gas, 2, $60k)
I'm trying to find the necessary joins required to join the two queries above together to give a complete set of local cars attributes, some inherited:
LocalCarId AttributeNameId Value
------------------------------------------
1 1 Yellow
1 2 Gas
1 3 4
1 4 $100K
2 1 Blue
2 2 Diesel
2 3 6
2 4 $80K
3 1 Red
3 2 Gas
3 3 2
3 4 $60K
or possibly even:
LocalCarId AttributeNameId LocalValue MasterValue
-------------------------------------------------------------
1 1 Yellow Red
1 2 NULL Gas
1 3 NULL 4
1 4 NULL $100K
2 1 NULL Blue
2 2 NULL Diesel
2 3 6 3
2 4 NULL $80K
3 1 Red NULL
3 2 Gas NULL
3 3 2 NULL
3 4 $60K NULL
The problem can be solved by performing a union on all of your local car attributes and master car attributes. Each record is marked with an [IsMasterAttribute] flag. The next step is then use the ROW_NUMBER() window function to rank each of the duplicate attributes. The final step is to only select attributes which has a rank of 1.
;WITH CTE_CombinedAttributes
AS
(
SELECT 1 AS IsMasterAttribute
,LC.ID
,MC.ID AS MasterCarId
,MCA.AttributeNameId
,MCA.Value
FROM MasterCars MC
LEFT OUTER JOIN MasterCarAttributes MCA on MC.ID = MCA.MasterCarId
INNER JOIN LocalCars LC ON LC.MasterCarId = MC.ID
UNION ALL
SELECT 0 AS IsMasterAttribute
,LC.ID
,LC.MasterCarId
,LCA.AttributeNameId
,LCA.Value
FROM LocalCars LC
LEFT OUTER JOIN LocalCarAttributes LCA on LC.ID = LCA.LocalCarId
)
,
CTE_RankedAttributes
AS
(
SELECT [IsMasterAttribute]
,[ID]
,[AttributeNameId]
,[Value]
,ROW_NUMBER() OVER (PARTITION BY [ID], [AttributeNameId] ORDER BY [IsMasterAttribute]) AS [AttributeRank]
FROM CTE_CombinedAttributes
)
SELECT [IsMasterAttribute]
,[ID]
,[AttributeNameId]
,[Value]
FROM CTE_RankedAttributes
WHERE [AttributeRank] = 1
ORDER BY [ID]
The second output is also possible by performing a simple pivot on the final result:
;WITH CTE_CombinedAttributes
AS
(
SELECT 1 AS IsMasterAttribute
,LC.ID
,MC.ID AS MasterCarId
,MCA.AttributeNameId
,MCA.Value
FROM MasterCars MC
LEFT OUTER JOIN MasterCarAttributes MCA on MC.ID = MCA.MasterCarId
INNER JOIN LocalCars LC ON LC.MasterCarId = MC.ID
UNION ALL
SELECT 0 AS IsMasterAttribute
,LC.ID
,LC.MasterCarId
,LCA.AttributeNameId
,LCA.Value
FROM LocalCars LC
LEFT OUTER JOIN LocalCarAttributes LCA on LC.ID = LCA.LocalCarId
)
,
CTE_RankedAttributes
AS
(
SELECT [IsMasterAttribute]
,[ID]
,[AttributeNameId]
,[Value]
,ROW_NUMBER() OVER (PARTITION BY [ID], [AttributeNameId] ORDER BY [IsMasterAttribute]) AS [AttributeRank]
FROM CTE_CombinedAttributes
)
SELECT [ID]
,[AttributeNameId]
,MAX(
CASE [IsMasterAttribute]
WHEN 0 THEN [Value]
END
) AS LocalValue
,MAX(
CASE [IsMasterAttribute]
WHEN 1 THEN [Value]
END
) AS MasterValue
FROM CTE_RankedAttributes
GROUP BY [ID], [AttributeNameId]
ORDER BY [ID]
SQL Fiddle Demo
SELECT LC."ID" as LocalCarID,
COALESCE(LCA."AttributeNameId", MCA."AttributeNameId") as "AttributeNameId",
COALESCE(LCA."Value", MCA."Value") as "Value"
FROM LocalCars LC
LEFT JOIN MasterCars MC
ON LC."MasterCarId" = MC."ID"
LEFT JOIN MasterCarAttributes MCA
ON MC."ID" = MCA."MasterCarId"
LEFT JOIN LocalCarAttributes LCA
ON ( MCA."AttributeNameId" = LCA."AttributeNameId"
OR MCA."AttributeNameId" IS NULL)
-- This is the important part
-- Try to join with a MasterAtribute otherwise use the Car Atribute.
AND LC."ID" = LCA."ID"
OUTPUT
| LocalCarID | AttributeNameId | Value |
|------------|-----------------|--------|
| 1 | 1 | Blue |
| 1 | 2 | Gas |
| 2 | 1 | Green |
| 2 | 2 | Diesel |