Get output column for each Key - sql

I have a database with roughly this fields:
SELECT [UserId]
,[MissionId]
,[LevelId]
,[Key]
,[BooleanAnswer]
,[CreationDateTimeUtc]
,[QuestionId]
FROM [Bakman].[dbo].[LevelEntries]
WHERE LevelId = 2 AND QuestionId = 1 OR QuestionId = 2 OR QuestionId = 3 OR QuestionId = 4 OR QuestionId = 5 OR QuestionId = 6
Now I get this kind of output:
UserId MissionId LevelId Key BooleanAnswer CreationDateTimeUtc QuestionId
0 1 2 1 1 2017-10-07 11:39:26.350 1
0 1 2 2 1 2017-10-07 11:39:26.350 1
0 1 2 3 0 2017-10-07 11:39:26.350 1
0 1 2 4 1 2017-10-07 11:39:26.350 1
0 1 2 5 0 2017-10-07 11:39:26.350 1
0 1 2 6 1 2017-10-07 11:39:26.350 1
But now I want to get a column for each QuestionId (which are equal to the [key]).
Like this :
UserId MissionId LevelId Key1 Key2 Key3 Key4 Key5 Key6 CreationDateTimeUtc QuestionId
1 2 1 0 1 1 0 1 1 1 2017-10-07 11:39:26.350 1
Where Key[key] has the boolean awnser and the key belonging to that element.
Good to know is that the Userid + MissionId + LevelId + Key are the Composite key for this table.
EDIT:
Trying both given answers I now get something like this
UserId MissionId LevelId Key1 Key2 Key3 Key4 Key5 Key6
1 1 2 1 NULL NULL NULL NULL NULL
1 1 2 NULL 1 NULL NULL NULL NULL
1 1 2 NULL NULL 0 NULL NULL NULL
1 1 2 NULL NULL NULL 1 NULL NULL
1 1 2 NULL NULL NULL NULL 1 NULL
1 1 2 NULL NULL NULL NULL NULL 1
Now I want to combine those so I get the level result of the user.

Try the following query
SELECT
UserId,
MissionId,
LevelId,
MAX(CASE WHEN [Key]=1 THEN BooleanAnswer END) Key1,
MAX(CASE WHEN [Key]=2 THEN BooleanAnswer END) Key2,
MAX(CASE WHEN [Key]=3 THEN BooleanAnswer END) Key3,
MAX(CASE WHEN [Key]=4 THEN BooleanAnswer END) Key4,
MAX(CASE WHEN [Key]=5 THEN BooleanAnswer END) Key5,
MAX(CASE WHEN [Key]=6 THEN BooleanAnswer END) Key6,
CreationDateTimeUtc,
QuestionId
FROM LevelEntries
WHERE LevelId=2
AND QuestionId IN(1,2,3,4,5,6)
GROUP BY UserId,MissionId,LevelId,CreationDateTimeUtc,QuestionId
The second variant
SELECT
UserId,
MissionId,
LevelId,
MAX(CASE WHEN [Key]=1 THEN BooleanAnswer END) Key1,
MAX(CASE WHEN [Key]=2 THEN BooleanAnswer END) Key2,
MAX(CASE WHEN [Key]=3 THEN BooleanAnswer END) Key3,
MAX(CASE WHEN [Key]=4 THEN BooleanAnswer END) Key4,
MAX(CASE WHEN [Key]=5 THEN BooleanAnswer END) Key5,
MAX(CASE WHEN [Key]=6 THEN BooleanAnswer END) Key6,
MAX(CreationDateTimeUtc) MaxCreationDateTimeUtc,
QuestionId
FROM LevelEntries
WHERE LevelId=2
AND QuestionId IN(1,2,3,4,5,6)
GROUP BY UserId,MissionId,LevelId,QuestionId

You can use PIVOT for that purpose.
with Entries as
(
select UserId,
MissionId,
LevelId,
'Key' + cast([Key] as varchar) as [Key],
BooleanAnswer,
CreationDateTimeUtc,
QuestionId
from [Bakman].[dbo].[LevelEntries]
where LevelId = 2 and QuestionId in (1, 2, 3, 4, 5, 6)
)
select UserId,
MissionId,
LevelId,
Key1,
Key2,
Key3,
Key4,
Key5,
Key6,
CreationDateTimeUtc,
QuestionId
from Entries t
pivot
(
max(BooleanAnswer)
for [Key] in (Key1, Key2, Key3, Key4, Key5, Key6)
) p;

Related

TSQL Pivot table rows to columns

I've got some data in a table that looks like the following. I'm trying to run a query that will get my data on a single row per requestId. I don't need the dates or the denial reason just the eprojman and apvStatus for each groupId
requestId - projMan1 - apvStatus1 - projMan2 - apvStatus2 - projMan3 - apvStatus3 etc.. for all 5 groupIds
requestId
groupId
entryDate
approvalDate
apvStatus
projMan
denialReason
1
1
2020-11-02
2019-07-25
APPROVED
rx1942
NULL
1
2
2020-11-02
2019-07-25
APPROVED
ma2674
NULL
1
3
2020-11-02
2019-07-25
APPROVED
cb9097
NULL
1
4
2020-11-02
2019-07-25
APPROVED
bj1763
NULL
1
5
2020-11-02
2019-07-25
APPROVED
tr5972
NULL
2
1
2020-11-02
NULL
NOT APPROVED
NULL
6
2
2
2020-11-02
NULL
PENDING
ma2674
NULL
2
3
2020-11-02
NULL
PENDING
cb9097
NULL
2
4
2020-11-02
NULL
PENDING
bj1763
NULL
2
5
2020-11-02
NULL
PENDING
tr5972
NULL
I've been trying to use a PIVOT table but all the examples I find involves summing data or something. I just pretty much want to take the 5 rows and turn it into 1 for each requestID
The only thing I've been able to come up with is to select from the same table 5 times for each groupID and union it but that's slower than heck. Got to be a better way
Thanks.
Current query:
select group1.requestId
, group1.apvStatus as apvStatus1
, group1.projMan as projMan1
, group2.apvStatus as apvStatus2
, group2.projMan as projMan2
, group3.apvStatus as apvStatus3
, group3.projMan as projMan3
,group4.apvStatus as apvStatus4
, group4.projMan as projMan4
,group5.apvStatus as apvStatus5
, group5.projMan as projMan5
,group1.denialReason
INTO #TEMPBAOrganized
from (
select requestId, apvStatus, projMan, denialReason from #TEMPBULKAPPROVAL where groupId = 1) group1
INNER JOIN
(select requestId, apvStatus, projMan, denialReason from #TEMPBULKAPPROVAL where groupId = 2) group2
on group1.requestId = group2.requestId
INNER JOIN
(select requestId, apvStatus, projMan from #TEMPBULKAPPROVAL where groupId = 3) group3
on group1.requestId = group3.requestId
INNER JOIN
(select requestId, apvStatus, projMan from #TEMPBULKAPPROVAL where groupId = 4) group4
on group1.requestId = group4.requestId
INNER JOIN
(select requestId, apvStatus, projMan from #TEMPBULKAPPROVAL where groupId = 5) group5
on group1.requestId = group5.requestId
For pivoting of multiple column, it is easier to use CASE expression with aggregate.
select t.requestId,
projMan1 = max(case when t.groupId = 1 then t.projMan end),
apvStatus1 = max(case when t.groupId = 1 then t.apvStatus end),
projMan2 = max(case when t.groupId = 2 then t.projMan end),
apvStatus2 = max(case when t.groupId = 2 then t.apvStatus end),
projMan3 = max(case when t.groupId = 3 then t.projMan end),
apvStatus3 = max(case when t.groupId = 3 then t.apvStatus end),
projMan4 = max(case when t.groupId = 4 then t.projMan end),
apvStatus4 = max(case when t.groupId = 4 then t.apvStatus end),
projMan5 = max(case when t.groupId = 5 then t.projMan end),
apvStatus5 = max(case when t.groupId = 5 then t.apvStatus end)
from #TEMPBULKAPPROVAL t
group by t.requestId
Note : max is also an aggregate function
You can do it with something like this:
SELECT
requestId,
approvalDate_1 = MAX(approvalDate_1),
approvalDate_2 = MAX(approvalDate_2),
approvalDate_3 = MAX(approvalDate_3),
approvalDate_4 = MAX(approvalDate_4),
approvalDate_5 = MAX(approvalDate_5),
projMan_1 = MAX(projMan_1),
projMan_2 = MAX(projMan_2),
projMan_3 = MAX(projMan_3),
projMan_4 = MAX(projMan_4),
projMan_5 = MAX(projMan_5)
FROM
(
SELECT
requestId,
groupId,
approvalDate,
projMan,
'approvalDate_' + CAST( groupId AS VARCHAR(2)) AS approvalDatePivot,
'projMan_' + CAST( groupId AS VARCHAR(2)) AS projManPivot
FROM
#tbl
) T
PIVOT (
MAX(approvalDate) FOR approvalDatePivot IN ([approvalDate_1],[approvalDate_2],[approvalDate_3],[approvalDate_4],[approvalDate_5])
) pvt_1
PIVOT (
MAX(projMan) FOR projManPivot IN ([projMan_1],[projMan_2],[projMan_3],[projMan_4],[projMan_5])
) pvt_2
GROUP BY requestId

Get custom product selection upon dynamic ID in SQL

I have below table structure and I would like to obtain the result in the following form:
First, this is my item table output:
orderID code action id level description Price solvedChoice
--------------------------------------------------------------------------
321 622 RECIPE 0 0 SPICM1 15.5 NULL
321 10 RECIPE 0 1 SPICKN 17 NULL
321 7091 RECIPE 0 1 RFRY 8.5 NULL
321 521 CHOICE 0 1 R-COKE 7.5 10000003
321 612 RECIPE 1 0 BIGTM1 20.5 NULL
321 13 RECIPE 1 1 BTASTY 21 NULL
321 7091 RECIPE 1 1 RFRY 8.5 NULL
321 522 CHOICE 1 1 R-FANT 7.5 10000003
321 608 RECIPE 2 0 ROYAL1 18.5 NULL
321 11 RECIPE 2 1 MCROYA 18 NULL
321 7091 RECIPE 2 1 RFRY 8.5 NULL
321 411 CHOICE 2 1 ARWA 7.5 10000003
321 612 RECIPE 3 0 BIGTM1 20.5 NULL
321 13 RECIPE 3 1 BTASTY 21 NULL
321 7091 RECIPE 3 1 RFRY 8.5 NULL
321 524 CHOICE 3 1 R-SPRT 7.5 10000003
I want to get what select under each meal, for example id = 0, represent one meal with their sub-level (components) and we can see the choice made was R-Coke while for id =1 , the choice made is R-FANT.
The output should be like this:
R-COKE R-FANT ARWA R-SPRT
--------------------------------------
SPICM1 1 0 0 0
BIGTM1 0 1 0 1
ROYAL1 0 0 1 0
This looks like two levels of aggregation to me:
select col1,
sum(r_coke) as r_coke,
sum(r_fant) as r_fant,
sum(arwa) as arwa,
sum(r_sprt) as r_sprt
from (select max(case when level = 0 then description end) as col1,
sum(case when description = 'R-COKE' then 1 else 0 end) as r_coke,
sum(case when description = 'R-FANT' then 1 else 0 end) as r_fant,
sum(case when description = 'ARWA' then 1 else 0 end) as arwa,
sum(case when description = 'R-SPRT' then 1 else 0 end) as r_sprt
from t
group by id
) x
group by col1;
Or, perhaps more simply, using window functions:
select col1,
sum(case when description = 'R-COKE' then 1 else 0 end) as r_coke,
sum(case when description = 'R-FANT' then 1 else 0 end) as r_fant,
sum(case when description = 'ARWA' then 1 else 0 end) as arwa,
sum(case when description = 'R-SPRT' then 1 else 0 end) as r_sprt
from (select t.*,
max(case when level = 0 then description end) over (partition by id) as col1
from t
) t
group by col1;
Just following input and output provided
select orderID, x, [R-COKE], [R-FANT], [ARWA], [R-SPRT]
from (
select orderID, id
, max(case when level = 0 then description end) x
, max(case when level = 1 and solvedChoice is not null then description end) y
from mytable
group by orderID, id
) t
pivot (count(id) for y in ([R-COKE], [R-FANT], [ARWA], [R-SPRT]) ) pvt;
You could join the table to itself. Something like this
drop TABLE if exists #MyItemTable;
go
CREATE TABLE #MyItemTable
(
orderID INT,
code int,
action char(6),
id int,
level int,
description varchar(10),
price money,
solvedChoice int
)
INSERT INTO #MyItemTable (orderID, code, action, id , level, description, Price, solvedChoice)
VALUES
(321, 622 ,'RECIPE',0,0,'SPICM1',15.5 ,NULL)
,(321, 10 ,'RECIPE',0,1,'SPICKN',17 ,NULL )
,(321, 7091,'RECIPE',0,1,'RFRY ',8.5 ,NULL )
,(321, 521 ,'CHOICE',0,1,'R-COKE',7.5 ,10000003)
,(321, 612 ,'RECIPE',1,0,'BIGTM1',20.5 ,NULL )
,(321, 13 ,'RECIPE',1,1,'BTASTY',21 ,NULL )
,(321, 7091,'RECIPE',1,1,'RFRY ',8.5 ,NULL )
,(321, 522 ,'CHOICE',1,1,'R-FANT',7.5 ,10000003)
,(321, 608 ,'RECIPE',2,0,'ROYAL1',18.5 ,NULL )
,(321, 11 ,'RECIPE',2,1,'MCROYA',18 ,NULL )
,(321, 7091,'RECIPE',2,1,'RFRY ',8.5 ,NULL )
,(321, 411 ,'CHOICE',2,1,'ARWA ',7.5 ,10000003)
,(321, 612 ,'RECIPE',3,0,'BIGTM1',20.5 ,NULL )
,(321, 13 ,'RECIPE',3,1,'BTASTY',21 ,NULL )
,(321, 7091,'RECIPE',3,1,'RFRY ',8.5 ,NULL )
,(321, 524 ,'CHOICE',3,1,'R-SPRT',7.5 ,10000003);
select i.[description],
sum(case when i2.[description] = 'R-COKE' then 1 else 0 end) as r_coke,
sum(case when i2.[description] = 'R-FANT' then 1 else 0 end) as r_fant,
sum(case when i2.[description] = 'ARWA' then 1 else 0 end) as arwa,
sum(case when i2.[description] = 'R-SPRT' then 1 else 0 end) as r_sprt
from #MyItemTable i
left join #MyItemTable i2 on i.id=i2.id
and i2.[action]='CHOICE'
where i.[level]=0
group by i.[description];
description r_coke r_fant arwa r_sprt
BIGTM1 0 1 0 1
ROYAL1 0 0 1 0
SPICM1 1 0 0 0
The aim is to get 1 result for each type of order, that is represented by level=0, id is the order identifier.
You need to first normalize the results by querying the orders and the CHOICE items separately, then you can correlate them with a join.
Once you have identified the spearate order and item records, then we can easily target them with aggregates, in this case a simple COUNT
COUNT works well in this context because it will exclude NULL values.
SELECT [order].description
, COUNT(DISTINCT [order].id) as [Orders]
, COUNT(CASE WHEN item.description = 'R-COKE' THEN 1 END) as [R-COKE]
, COUNT(CASE WHEN item.description = 'R-FANT' THEN 1 END) as [R-FANT]
, COUNT(CASE WHEN item.description = 'ARWA' THEN 1 END) as [ARWA]
, COUNT(CASE WHEN item.description = 'R-SPRT' THEN 1 END) as [R-SPRT]
FROM tblOrders [order]
INNER JOIN tblOrders item ON item.id = [order].id AND item.level = 1
WHERE [order].level = 0
GROUP BY [order].description
Try it out in this fiddle: http://sqlfiddle.com/#!18/81ec5/1
To further highlight the groupings, I have included a count of the separate orders, seeing we are counting the drinks as well.

sql: long to wide format without using PIVOT

I have a table like the following:
user_id time_id val0 val1 val2 actual_value score
1 1 1 0 0 0 0.6
1 1 0 1 0 1 0.4
1 1 0 0 1 2 0.3
1 2 1 0 0 0 0.7
1 2 0 1 0 1 0.4
1 2 0 0 1 2 0.3
2 1 1 0 0 0 0.9
2 1 0 1 0 1 0.5
2 1 0 0 1 2 0.4
I want to convert the data to wide format like the following:
user_id time_id score_0 score_1 score_2
1 1 0.6. 0.3. 0.3
1 2 0.7. 0.4. 0.3
2 1 0.9. 0.5. 0.4
the SQL I used does not have a pivot choice so I am wondering how to convert the long format to wide without using PIVOT.
If I understand your question correctly, you can do conditional aggregation:
select
user_id,
time_id,
max(case when val0 = 1 then score end) score0,
max(case when val1 = 1 then score end) score1,
max(case when val2 = 1 then score end) score2
from mytable
group by user_id, time_id
Maybe you want to use actual_value to pivot instead of the val[n] columns:
select
user_id,
time_id,
max(case when actual_value = 0 then score end) score0,
max(case when actual_value = 1 then score end) score1,
max(case when actual_value = 2 then score end) score2
from mytable
group by user_id, time_id
You can use conditional aggregation:
select user_id, time_id,
max(case when actual_value = 0 then score end) as score_0,
max(case when actual_value = 1 then score end) as score_1,
max(case when actual_value = 2 then score end) as score_2
from t
group by user_id, time_id;

SQL using GROUP BY and COUNT

I created the MariaDB(10.1.21) table named 'group_test' and saved some data as below.
Group Item Value1 Value2 Value3
A a1 1 0 0
A a2 1 1 1
A a3 1 1 2
B b1 1 1 0
B b2 1 1 1
B b3 1 0 0
B b4 1 1 3
C c1 1 1 0
C c2 1 1 1
Using a query, I want to make the result as below at once.
Group Items Value1_1 Value2_1 Value3_1
A 3 3 2 1
B 4 4 3 1
C 2 2 2 1
Items means the total number of 'Item' in the 'Group'.
ValueN_1 means the total number of 'ValueN' value equal to 1 in the 'Group'.
I think I would use GROUP BY and COUNT but I don't know exactly what to do.
How do I write SQL to get the above results in one query?
Thanks.
Simply do a GROUP BY. Since Value1 to Value3 are only 0's and 1's, you can use SUM() to count the 1's.
select Group, count(Item), sum(Value1), sum(Value2), sum(Value3)
from tablename
group by Group
Edit: "ValueN can have a value from 0 to 4":
select Group,
count(Item),
sum(case when Value1 = 1 then 1 else 0 end) Value1_1,
sum(case when Value2 = 1 then 1 else 0 end) Value2_1,
sum(case when Value3 = 1 then 1 else 0 end) Value3_1
from tablename
group by Group
Assuming value1, value2 and value3 can only be either 0 or 1, you can write it like this
select Group,
count(*) as Items,
sum(value1) as Value1_1,
sum(value2) as Value2_1,
sum(value3) as Value3_1
from yourTable t1
group by Group
If that's not the case, you will have to use a case in the sum
select Group,
count(*) as Items,
sum(case when value1 = 1 then 1 else 0 end) as Value1_1,
sum(case when value2 = 1 then 1 else 0 end) as Value2_1,
sum(case when value3 = 1 then 1 else 0 end) as Value3_1
from yourTable t1
group by Group
Use GROUP BY clause and SUM aggregate function in SELECT statement
SELECT *
FROM
(
SELECT [Group], count(Item) Item, sum(Value1) Value1, sum(Value2) Value2,
sum(Value3) Value3
FROM Your_tableName
GROUP BY [Group]
) A

SQL Server: Rows to Columns with Case

I have a table with 2 columns:
CREATE TABLE Prop_Cl
(
Id int,
ClId int
);
INSERT INTO Prop_Cl
(Id, ClId)
VALUES
(1, 1111111),
(1, 1111112),
(1, 1111113),
(2, 2222221),
(3, 3333331),
(3, 3333332);
ID CLID
1 1111111
1 1111112
1 1111113
2 2222221
3 3333331
3 3333332
I'm trying to show this table in that way:
ID CLIENT 1 CLIENT 2 CLIENT 3 CLIENT 4
1 1111111 1111112 1111113 0
2 2222221 0 0 0
3 3333331 3333332 0 0
with this statement:
SELECT p.Id,
CASE WHEN (ROW_NUMBER() OVER(PARTITION BY p.Id ORDER BY p.Id)) = 1 THEN p.ClId ELSE 0 END AS 'Client 1',
CASE WHEN (ROW_NUMBER() OVER(PARTITION BY p.Id ORDER BY p.Id)) = 2 THEN p.ClId ELSE 0 END AS 'Client 2',
CASE WHEN (ROW_NUMBER() OVER(PARTITION BY p.Id ORDER BY p.Id)) = 3 THEN p.ClId ELSE 0 END AS 'Client 3',
CASE WHEN (ROW_NUMBER() OVER(PARTITION BY p.Id ORDER BY p.Id)) = 4 THEN p.ClId ELSE 0 END AS 'Client 4'
FROM Prop_Cl p
But I get this result:
ID CLIENT 1 CLIENT 2 CLIENT 3 CLIENT 4
1 1111111 0 0 0
1 0 1111112 0 0
1 0 0 1111113 0
2 2222221 0 0 0
3 3333331 0 0 0
3 0 3333332 0 0
I can't use PIVOT function because of my Sql Server realisation.
There are maximum 4 clients in each ID.
Any ideas?
SQL Fiddle
I would change the syntax slightly to use an aggregate function and a subquery similar to:
select id,
max(case when seq = 1 then ClId else 0 end) Client1,
max(case when seq = 2 then ClId else 0 end) Client2,
max(case when seq = 3 then ClId else 0 end) Client3,
max(case when seq = 4 then ClId else 0 end) Client4
from
(
select Id, ClId,
ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Id) seq
from Prop_Cl
) s
group by id;
See SQL Fiddle with Demo