sql Pivot how can I do this - sql

I have 3 tables
------------------------
users
1 -> mark
2 -> adel
3 -> khali
4 -> piter
5 -> adam
------------------------
groups
1 -> group 1
2 -> group 2
3 -> group 3
4 -> group 4
----------------------
usersGroups
1 -> 4
3 -> 2
4 -> 3
1 -> 2
I want to display
if username has a group then 1 if it hasn't then 0
like this whith pivot but I dont know how ???
-- username group1 group2 group3 group4
----------------------------------------------------
-- mark 0 1 0 1
-- adel 0 1 0 0
-- adam 0 0 1 0
I try this please help me
SELECT username, [group1] AS 'group1', [group2] AS 'group2', [group3] AS 'group3', [group4] AS 'group4'
FROM
(
SELECT ug.groupid, ug.userid, g.description, u.username FROM users u
INNER JOIN usersgroups ug ON u.userid = ug.userid
INNER JOIN groups g ON ug.groupid = g.groupid
)AS q
PIVOT
(COUNT(groupid) FOR [description] IN ([group1],[group2],[group3],[group4])) AS pvt

Test Data
DECLARE #users TABLE (userid INT, username NVARCHAR(100))
INSERT INTO #users
VALUES (1,'mark'),(2,'adel'),(3,'khali'),(4,'piter'),(5,'adam')
DECLARE #groups TABLE (groupid INT, [description] NVARCHAR(100))
INSERT INTO #groups
VALUES
(1,'group 1'),(2,'group 2'),(3,'group 3'),(4,'group 4')
DECLARE #usersGroups TABLE (userid INT, groupid INT)
INSERT INTO #usersGroups
VALUES (1,4),(3,2),(4,3),(1,2)
Query
SELECT username
, CASE WHEN [group 1] IS NOT NULL THEN 1 ELSE 0 END AS 'group1'
, CASE WHEN [group 2] IS NOT NULL THEN 1 ELSE 0 END AS 'group2'
, CASE WHEN [group 3] IS NOT NULL THEN 1 ELSE 0 END AS 'group3'
, CASE WHEN [group 4] IS NOT NULL THEN 1 ELSE 0 END AS 'group4'
FROM
(
SELECT ug.groupid, ug.userid, g.description, u.username
FROM #users u INNER JOIN #usersGroups ug
ON u.userid = ug.userid
INNER JOIN #groups g
ON ug.groupid = g.groupid
)AS q
PIVOT
(MIN(groupid)
FOR [description]
IN ([group 1],[group 2],[group 3],[group 4])
) AS pvt
Result Set
╔══════════╦════════╦════════╦════════╦════════╗
║ username ║ group1 ║ group2 ║ group3 ║ group4 ║
╠══════════╬════════╬════════╬════════╬════════╣
║ mark ║ 0 ║ 1 ║ 0 ║ 1 ║
║ khali ║ 0 ║ 1 ║ 0 ║ 0 ║
║ piter ║ 0 ║ 0 ║ 1 ║ 0 ║
╚══════════╩════════╩════════╩════════╩════════╝

SELECT case when ug.groupid is null then 0 else 1 end as groupid,
ug.userid, g.description, u.username FROM users u
LEFT JOIN usersgroups ug ON u.userid = ug.userid
INNER JOIN groups g ON ug.groupid = g.groupid
and then change aggregate COUNT(groupid) to MAX(groupid)

Related

How can I Ascertain the structure for each person from a self referencing table

I have the following tables:
Employees
-------------
ClockNo int
CostCentre varchar
Department int
and
Departments
-------------
DepartmentCode int
CostCentreCode varchar
Parent int
Departments can have other departments as parents meaning there is infinite hierarchy. All departments belong to a cost centre and so will always have a CostCentreCode. If parent = 0 it is a top level department
Employees must have a CostCentre value but may have a Department of 0 meaning they are not in a department
What I want to try and generate is a query that will give the up to four levels of hierarchy. Like this:
EmployeesLevels
-----------------
ClockNo
CostCentre
DeptLevel1
DeptLevel2
DeptLevel3
DeptLevel4
I've managed to get something to display the department structure on it's own, but I can't work out how to link this to the employees without creating duplicate employee rows:
SELECT d1.Description AS lev1, d2.Description as lev2, d3.Description as lev3, d4.Description as lev4
FROM departments AS d1
LEFT JOIN departments AS d2 ON d2.parent = d1.departmentcode
LEFT JOIN departments AS d3 ON d3.parent = d2.departmentcode
LEFT JOIN departments AS d4 ON d4.parent = d3.departmentcode
WHERE d1.parent=0;
SQL To create Structure and some sample data:
CREATE TABLE Employees(
ClockNo integer NOT NULL PRIMARY KEY,
CostCentre varchar(20) NOT NULL,
Department integer NOT NULL);
CREATE TABLE Departments(
DepartmentCode integer NOT NULL PRIMARY KEY,
CostCentreCode varchar(20) NOT NULL,
Parent integer NOT NULL
);
CREATE INDEX idx0 ON Employees (ClockNo);
CREATE INDEX idx1 ON Employees (CostCentre, ClockNo);
CREATE INDEX idx2 ON Employees (CostCentre);
CREATE INDEX idx0 ON Departments (DepartmentCode);
CREATE INDEX idx1 ON Departments (CostCentreCode, DepartmentCode);
INSERT INTO Employees VALUES (1, 'AAA', 0);
INSERT INTO Employees VALUES (2, 'AAA', 3);
INSERT INTO Employees VALUES (3, 'BBB', 0);
INSERT INTO Employees VALUES (4, 'BBB', 4);
INSERT INTO Employees VALUES (5, 'CCC', 0);
INSERT INTO Employees VALUES (6, 'AAA', 1);
INSERT INTO Employees VALUES (7, 'AAA', 5);
INSERT INTO Employees VALUES (8, 'AAA', 15);
INSERT INTO Departments VALUES (1, 'AAA', 0);
INSERT INTO Departments VALUES (2, 'AAA', 1);
INSERT INTO Departments VALUES (3, 'AAA', 1);
INSERT INTO Departments VALUES (4, 'BBB', 0);
INSERT INTO Departments VALUES (5, 'AAA', 3);
INSERT INTO Departments VALUES (12, 'AAA', 5);
INSERT INTO Departments VALUES (15, 'AAA', 12);
This gives the following structure (employee clock numbers in square brackets):
Root
|
|---AAA [1]
| \---1 [6]
| |---2
| \---3 [2]
| \---5 [7]
| \---12
| \---15 [8]
|
|---BBB [3]
| \---4 [4]
|
\---CCC [5]
The query should return the following:
ClockNo CostCentre Level1 Level2 Level3 Level4
1 AAA
2 AAA 1 3
3 BBB
4 BBB 4
5 CCC
6 AAA 1
7 AAA 1 3 5
8 AAA 1 3 5 12 *
* In the case of Employee 8, they are in level5. Ideally I would like to show all their levels down to level4, but I am happy just to show the CostCentre in this case
When we join the tables we should stop further traversal of the path when we found proper department that belongs to the Employee at previous level.
Also we have exceptional case when Employee.Department=0. In this case we shouldn't join any of departments, because in this case Department is the Root.
We need to choose only those records which contains employee's Department at one of the levels.
In case if employee's department level is greater than 4 we should expand all 4 levels of departments and show them as is (even if can't reach the desired department level and didn't find it within expanded ones).
select e.ClockNo,
e.CostCentre,
d1.DepartmentCode as Level1,
d2.DepartmentCode as Level2,
d3.DepartmentCode as Level3,
d4.DepartmentCode as Level4
from Employees e
left join Departments d1
on e.CostCentre=d1.CostCentreCode
and d1.Parent=0
and ((d1.DepartmentCode = 0 and e.Department = 0) or e.Department <> 0)
left join Departments d2
on d2.parent=d1.DepartmentCode
and (d1.DepartMentCode != e.Department and e.Department<>0)
left join Departments d3
on d3.parent=d2.DepartmentCode
and (d2.DepartMentCode != e.Department and e.Department<>0)
left join Departments d4
on d4.parent=d3.DepartmentCode
and (d3.DepartMentCode != e.Department and e.Department<>0)
where e.Department=d1.DepartmentCode
or e.Department=d2.DepartmentCode
or e.Department=d3.DepartmentCode
or e.Department=d4.DepartmentCode
or e.Department=0
or (
(d1.DepartmentCode is not null) and
(d2.DepartmentCode is not null) and
(d3.DepartmentCode is not null) and
(d4.DepartmentCode is not null)
)
order by e.ClockNo;
SELECT [ClockNo]
, [CostCentre]
, CASE
WHEN Department <> 0 THEN dept.[Level1]
END AS [Level1]
, CASE
WHEN Department <> 0 THEN dept.[Level2]
END AS [Level2]
, CASE
WHEN Department <> 0 THEN dept.[Level3]
END AS [Level3]
, CASE
WHEN Department <> 0 THEN dept.[Level4]
END AS [Level4]
FROM [Employees] emp
LEFT JOIN
(
SELECT
CASE
WHEN d4.[DepartmentCode] IS NOT NULL THEN d4.[DepartmentCode]
WHEN d3.[DepartmentCode] IS NOT NULL THEN d3.[DepartmentCode]
WHEN d2.[DepartmentCode] IS NOT NULL THEN d2.[DepartmentCode]
ELSE d1.[DepartmentCode]
END AS [Level1]
, CASE
WHEN d4.[DepartmentCode] IS NOT NULL THEN d3.[DepartmentCode]
WHEN d3.[DepartmentCode] IS NOT NULL THEN d2.[DepartmentCode]
WHEN d2.[DepartmentCode] IS NOT NULL THEN d1.[DepartmentCode]
ELSE NULL
END AS [Level2]
, CASE
WHEN d4.[DepartmentCode] IS NOT NULL THEN d2.[DepartmentCode]
WHEN d3.[DepartmentCode] IS NOT NULL THEN d1.[DepartmentCode]
ELSE NULL
END AS [Level3]
, CASE
WHEN d4.[DepartmentCode] IS NOT NULL THEN d1.[DepartmentCode]
ELSE NULL
END AS [Level4]
, d1.[DepartmentCode] AS [DepartmentCode]
, d1.[CostCentreCode] AS [CostCenter]
FROM [Departments] d1
LEFT JOIN
[Departments] d2
ON d1.[Parent] = d2.[DepartmentCode]
LEFT JOIN
[Departments] d3
ON d2.[Parent] = d3.[DepartmentCode]
LEFT JOIN
[Departments] d4
ON d3.[Parent] = d4.[DepartmentCode]
) AS dept
ON emp.[Department] = dept.[DepartmentCode]
ORDER BY emp.[ClockNo]
The main challenge here is that the employee's department might need to be displayed in column Level1, Level2, Level3, or Level4, depending on how many upper levels there are for that department in the hierarchy.
I would suggest to first query the number of department levels there are for each employee in an inner query, and then to use that information to put the department codes in the right column:
SELECT ClockNo, CostCentre,
CASE LevelCount
WHEN 1 THEN Dep1
WHEN 2 THEN Dep2
WHEN 3 THEN Dep3
ELSE Dep4
END Level1,
CASE LevelCount
WHEN 2 THEN Dep1
WHEN 3 THEN Dep2
WHEN 4 THEN Dep3
END Level2,
CASE LevelCount
WHEN 3 THEN Dep1
WHEN 4 THEN Dep2
END Level3,
CASE LevelCount
WHEN 4 THEN Dep1
END Level4
FROM (SELECT e.ClockNo, e.CostCentre,
CASE WHEN d2.DepartmentCode IS NULL THEN 1
ELSE CASE WHEN d3.DepartmentCode IS NULL THEN 2
ELSE CASE WHEN d4.DepartmentCode IS NULL THEN 3
ELSE 4
END
END
END AS LevelCount,
d1.DepartmentCode Dep1, d2.DepartmentCode Dep2,
d3.DepartmentCode Dep3, d4.DepartmentCode Dep4
FROM Employees e
LEFT JOIN departments AS d1 ON d1.DepartmentCode = e.Department
LEFT JOIN departments AS d2 ON d2.DepartmentCode = d1.Parent
LEFT JOIN departments AS d3 ON d3.DepartmentCode = d2.Parent
LEFT JOIN departments AS d4 ON d4.DepartmentCode = d3.Parent) AS Base
ORDER BY ClockNo
SQL Fiddle
Alternatively, you could do a plain UNION ALL of the 5 possible scenarios in terms of existing levels (chains of 0 to 4 departments):
SELECT ClockNo, CostCentre, d4.DepartmentCode Level1,
d3.DepartmentCode Level2, d2.DepartmentCode Level3,
d1.DepartmentCode Level4
FROM Employees e
INNER JOIN departments AS d1 ON d1.DepartmentCode = e.Department
INNER JOIN departments AS d2 ON d2.DepartmentCode = d1.Parent
INNER JOIN departments AS d3 ON d3.DepartmentCode = d2.Parent
INNER JOIN departments AS d4 ON d4.DepartmentCode = d3.Parent
UNION ALL
SELECT ClockNo, CostCentre, d3.DepartmentCode,
d2.DepartmentCode, d1.DepartmentCode, NULL
FROM Employees e
INNER JOIN departments AS d1 ON d1.DepartmentCode = e.Department
INNER JOIN departments AS d2 ON d2.DepartmentCode = d1.Parent
INNER JOIN departments AS d3 ON d3.DepartmentCode = d2.Parent
WHERE d3.Parent = 0
UNION ALL
SELECT ClockNo, CostCentre, d2.DepartmentCode,
d1.DepartmentCode, NULL, NULL
FROM Employees e
INNER JOIN departments AS d1 ON d1.DepartmentCode = e.Department
INNER JOIN departments AS d2 ON d2.DepartmentCode = d1.Parent
WHERE d2.Parent = 0
UNION ALL
SELECT ClockNo, CostCentre, d1.DepartmentCode Level1,
NULL, NULL, NULL
FROM Employees e
INNER JOIN departments AS d1 ON d1.DepartmentCode = e.Department
WHERE d1.Parent = 0
UNION ALL
SELECT ClockNo, CostCentre, NULL, NULL, NULL, NULL
FROM Employees e
WHERE e.Department = 0
ORDER BY ClockNo
SQL Fiddle
SunnyMagadan's query is good. But depending on number of employees in a department you may wish to try the following one which leaves DB optimizer an opportunity to traverse department hierarchy only once for a department instead of repeating it for every employee in a department.
SELECT e.ClockNo, e.CostCentre, Level1, Level2, Level3, Level4
FROM Employees e
LEFT JOIN
(SELECT
d1.departmentcode
, d1.CostCentreCode
, coalesce (d4.departmentcode, d3.departmentcode
, d2.departmentcode, d1.departmentcode) AS Level1
, case when d4.departmentcode is not null then d3.departmentcode
when d3.departmentcode is not null then d2.departmentcode
when d2.departmentcode is not null then d1.departmentcode end as Level2
, case when d4.departmentcode is not null then d2.departmentcode
when d3.departmentcode is not null then d1.departmentcode end as Level3
, case when d4.departmentcode is not null then d1.departmentcode end as Level4
FROM departments AS d1
LEFT JOIN departments AS d2 ON d1.parent = d2.departmentcode
LEFT JOIN departments AS d3 ON d2.parent = d3.departmentcode
LEFT JOIN departments AS d4 ON d3.parent = d4.departmentcode) d
ON d.DepartmentCode = e.Department AND d.CostCentreCode = e.CostCentre
;
EDIT Regarding level 5+ departments.
Any fixed step query can not get top 4 levels for them. So change above query just to mark them some way, -1 for example.
, case when d4.Parent > 0 then NULL else
coalesce (d4.departmentcode, d3.departmentcode
, d2.departmentcode, d1.departmentcode) end AS Level1
and so on.
Try this query. Not sure how it will show itself performance-wise on large data with this COALESCE in place.
The idea is to build a derived table of hierarchies leading to each Department
lev1 lev2 lev3 lev4
1 NULL NULL NULL
1 2 NULL NULL
1 3 NULL NULL
1 3 5 NULL
4 NULL NULL NULL
and then use rightmost department to join it with Employees. Here's the full query:
SELECT
ClockNo,
CostCentre,
lev1,
lev2,
lev3,
lev4
FROM Employees
LEFT JOIN
(
SELECT
d1.DepartmentCode AS lev1,
NULL as lev2,
NULL as lev3,
NULL as lev4
FROM departments AS d1
WHERE d1.parent=0
UNION ALL
SELECT
d1.DepartmentCode AS lev1,
d2.DepartmentCode as lev2,
NULL as lev3,
NULL as lev4
FROM departments AS d1
JOIN departments AS d2 ON d2.parent = d1.departmentcode
WHERE d1.parent=0
UNION ALL
SELECT
d1.DepartmentCode AS lev1,
d2.DepartmentCode as lev2,
d3.DepartmentCode as lev3,
NULL as lev4
FROM departments AS d1
JOIN departments AS d2 ON d2.parent = d1.departmentcode
JOIN departments AS d3 ON d3.parent = d2.departmentcode
WHERE d1.parent=0
UNION ALL
SELECT
d1.DepartmentCode AS lev1,
d2.DepartmentCode as lev2,
d3.DepartmentCode as lev3,
d4.DepartmentCode as lev4
FROM departments AS d1
JOIN departments AS d2 ON d2.parent = d1.departmentcode
JOIN departments AS d3 ON d3.parent = d2.departmentcode
JOIN departments AS d4 ON d4.parent = d3.departmentcode
WHERE d1.parent=0
) Department
ON COALESCE(Department.lev4, Department.lev3, Department.lev2, Department.lev1) = Employees.Department
ORDER BY ClockNo
I would suggest that you seperate the query for getting the employee and getting his / her department hierarchy.
To get the hierarchy of the department, I would suggest you use recursive CTE something like this:
with DepartmentList (DepartmentCode, CostCentreCode, Parent) AS
(
SELECT
parentDepartment.DepartmentCode,
parentDepartment.CostCentreCode,
parentDepartment.Parent
FROM Departments parentDepartment
WHERE DepartmentCode = #departmentCode
UNION ALL
SELECT
childDepartment.DepartmentCode
childDepartment.CostCentreCode,
childDepartment.Parent,
FROM Departments childDepartment
JOIN DepartmentList
ON childDepartment.Parent = DepartmentList.DepartmentCode
)
SELECT * FROM DepartmentList
This is not the direct answer to your question, but this will give you option and idea. Hope this helps.
So I've taken two steps in order to get this done:
I had to generate levels for deparments recursively
Generate all possible parent nodes so that I could display them in pivoted view
This recursive query builds DepartmentLevels:
;WITH CTE (DepartmentCode, CostCentreCode, Parent, DepartmentLevel)
AS (
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, 1
FROM dbo.Departments AS D
WHERE D.Parent = 0
UNION ALL
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, C.DepartmentLevel + 1
FROM dbo.Departments AS D
INNER JOIN CTE AS C
ON C.DepartmentCode = D.Parent
AND C.CostCentreCode = D.CostCentreCode
)
SELECT *
INTO #DepartmentLevels
FROM CTE;
That's the output:
╔════════════════╦════════════════╦════════╦═════════════════╗
║ DepartmentCode ║ CostCentreCode ║ Parent ║ DepartmentLevel ║
╠════════════════╬════════════════╬════════╬═════════════════╣
║ 1 ║ AAA ║ 0 ║ 1 ║
║ 4 ║ BBB ║ 0 ║ 1 ║
║ 2 ║ AAA ║ 1 ║ 2 ║
║ 3 ║ AAA ║ 1 ║ 2 ║
║ 5 ║ AAA ║ 3 ║ 3 ║
╚════════════════╩════════════════╩════════╩═════════════════╝
Now this query will generate all possible parent nodes for each node (a kind of a mapping table):
;WITH CTE (DepartmentCode, CostCentreCode, Parent, DepartmentLevelCode)
AS (
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, D.DepartmentCode
FROM dbo.Departments AS D
UNION ALL
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, C.DepartmentLevelCode
FROM dbo.Departments AS D
INNER JOIN CTE AS C
ON C.Parent = D.DepartmentCode
)
SELECT *
FROM CTE;
Which gives us this result:
╔════════════════╦════════════════╦════════╦═════════════════════╗
║ DepartmentCode ║ CostCentreCode ║ Parent ║ DepartmentLevelCode ║
╠════════════════╬════════════════╬════════╬═════════════════════╣
║ 1 ║ AAA ║ 0 ║ 1 ║
║ 2 ║ AAA ║ 1 ║ 2 ║
║ 3 ║ AAA ║ 1 ║ 3 ║
║ 4 ║ BBB ║ 0 ║ 4 ║
║ 5 ║ AAA ║ 3 ║ 5 ║
║ 3 ║ AAA ║ 1 ║ 5 ║
║ 1 ║ AAA ║ 0 ║ 5 ║
║ 1 ║ AAA ║ 0 ║ 3 ║
║ 1 ║ AAA ║ 0 ║ 2 ║
╚════════════════╩════════════════╩════════╩═════════════════════╝
Now we can combine these three buddies together with Employees table and get desired output:
;WITH CTE (DepartmentCode, CostCentreCode, Parent, DepartmentLevelCode)
AS (
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, D.DepartmentCode
FROM dbo.Departments AS D
UNION ALL
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, C.DepartmentLevelCode
FROM dbo.Departments AS D
INNER JOIN CTE AS C
ON C.Parent = D.DepartmentCode
)
SELECT E.ClockNo
, E.CostCentre
, C.Level1
, C.Level2
, C.Level3
, C.Level4
FROM dbo.Employees AS E
OUTER APPLY (
SELECT MAX(CASE WHEN DL.DepartmentLevel = 1 THEN C.DepartmentCode END)
, MAX(CASE WHEN DL.DepartmentLevel = 2 THEN C.DepartmentCode END)
, MAX(CASE WHEN DL.DepartmentLevel = 3 THEN C.DepartmentCode END)
, MAX(CASE WHEN DL.DepartmentLevel = 4 THEN C.DepartmentCode END)
FROM CTE AS C
INNER JOIN #DepartmentLevels AS DL
ON DL.DepartmentCode = C.DepartmentCode
WHERE C.DepartmentLevelCode = E.Department
) AS C(Level1, Level2, Level3, Level4);
It will give this:
╔═════════╦════════════╦════════╦════════╦════════╦════════╗
║ ClockNo ║ CostCentre ║ Level1 ║ Level2 ║ Level3 ║ Level4 ║
╠═════════╬════════════╬════════╬════════╬════════╬════════╣
║ 1 ║ AAA ║ ║ ║ ║ ║
║ 2 ║ AAA ║ 1 ║ 3 ║ ║ ║
║ 3 ║ BBB ║ ║ ║ ║ ║
║ 4 ║ BBB ║ 4 ║ ║ ║ ║
║ 5 ║ CCC ║ ║ ║ ║ ║
║ 6 ║ AAA ║ 1 ║ ║ ║ ║
║ 7 ║ AAA ║ 1 ║ 3 ║ 5 ║ ║
╚═════════╩════════════╩════════╩════════╩════════╩════════╝
This query will find coresponding DepartmentLevelCode based on DepartmentCode and will pivot stuff based on the DepartmentLevel. Hopefully it's right.

Pivot Table SQL server 2008

I can't get to grips with pivot tables, I have 3 tables:
AccidentType:
AccidentTypeID Description
1 Type1
2 Type2
3 Type3
4 Type4
CaseAccidentType:
AccidentTypeID CaseID
1 1000
2 1000
3 1001
Case:
CaseID Name
1000 Case A
1001 Case B
Case is the main table with caseaccidenttype as a link table to the accident type. Each case can have multiple accidenttypes.
I want something like:
CaseID Name Type1 Type2 Type3 Type4
1000 Case A True True False False
1001 Case B False False True False
Looks like a pivot table but i just cant figure it out.
Update:
There will be many AccidentTypes is it possible to dynamically generate the column headings.
Test Data
DECLARE #AccidentType TABLE(AccidentTypeID INT, [Description] NVARCHAR(100))
INSERT INTO #AccidentType VALUES
(1,'Type1'),(2,'Type2'),(3,'Type3'),(4,'Type4')
DECLARE #CaseAccidentType TABLE(AccidentTypeID INT,CaseID INT)
INSERT INTO #CaseAccidentType VALUES
(1,1000),(2,1000),(3,1001)
DECLARE #Case TABLE(CaseID INT, Name NVARCHAR(100))
INSERT INTO #Case VALUES
(1000,'Case A'),(1001,'Case B')
Query
;With CTE
AS(
SELECT *
FROM
(
SELECT C.CaseID,C.Name, AT.[Description]
FROM #Case C INNER JOIN #CaseAccidentType CAT
ON C.CaseID = CAT.CaseID
INNER JOIN #AccidentType AT
ON CAT.AccidentTypeID = AT.AccidentTypeID
) Q
PIVOT
(MAX(Name)
FOR [Description]
IN ([Type1],[Type2],[Type3],[Type4])
)Pv
)
SELECT CT.CaseID
,C.Name
,CASE WHEN CT.Type1 IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END AS Type1
,CASE WHEN CT.Type2 IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END AS Type2
,CASE WHEN CT.Type3 IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END AS Type3
,CASE WHEN CT.Type4 IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END AS Type4
FROM CTE CT INNER JOIN #Case C
ON CT.CaseID = C.CaseID
Result Set
╔════════╦════════╦═══════╦═══════╦═══════╦═══════╗
║ CaseID ║ Name ║ Type1 ║ Type2 ║ Type3 ║ Type4 ║
╠════════╬════════╬═══════╬═══════╬═══════╬═══════╣
║ 1000 ║ Case A ║ TRUE ║ TRUE ║ FALSE ║ FALSE ║
║ 1001 ║ Case B ║ FALSE ║ FALSE ║ TRUE ║ FALSE ║
╚════════╩════════╩═══════╩═══════╩═══════╩═══════╝
Another approach -
SELECT C.CaseID,C.[Name],AT.[Description]
INTO #NO_PIVOT
FROM [Case] C
INNER JOIN CaseAccidentType CAT
ON CAT.CaseID = C.CaseID
INNER JOIN AccidentType AT
ON AT.AccidentTypeID = CAT.AccidentTypeID
SELECT
CaseID
,[Name]
,CASE WHEN SUM(Type1) > 0 THEN CAST('True' AS VARCHAR(5)) ELSE 'False' END AS [Type1]
,CASE WHEN SUM(Type2) > 0 THEN CAST('True' AS VARCHAR(5)) ELSE 'False' END AS [Type2]
,CASE WHEN SUM(Type3) > 0 THEN CAST('True' AS VARCHAR(5)) ELSE 'False' END AS [Type3]
,CASE WHEN SUM(Type4) > 0 THEN CAST('True' AS VARCHAR(5)) ELSE 'False' END AS [Type4]
FROM
( SELECT
CaseID
,[Name]
,CASE WHEN [Description] = 'Type1' AND ISNULL((SELECT COUNT(1) FROM #NO_PIVOT WHERE CaseID = NP.CaseID AND [Name] = NP.[Name]),-1) > 0
THEN 1 ELSE 0 END AS [Type1]
,CASE WHEN [Description] = 'Type2' AND ISNULL((SELECT COUNT(1) FROM #NO_PIVOT WHERE CaseID = NP.CaseID AND [Name] = NP.[Name]),-1) > 0
THEN 1 ELSE 0 END AS [Type2]
,CASE WHEN [Description] = 'Type3' AND ISNULL((SELECT COUNT(1) FROM #NO_PIVOT WHERE CaseID = NP.CaseID AND [Name] = NP.[Name]),-1) > 0
THEN 1 ELSE 0 END AS [Type3]
,CASE WHEN [Description] = 'Type4' AND ISNULL((SELECT COUNT(1) FROM #NO_PIVOT WHERE CaseID = NP.CaseID AND [Name] = NP.[Name]),-1) > 0
THEN 1 ELSE 0 END AS [Type4]
FROM #NO_PIVOT NP
)RESULTSET
GROUP BY CaseID, [Name]

Using a SQL Server table, I need to create a new table recursive?

I have a simple table of related items, like so (SQL Server db)
id Item Parent
1 2 5
2 4 5
3 5 12
4 6 2
5 10 6
I'd like to output a table that shows, for each Item a full path of all inter-related items (up to 4 "levels"), like so
id Item ParentL1 ParentL2 ParentL3 ParentL4
1 2 5 12
2 4 5 12
3 5 12
4 6 2 5 12
5 10 6 2 5 12
Thanks!
This is the simple approach.
SELECT id, t1.Item as Item,
t1.Parent as ParentL1,
t2.Parent as ParentL2,
t3.Parent as ParentL3,
t4.Parent as ParentL4
FROM Items t1
LEFT JOIN Items t2 ON t1.Parent = t2.Id
LEFT JOIN Items t3 ON t2.Parent = t3.Id
LEFT JOIN Items t4 ON t3.Parent = t4.Id
The follwoing query should do the trick
SELECT t1.id, t1.Item, t1.Parent [ParentL1], t2.Parent [ParentL2], t3.Parent [ParentL3], t4.Parent [ParentL4]
FROM MyTable t1
LEFT JOIN MyTable t2
ON t1.Parent = t2.Item
LEFT JOIN MyTable t3
ON t2.Parent = t3.Item
LEFT JOIN MyTable t4
ON t3.Parent = t4.Item
Used the following to create the test table, MyTable to confirm the resultset
CREATE TABLE MyTable
(
id Int IDENTITY,
Item Int,
Parent Int
)
INSERT MyTable
VALUES (2, 5),
(4, 5),
(5, 12),
(6, 2),
(10, 6)
Ok, even though the LEFT JOINs are the simplest way in this case (when only 4 levels of recursion are needed), this is another option using recursive CTEs (SQL Server 2005+):
;WITH CTE AS
(
SELECT *, 1 RecursionLevel
FROM YourTable
UNION ALL
SELECT B.id, A.Item, B.Parent, RecursionLevel + 1
FROM CTE A
INNER JOIN YourTable B
ON A.Parent = B.Item
)
SELECT Item,
MIN(CASE WHEN RecursionLevel = 1 THEN Parent END) ParentL1,
MIN(CASE WHEN RecursionLevel = 2 THEN Parent END) ParentL2,
MIN(CASE WHEN RecursionLevel = 3 THEN Parent END) ParentL3,
MIN(CASE WHEN RecursionLevel = 4 THEN Parent END) ParentL4
FROM CTE
WHERE RecursionLevel <= 4
GROUP BY Item
This is the result:
╔══════╦══════════╦══════════╦══════════╦══════════╗
║ Item ║ ParentL1 ║ ParentL2 ║ ParentL3 ║ ParentL4 ║
╠══════╬══════════╬══════════╬══════════╬══════════╣
║ 2 ║ 5 ║ 12 ║ NULL ║ NULL ║
║ 4 ║ 5 ║ 12 ║ NULL ║ NULL ║
║ 5 ║ 12 ║ NULL ║ NULL ║ NULL ║
║ 6 ║ 2 ║ 5 ║ 12 ║ NULL ║
║ 10 ║ 6 ║ 2 ║ 5 ║ 12 ║
╚══════╩══════════╩══════════╩══════════╩══════════╝
And here is a sqlfiddle with a demo of this.

select data that has at least P and R

I have a table named Table1 as shown below:
ID AccountNo Trn_cd
1 123456 P
2 123456 R
3 123456 P
4 12345 P
5 111 R
6 111 R
7 5625 P
I would like to display those records that accountNo appears more than one time (duplicate) and trn_cd has at least both P and R.
In this case the output should be at this way:
ID AccountNo Trn_cd
1 123456 P
2 123456 R
3 123456 P
I have done this sql but not the result i want:
select * from Table1
where AccountNo IN
(select accountno from table1
where trn_cd = 'P' or trn_cd = 'R'
group by AccountNo having count(*) > 1)
Result as below which AccountNo 111 shouldn't appear because there is no trn_cd P for 111:
ID AccountNo Trn_cd
1 123456 P
2 123456 R
3 123456 P
5 111 R
6 111 R
Any idea?
Use aggregation for this. To get the account numbers:
select accountNo
from table1
having count(*) > 1 and
sum(case when trn_cd = 'P' then 1 else 0 end) > 0 and
sum(case when trn_cd = 'N' then 1 else 0 end) > 0
To get the account information, use a join or in statement:
select t.*
from table1 t
where t.accountno in (select accountNo
from table1
having count(*) > 1 and
sum(case when trn_cd = 'P' then 1 else 0 end) > 0 and
sum(case when trn_cd = 'N' then 1 else 0 end) > 0
)
This problem is called Relational Division.
This can be solved by filtering the records which contains P and R and counting the records for every AccountNo returned, and filtering it again using COUNT(DISTINCT Trn_CD) = 2.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT AccountNo
FROM TableName
WHERE Trn_CD IN ('P','R')
GROUP BY AccountNo
HAVING COUNT(DISTINCT Trn_CD) = 2
) b ON a.AccountNO = b.AccountNo
SQLFiddle Demo
SQL of Relational Division
OUTPUT
╔════╦═══════════╦════════╗
║ ID ║ ACCOUNTNO ║ TRN_CD ║
╠════╬═══════════╬════════╣
║ 1 ║ 123456 ║ P ║
║ 2 ║ 123456 ║ R ║
║ 3 ║ 123456 ║ P ║
╚════╩═══════════╩════════╝
For faster performance, add an INDEX on column AccountNo.

Group by does not show all the rows

I have a table tblPersonaldata and tblStudentsadmitted
tblPersonalData
UID Name Gender
------------------------
E1 xyz M
E2 pqr M
E3 mno M
tblStudentsadmitted
UID Status Stage
----------------------
E1 Y 1
E2 Y 2
E3 Y 1
Now I want the data like this:
Gender Stage1 Stage2
M 2 1
But in this case I dont get the data for female gender. I want the data for female gender even if it is null
I have tried this:
select
case
when gender='M' then 'Male'
when gender='F' then 'Female'
end as Gender,
sum(case when Stage=1 then 1 else 0) end as Stage1,
sum(case when Stage=2 then 1 else 0) end as Stage2
from tblPersonaldata A inner join
tblStudentsadmitted B on A.UID=B.UID
where B.Status='Y'
group by Gender
SELECT CASE WHEN a.Gender = 'M' THEN 'Male' ELSE 'FEMALE' END Gender,
SUM(CASE WHEN Stage = 1 THEN 1 ELSE 0 END) Stage1,
SUM(CASE WHEN Stage = 2 THEN 1 ELSE 0 END) Stage2
FROM personal a
LEFT JOIN studentadmitted b
ON a.UID = b.UID AND b.Status = 'Y'
GROUP BY a.Gender
SQLFiddle Demo
SELECT CASE WHEN c.Gender = 'M' THEN 'Male' ELSE 'Female' END Gender,
SUM(CASE WHEN Stage = 1 THEN 1 ELSE 0 END) Stage1,
SUM(CASE WHEN Stage = 2 THEN 1 ELSE 0 END) Stage2
FROM (SELECT 'F' Gender UNION SELECT 'M' Gender) c
LEFT JOIN personal a
ON a.Gender = c.Gender
LEFT JOIN studentadmitted b
ON a.UID = b.UID AND b.Status = 'Y'
GROUP BY c.Gender
SQLFiddle Demo
OUTPUT
╔════════╦════════╦════════╗
║ GENDER ║ STAGE1 ║ STAGE2 ║
╠════════╬════════╬════════╣
║ Female ║ 0 ║ 0 ║
║ Male ║ 2 ║ 1 ║
╚════════╩════════╩════════╝
In SQL Server, you can use the PIVOT function to generate the result:
select gender,
Stage1,
Stage2
from
(
select
c.gender,
'Stage'+cast(stage as varchar(10)) Stage
from (values ('F'),('M')) c (gender)
left join tblpersonaldata p
on c.gender = p.gender
left join tblStudentsadmitted s
on p.uid = s.uid
and s.Status='Y'
)src
pivot
(
count(stage)
for stage in (Stage1, Stage2)
) piv
See SQL Fiddle with Demo.
Since you are using SQL Server 2008 this query uses the VALUES to generate the list of the genders that you want in the final result set
from (values ('F'),('M')) c (gender)
Then by using a LEFT JOIN on the other tables the final result will return a row for both the M and F values.
This can also be written using a UNION ALL to generate the list of genders:
select gender,
Stage1,
Stage2
from
(
select
c.gender,
'Stage'+cast(stage as varchar(10)) Stage
from
(
select 'F' gender union all
select 'M' gender
) c
left join tblpersonaldata p
on c.gender = p.gender
left join tblStudentsadmitted s
on p.uid = s.uid
and s.Status='Y'
)src
pivot
(
count(stage)
for stage in (Stage1, Stage2)
) piv
See SQL Fiddle with Demo
The result of both is:
| GENDER | STAGE1 | STAGE2 |
----------------------------
| F | 0 | 0 |
| M | 2 | 1 |
This is also working. Using Left joins with a new table (a table with two records for genders M & F).
Fiddle demo
select t.g Gender,
isnull(sum(case when Stage = 1 then 1 end),0) Stage1,
isnull(sum(case when Stage = 2 then 1 end),0) Stage2
from (values ('M'),('F')) t(g)
left join personal a on t.g = a.gender
left join studentadmitted b on a.uid = b.uid and b.Status = 'Y'
group by t.g
order by t.g
| GENDER | STAGE1 | STAGE2 |
----------------------------
| F | 0 | 0 |
| M | 2 | 1 |
SELECT GENDER, 0 AS 'STAGE 0', 1 AS 'STAGE 1', 2 AS 'STAGE 2'
FROM
(
SELECT P.ID, GENDER,CASE WHEN STAGE IS NULL THEN 0 ELSE STAGE END STAGE
FROM tblPersonaldata P
LEFT JOIN tblStudentsadmitted S ON P.UID = S.UID
) AS A
PIVOT
(
COUNT (ID) FOR STAGE IN ([0],[1],[2])
)P