Pivot Table SQL server 2008 - sql

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]

Related

How to count amount of data for each row

I have this table:
ID Name 01 02 03 04 05 06 07
0000068 Name1 V VX
0000069 Name2 V VX VX V V
0000070 Name3 V V V V V V
This is an table for absence check and I want to count the amount of data from each row from column 01 to 07, so I expect the first row will give result of 2, second row 5, and third row 6. And if possible I want that result to be added as a new column. Is there a way to work this somehow?
If it's always V or VX you could replace the VX with V, concatenate
them together and take the LEN():
SELECT *, LEN(REPLACE([01]+[02]+[03]+[04]+[05]+[06]+[07],'X','')) AS New_Ct
FROM YourTable
If the blanks are actually NULL you'd have to wrap them in ISNULL():
SELECT *, LEN(REPLACE(ISNULL([01],'')+ISNULL([02],'')+ISNULL([03],'')+ISNULL([04],'')+ISNULL([05],'')+ISNULL([06],'')+ISNULL([07],''),'X','')) AS New_Ct
FROM Table1
Demo: SQL Fiddle
If the number of columns is static, you can use a simple CASE expression.
SELECT *,
CASE WHEN [01] = '' OR [01] IS NULL THEN 0 ELSE 1 END +
CASE WHEN [02] = '' OR [02] IS NULL THEN 0 ELSE 1 END +
CASE WHEN [03] = '' OR [03] IS NULL THEN 0 ELSE 1 END +
CASE WHEN [04] = '' OR [04] IS NULL THEN 0 ELSE 1 END +
CASE WHEN [05] = '' OR [05] IS NULL THEN 0 ELSE 1 END +
CASE WHEN [06] = '' OR [06] IS NULL THEN 0 ELSE 1 END +
CASE WHEN [07] = '' OR [07] IS NULL THEN 0 ELSE 1 END [cnt]
FROM Table1;
An SQLfiddle to test with.
SELECT 01+02+03+04+05+06+07 AS total_number FROM table_name;
Try Like this
SELECT ID,Name,
(Case When[01] IS Null or [01] = '' then 0 else 1 end) +
(Case When[02] IS Null or [02] = '' then 0 else 1 end) +
(Case When[03] IS Null or [03] = '' then 0 else 1 end) +
(Case When[04] IS Null or [04] = '' then 0 else 1 end) +
(Case When[05] IS Null or [05] = '' then 0 else 1 end) +
(Case When[06] IS Null or [06] = '' then 0 else 1 end) +
(Case When[07] IS Null or [07] = '' then 0 else 1 end) AS Total
FROM table1;
FIDDLE DEMO

sql Pivot how can I do this

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)

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.

Get Data Of Third Column Based on Two other Columns

This is a sample table
ID STOREA STOREB STOREC AB BC CA ABC
--- ------- ------ ------- -- -- --- ---
10 1 0 0
10 0 1 0
10 0 1 0
29 0 1 0
29 0 0 1
29 1 0 0
Each row corresponds to a purchase made at either of Store A or B or C. Customer 10 shops at A and B but not c. So I want AB=1 BC=0 CA=0 ABC=0 for all ID=10 rows and for ID=29, he shops at all 3, so I need AB=1 BC=1 CA=1 ABC=1 for all rows where ID=29 (using ORACLE SQL)
I would like to update the columns in the table.
Here is one way you can do this. I don't think you can use JOINs in Oracle with UPDATE statements -- however, you can accomplish the same thing by using MERGE:
MERGE
INTO yourtable
USING (
select id as idnew,
case when a + b = 2 then 1 else 0 end abnew,
case when b + c = 2 then 1 else 0 end bcnew,
case when a + c = 2 then 1 else 0 end acnew,
case when a + b + c = 3 then 1 else 0 end abcnew
from (
select
id,
max(case storea when 1 then 1 else 0 end) A,
max(case storeb when 1 then 1 else 0 end) B,
max(case storec when 1 then 1 else 0 end) C
from yourtable
group by id
) a
)
ON (id = idnew)
WHEN MATCHED THEN
UPDATE
SET ab = abnew,
bc = bcnew,
ac = acnew,
abc = abcnew
SQL Fiddle Demo
Here is how you can do this as a select:
update (select id, storea, storeb, storec, AB as new_AB, BC as new_BC, AC as new_AC, ABC as new_ABC
from t join
(select id,
(case when max(storeA) = 1 and max(storeB) = 1 then 1 else 0 end) as AB,
(case when max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as BC,
(case when max(storeA) = 1 and max(storeC) = 1 then 1 else 0 end) as AC,
(case when max(storeA) = 1 and max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as ABC
from t
group by id
) tsum
on t.id = tsum.id
)
set AB = new_AB, AC = new_AC, BC = new_BC, ABC = new_ABC;
I think this might work:
select id, storea, storeb, storec, AB, BC, AC, ABC
from t join
(select id,
(case when max(storeA) = 1 and max(storeB) = 1 then 1 else 0 end) as AB,
(case when max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as BC,
(case when max(storeA) = 1 and max(storeC) = 1 then 1 else 0 end) as AC,
(case when max(storeA) = 1 and max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as ABC
from t
group by id
) tsum
on t.id = tsum.id
)
set AB = new_AB, AC = new_AC, BC = new_BC, ABC = new_ABC;

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