Can we use two pivot in a single query? - sql

Declare #tbl table(SessionId varchar(max),ItemID_FK int,Roles varchar(max))
insert into #tbl
select distinct SessionID,ItemID_FK,Roles from tbl_Answers where ID_FK=#ID
SELECT ItemID_PK,ItemName,case when [Role1] IS NULL then 0 else [Role1] end as [Role1],
case when [Role2] IS NULL then 0 else [Role2] end as [Role2],
case when [Role3] IS NULL then 0 else [Role3] end as [Role3],
case when [Role4] IS NULL then 0 else [Role4] end as [Role4],
case when [Role5] IS NULL then 0 else [Role5] end as [Role5],
case when [Role6] IS NULL then 0 else [Role6] end as [Role6],
case when [Role7] IS NULL then 0 else [Role7] end as [Role7]
FROM
(
select items.ItemID_PK ,items.ItemName,count(ans.Roles) as cntRoles,ans.Roles from tbl_Items items Full join #tbl ans
on items.ItemID_PK=ans.ItemID_FK where items.ID_FK= #ID group by Roles,ItemName , items.ItemID_PK
) d PIVOT
(
max(cntRoles)
FOR Roles IN ([Role1],[Role2],[Role3],[Role4],[Role5],[Role6],[Role7])
) AS pvt order by ItemID_PK
I used the above stored procedure and got the output as
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
|ItemID_PK |ItemName |Role1|Role2|Role3|Role4|Role5|Role6|Role7|
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
| 111 | aaaaa | 6 | 5 | 0 | 5 | 1 | 4 | 2 |
| 222 | bbbbb | 1 | 1 | 7 | 2 | 0 | 3 | 1 |
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
I have another query and got the following output.
Select Category,Answer,Roles
from tbl_Answers where ID_FK=1 and Category='OtherText'
+---------+--------+-----+
|Category |Answer |Roles|
+---------+--------+-----+
|OtherText| xxx |Role1|
|OtherText| yyy |Role1|
|OtherText| zzz |Role2|
|OtherText| xzx |Role3|
+---------+--------+-----+
I need to merge the above two outputs to generate the result as
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
|ItemID_PK |ItemName |Role1|Role2|Role3|Role4|Role5|Role6|Role7|
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
| 111 | aaaaa | 6 | 5 | 0 | 5 | 1 | 4 | 2 |
| 222 | bbbbb | 1 | 1 | 7 | 2 | 0 | 3 | 1 |
| Null | Othertext| xxx | zzz | xzx | saa | | xxx | |
| Null | Othertext| yyy | | | zxz | | | |
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
How to combine the second query to the first pivot query to get the result mentioned above?
Thanks in advance.

You could just use UNION ALL to combine the two results, you would need to convert the roles from the top query from int to VARCHAR though:
DECLARE #ID INT = 1;
WITH Ans AS
( SELECT DISTINCT SessionID,ItemID_FK,Roles
FROM tbl_Answers
WHERE ID_FK = #ID
), PivotData AS
( SELECT items.ItemID_PK,
items.ItemName,
cntRoles = COUNT(ans.Roles),
ans.Roles
FROM tbl_Items items
FULL JOIN Ans
ON items.ItemID_PK = ans.ItemID_FK
WHERE items.ID_FK = #ID
GROUP BY Roles,ItemName, items.ItemID_PK
)
SELECT ItemID_PK,
ItemName,
[Role1] = CAST(ISNULL([Role1], 0) AS VARCHAR(255)),
[Role2] = CAST(ISNULL([Role2], 0) AS VARCHAR(255)),
[Role3] = CAST(ISNULL([Role3], 0) AS VARCHAR(255)),
[Role4] = CAST(ISNULL([Role4], 0) AS VARCHAR(255)),
[Role5] = CAST(ISNULL([Role5], 0) AS VARCHAR(255)),
[Role6] = CAST(ISNULL([Role6], 0) AS VARCHAR(255)),
[Role7] = CAST(ISNULL([Role7], 0) AS VARCHAR(255))
FROM PivotData
PIVOT
( MAX(cntRoles)
FOR Roles IN ([Role1],[Role2],[Role3],[Role4],[Role5],[Role6],[Role7])
) AS pvt
UNION ALL
SELECT ItemID_PK = NULL,
ItemName = Category,
[Role1] = ISNULL([Role1], ''),
[Role2] = ISNULL([Role2], ''),
[Role3] = ISNULL([Role3], ''),
[Role4] = ISNULL([Role4], ''),
[Role5] = ISNULL([Role5], ''),
[Role6] = ISNULL([Role6], ''),
[Role7] = ISNULL([Role7], '')
FROM ( SELECT Category,Answer,Roles
FROM tbl_Answers
WHERE ID_FK = 1
AND Category = 'OtherText'
) pd
PIVOT
( MAX(Answer)
FOR Roles IN ([Role1],[Role2],[Role3],[Role4],[Role5],[Role6],[Role7])
) AS pvt
ORDER BY ItemID_PK;
Note, I have changed this expression:
case when [Role2] IS NULL then 0 else [Role2] end
to
ISNULL([Role2], 0)
as the effect is the same, but it is much shorter. I have also removed the table variable, and just placed the same query within a Common Table Expression, as it seems redundant to fill a table variable then only refer to it once. You are removing the use of indexes and statistics on the actual table and gaining no benefit for it.

Use a UNION to combine the two pivots.

Related

Postgres SQL aggregates query in BigQuery?

For Below Postgres SQL query, I do use PIVOT in BigQuery, beside PIVOT, any other method for such query in BigQuery?
-- Postgres SQL --
SELECT
Apple,
Orange,
Lemon,
CASE WHEN Apple >= 50 THEN 1 ELSE 0 END AS Apple50
CASE WHEN Orange >= 50 THEN 1 ELSE 0 END AS Orange50
CASE WHEN Lemon >= 50 THEN 1 ELSE 0 END AS Lemon50
FROM (
SELECT td.timestamp,
COALESCE(MAX(td.value) FILTER (WHERE attribute_id = 16), 0) as Apple,
COALESCE(MAX(td.value) FILTER (WHERE attribute_id = 17), 0) as Orange,
COALESCE(MAX(td.value) FILTER (WHERE attribute_id = 18), 0) as Lemon
FROM TableData td
WHERE td.attribute_id IN (16, 17, 18)
GROUP BY td.timestamp
ORDER BY timestamp;
) AS td2
-- My attempt BigQuery Query --
SELECT
value_16 as Apple,
value_17 as Orange,
value_18 as Lemon,
CASE WHEN value_16 >= 50 THEN 1 ELSE 0 END as Apple50
CASE WHEN value_17 >= 50 THEN 1 ELSE 0 END as Orange50
CASE WHEN value_18 >= 50 THEN 1 ELSE 0 END AS Lemon50
FROM (
SELECT * FROM(
SELECT
timestamp,
attribute_id,
value
FROM `PROJECT_ID.DB_NAME.FRUITS` as td
WHERE td.attribute_id IN (16,17,18)
)PIVOT
(
MAX(value) as value
FOR attribute_id IN (16,17,18)
)
)as td2
Below is the sample relation of the table.
-- TableData --
attribute_id | value | timestamp |
--------------+-----------+------------+
17 | 100 | 1618822794 |
17 | 100 | 1618822861 |
16 | 50 | 1618822794 |
16 | 50 | 1618822861 |
-- TableAttribute --
id | name |
--------------+----------+
16 | Apple |
17 | Orange |
18 | Lemon |
-- Expected Result --
timestamp | Apple | Orange | Lemon | Apple50 | Orange50 | Lemon50 |
--------------+---------+--------+-------+---------+----------+---------+
1618822794 | 50 | 100 | 0 | 1 | 1 | 0
1618822861 | 50 | 100 | 0 | 1 | 1 | 0
Pivot is likely the best way to achieve what you're wanting. Consider the following approach though as it might be simpler to manage:
with aggregate_data as (
select td.timestamp
, ta.name
, td.value as value
from TableData td
full outer join TableAttribute ta
on td.attribute_id = ta.id
)
select timestamp
, value_Apple as Apple
, value_Orange as Orange
, value_Lemon as Lemon
, _50_Apple as Apple50
, _50_Orange as Orange50
, _50_Lemon as Lemon50
from aggregate_data
pivot(max(value) value, max(case when value >=50 then 1 else 0 end) _50 for name in ('Apple', 'Orange', 'Lemon'))
where timestamp is not null

SQL Server recursive query to show path of parents

I am working with SQL Server statements and have one table like:
| item | value | parentItem |
+------+-------+------------+
| 1 | 2test | 2 |
| 2 | 3test | 3 |
| 3 | 4test | 4 |
| 5 | 1test | 1 |
| 6 | 3test | 3 |
| 7 | 2test | 2 |
And I would like to get the below result using a SQL Server statement:
| item1 | value1 |
+-------+--------------------------+
| 1 | /4test/3test/2test |
| 2 | /4test/3test |
| 3 | /4test |
| 5 | /4test/3test/2test/1test |
| 6 | /4test/3test |
| 7 | /4test/3test/2test |
I didn't figure out the correct SQL to get all the values for all the ids according to parentItem.
I have tried this SQL :
with all_path as
(
select item, value, parentItem
from table
union all
select a.item, a.value, a.parentItem
from table a, all_path b
where a.item = b.parentItem
)
select
item as item1,
stuff(select '/' + value
from all_path
order by item asc
for xml path ('')), 1, 0, '') as value1
from
all_path
But got the "value1" column in result like
/4test/4test/4test/3test/3test/3test/3test/2test/2test/2test/2test
Could you please help me with that? Thanks a lot.
based on the expected output you gave, use the recursive part to concatenate the value
;with yourTable as (
select item, value, parentItem
from (values
(1,'2test',2)
,(2,'3test',3)
,(3,'4test',4)
,(5,'1test',1)
,(6,'3test',3)
,(7,'2test',2)
)x (item,value,parentItem)
)
, DoRecursivePart as (
select 1 as Pos, item, convert(varchar(max),value) value, parentItem
from yourTable
union all
select drp.pos +1, drp.item, convert(varchar(max), yt.value + '/' + drp.value), yt.parentItem
from yourTable yt
inner join DoRecursivePart drp on drp.parentItem = yt.item
)
select drp.item, '/' + drp.value
from DoRecursivePart drp
inner join (select item, max(pos) mpos
from DoRecursivePart
group by item) [filter] on [filter].item = drp.item and [filter].mpos = drp.Pos
order by item
gives
item value
----------- ------------------
1 /4test/3test/2test
2 /4test/3test
3 /4test
5 /4test/3test/2test/1test
6 /4test/3test
7 /4test/3test/2test
Here's the sample data
drop table if exists dbo.test_table;
go
create table dbo.test_table(
item int not null,
[value] varchar(100) not null,
parentItem int not null);
insert dbo.test_table values
(1,'test1',2),
(2,'test2',3),
(3,'test3',4),
(5,'test4',1),
(6,'test5',3),
(7,'test6',2);
Here's the query
;with recur_cte(item, [value], parentItem, h_level) as (
select item, [value], parentItem, 1
from dbo.test_table tt
union all
select rc.item, tt.[value], tt.parentItem, rc.h_level+1
from dbo.test_table tt join recur_cte rc on tt.item=rc.parentItem)
select rc.item,
stuff((select '/' + cast(parentItem as varchar)
from recur_cte c2
where rc.item = c2.item
order by h_level desc FOR XML PATH('')), 1, 1, '') [value1]
from recur_cte rc
group by item;
Here's the results
item value1
1 4/3/2
2 4/3
3 4
5 4/3/2/1
6 4/3
7 4/3/2

T-SQL If condition inside select

I have a condition where there are multiple scenarios and I need to check each scenario. I need to concatenate the result if two or more cases matches.
What I thought was , using CASE statement but i am unable to concatenate multiple scenarios using CASE statement.
So what I am doing now is , using if statement . Below is the query i did:
declare #result varchar(200)
set #result=''
;with cte as
(
select * from table 1
)
if(column 1=5) set #result=#result+'case1'
if(column 2=6) set #result=#result+'case2'
if(column 3=7) set #result=#result+'case3'
if(column 4=10) set #result=#result+'case4'
select *,#result from cte
So here, i need to use Select statement right after CTE but i cannot use IF/ELSE statement in select statement . But also i cannot use the CASE Statement with variable concatenate.
So the result should be like :
id | column 1 | column 2| column 3| column 4| Result
-----------------------------------------------------
3 | 5 | 6 | 6 | 9 | case1;`case2
4 | 4 | 7 | 7 | 10 | case2
5 | 5 | 6 | 6 | 10 | case1;`case2; case4
6 | 5 | 6 | 7 | 10 | case2;case2;case3;case4
7 | 4 | 5 | 6 | 3 | No Result
Anyone could help me to complete this ?
You can concatenate multiple CASEs in a single SELECT:
SELECT
CASE WHEN 1=1 THEN 'Case1' ELSE '' END
+ CASE WHEN 2=2 THEN 'Case2' ELSE '' END
+ CASE ...
EDIT based on comments: If, as you say, your broken code using IF really would solve your issue, then there is no issue, since the if statements you used don't need to be in the SELECT at all. You could simply do this:
declare #result varchar(200)
set #result=''
if(1=1) set #result=#result+'case1'
if(2=2) set #result=#result+'case2'
;with cte as
(
select * from table 1
)
select *,#result from cte
EDIT based on the update to original question:
So my first solution is correct. The additional details in your question allow me to make it a little more clear. By the way you can't use a variable at all for this:
;with cte as
(
select * ,
CASE WHEN column1=5 THEN 'case1' ELSE '' END +
CASE WHEN column2=6 THEN 'case2' ELSE '' END +
CASE WHEN column3=7 THEN 'case3' ELSE '' END +
CASE WHEN column4=10 THEN 'case4' ELSE '' END AS result
from table 1
)
select *,
CASE WHEN result='' THEN 'No Result Found' ELSE result END as result
from cte
Note that if you need to separate the values with semi-colons (as shown in your question) you can put a semi-colon before each value (example ';case3'), and in the final SELECT, use STUFF() to remove the first semi-colon.
;WITH cte AS
(
SELECT * FROM table1
)
SELECT *,(
CASE
WHEN Column1 <> 5 AND Column2 <> 6 AND Column3 <> 7 AND Column4 <> 10 THEN 'No Result'
ELSE
CASE WHEN Column1 = 5 THEN 'Case 1;' ELSE '' END +
CASE WHEN Column2 = 6 THEN 'Case 2;' ELSE '' END +
CASE WHEN Column3 = 7 THEN 'Case 3;' ELSE '' END +
CASE WHEN Column4 = 10 THEN 'Case 4;' ELSE '' END
END
)Result
FROM cte
| id | Column1 | Column2 | Column3 | Column4 | Result |
|----|---------|---------|---------|---------|------------------------------|
| 3 | 5 | 6 | 6 | 9 | Case 1;Case 2; |
| 4 | 4 | 7 | 7 | 10 | Case 3;Case 4; |
| 5 | 5 | 6 | 6 | 10 | Case 1;Case 2;Case 4; |
| 6 | 5 | 6 | 7 | 10 | Case 1;Case 2;Case 3;Case 4; |
| 7 | 4 | 5 | 6 | 9 | No Result |
Check SQL Fiddle

How to combine two records?

I have a table that looks like this
ID | Value | Type
-----------------------
1 | 50 | Travel
1 | 25 | Non-Travel
1 | 25 | Non-Travel
1 | 25 | Non-Travel
1 | 50 | Travel
1 | 75 | Non-Travel
How can I query this to make the output rearrange to this?
ID | Travel | Non-Travel
------------------------
1 | 100 | 150
The query to actually get the first table I posted has many joins and a BIT column in one of the tables where 0 or NULL is non-travel and 1 is travel. So I have something like this:
SELECT
[ID]
,CASE WHEN [IsTravel] IN (0,NULL) THEN ISNULL(SUM([VALUE]),0) END AS 'NonTravel'
,CASE WHEN [IsTravel] = 1 THEN ISNULL(SUM([VALUE]),0) END AS 'Travel'
FROM
...
However the result ends up showing this
ID | Travel | Non-Travel
------------------------
1 | 100 | NULL
1 | NULL | 150
How can I edit my query to combine the rows to show this result?
ID | Travel | Non-Travel
------------------------
1 | 100 | 150
Thanks in advance.
select ID,
SUM(CASE WHEN Type = 'Travel' THEn value ELSE 0 END) [Travel],
SUM(CASE WHEN Type = 'NonTravel' THEn value ELSE 0 END) [NonTravel]
from #Table1
GROUP BY ID
You need to wrap each of your conditionals in aggregations such as MAX(), and GROUP BY other columns to roll up the values and remove the NULL. Something like this:
SELECT
[ID]
,MAX(CASE WHEN [IsTravel] IN (0,NULL) THEN ISNULL(SUM([VALUE]),0) END) AS 'NonTravel'
,MAX(CASE WHEN [IsTravel] = 1 THEN ISNULL(SUM([VALUE]),0) END) AS 'Travel'
FROM
...
GROUP BY [ID]
If the logic gets too cluttered or confusing (don't know without seeing your whole current query) then drop those results into a temp table or CTE and do the simple MAX() and GROUP BY from there.
You can use pivot as below:
Select * from (
Select Id, [Value], [Type] from yourtable ) a
pivot (sum([Value]) for [Type] in ([Travel],[Non-Travel]) ) p
Output as below:
+----+------------+--------+
| Id | Non-Travel | Travel |
+----+------------+--------+
| 1 | 150 | 100 |
+----+------------+--------+
For dynamic list of Travel types you can do dynamic query as below:
Declare #cols1 varchar(max)
Declare #query nvarchar(max)
Select #cols1 = stuff((select Distinct ','+QuoteName([Type]) from #traveldata for xml path('')),1,1,'')
Set #query = ' Select * from (
Select Id, [Value], [Type] from #traveldata ) a
pivot (sum([Value]) for [Type] in (' + #cols1 + ') ) p '
Exec sp_executesql #query

Incorrect syntax near '1'. Expecting ID, QUOTED_ID, or '.' Error with SQL Pivot

I have a table with stats for universities that looks like this:
StatID | UniversityID | StatValue
1 | 1 | 100
2 | 1 | 90
3 | 1 | 80
1 | 2 | 50
2 | 2 | 55
I'd like a query to return something like this:
(Rows are StatIDs, Columns are UniversityIDs)
StatID | 1 | 2 | 3
1 | 100 | 50 | NULL
2 | 90 | 55 | NULL
3 | 80 | NULL | NULL
Here's my query:
SELECT StatID, 1, 2, 3
FROM
(SELECT StatID, UniversityID, StatValue FROM #table) up
PIVOT
(MAX(StatValue) FOR UniversityID IN (1, 2, 3)) AS pvt
ORDER BY StatisticID;
I get an error on FOR UniversityID IN (1, saying:
Incorrect syntax near '1'. Expecting ID, QUOTED_ID, or '.'.
What am I doing wrong? Does it have something to do with an int as a column header?
I will be using this with ~260,000 rows (~300 columns and ~3,000 rows)
You have the synatx for the IN wrong:
SELECT StatisticID, 1, 2, 3
FROM
(SELECT StatisticID, UniversityID, Value
FROM #table) up
PIVOT
(MAX(Value) FOR UniversityID IN ([1], [2], [3])) AS pvt
ORDER BY StatisticID;
Given what you want to produce as output, I am not sure you need to use the PIVOT operator.
You can get pretty close to the output you have above with the following query:
SELECT s.StatID
,UniversityID1 = SUM(CASE WHEN UniversityID = 1 THEN StatValue ELSE NULL END)
,UniversityID2 = SUM(CASE WHEN UniversityID = 2 THEN StatValue ELSE NULL END)
,UniversityID3 = SUM(CASE WHEN UniversityID = 3 THEN StatValue ELSE NULL END)
FROM StatsTable s
GROUP BY s.StatID
which will produce
StatID | UniversityID1 | UniversityID2 | UniversityID3
1 | 100 | 50 | NULL
2 | 90 | 55 | NULL
3 | 80 | NULL | NULL
It doesn't have the last row with StatID = 4, but I am not sure what value that is providing to you anyway as all the values are uniformly NULL and there is no StatID = 4 data in your input table.
If you really want the PIVOT syntax, here it is:
SELECT StatID
,UniversityID1 = [1]
,UniversityID2 = [2]
,UniversityID3 = [3]
FROM
(SELECT StatID, UniversityID, StatValue FROM #table) up
PIVOT
(SUM(StatValue) FOR UniversityID IN ([1], [2], [3])) AS pvt
ORDER BY StatID;
(You were missing your square brackets [])