Pivoting table throws error: "only simple column names allowed here" - sql

I have a dataset, example:
Master table:
x------x----------x-----------------x-----------x--------x
| Chasisnumber | Messagename | PaintML | Result
x------x--------------------x-------x-----------x--------x
| A123 | Message1 | 10 | OK
| A123 | Message2 | 70 | NOK
B123 | Message1 | 10 | OK
B123 | Message2 | 50 | OK
x------x--------------------x-------x-----------x--------x
I want to get:
Chasisnumber, PaintML-Message1 ,Result-Message1,PaintML-Message2,Result-Message2
| A123 | 10 | OK | 70 | NOK
B123 | 10 | OK | 50 | OK
This can be done with pivot. Can someone help me out?
Example:
select *
from
(Select chasis, message, paint, result
from paint_b) src
pivot
(
src.Paint, src.result for src.MessageName in
('Message1',
'Message2')
) piv;

Like i mentioned, a cross tab seems like a better idea:
--Sample Data
WITH VTE AS(
SELECT V.Chasisnumber,
V.Messagename,
V.PaintML,
V.Result
FROM (VALUES('A123','Message1',10,'OK'),
('A123','Message2',70,'NOK'),
('B123','Message1',10,'OK'),
('B123','Message2',50,'OK')) V(Chasisnumber,Messagename,PaintML,Result))
--Solution
SELECT V.Chasisnumber,
MAX(CASE V.Messagename WHEN 'Message1' THEN V.PaintML END) AS PaintML1,
MAX(CASE V.Messagename WHEN 'Message2' THEN V.PaintML END) AS PaintML2,
MAX(CASE V.Messagename WHEN 'Message1' THEN V.Result END) AS Result1,
MAX(CASE V.Messagename WHEN 'Message2' THEN V.Result END) AS Result2
FROM VTE V
GROUP BY V.Chasisnumber;

The following query should do what you want:
create table #tmp (Chasisnumber VARCHAR(10),Messagename VARCHAR(25), PaintML INT,Result VARCHAR(10))
insert into #tmp values
('A123','Message1',10,'OK'),
('A123','Message2',70,'NOK'),
('B123','Message1',10,'OK'),
('B123','Message2',50,'OK')
select * from (
select Chasisnumber, unpiv.val, unpiv.col+'-'+unpiv.Messagename as col
from (select Chasisnumber,Messagename,cast(PaintML as varchar(10)) PaintML,Result from #tmp) tmp
unpivot (val for col in (PaintML,Result)) unpiv )a
pivot (max(val) for col in ([PaintML-Message1],[Result-Message1],[PaintML-Message2],[Result-Message2])) piv

Related

Oracle Pivot Based on Multiple Columns

I am fairly new to Pivoting and trying to Pivot based on two columns.
Data I have:
Data I want to Achieve after pivoting:
My Query which is Flawed:
select *
from
(
select ISSUEID,ANSWER, ANSWERCOMMENT,QUESTION ,QUESTIONID
from issue_survey
WHERE ISSUEID = 6877
) d
pivot
(
max(QUESTION)
for QUESTIONID in (1 QUESTION1,2 QUESTION2, 3 QUESTION3)
) piv;
The Results I am getting with this flawed query:
Any suggestions with this are appreciated. Thank you!
Try:
select *
from
(
select ISSUEID,ANSWER, ANSWERCOMMENT,QUESTION ,
rownum rn
from issue_survey
WHERE ISSUEID = 6877
) d
pivot
(
max(QUESTION) as question, max(Answer) as answer,
max( ANSWERCOMMENT ) as ANSWERCOMMENT
for rn in ( 1 ,2 , 3 )
) piv;
Demo: http://www.sqlfiddle.com/#!4/e5aba7/6
| ISSUEID | 1_QUESTION | 1_ANSWER | 1_ANSWERCOMMENT | 2_QUESTION | 2_ANSWER | 2_ANSWERCOMMENT | 3_QUESTION | 3_ANSWER | 3_ANSWERCOMMENT |
|---------|-------------------|----------|-----------------|--------------|----------|-----------------|-----------------|----------|-----------------|
| 6877 | Do you wanna wait | YES | TEST1 | How about it | Its okay | TEST2 | Sample question | (null) | TEST3

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

SQL: Pick highest and lowest value (int) from one row

I am looking for a way to pick the highest and lowest value (integer) from a single row in table. There are 4 columns that i need to compare together and get highest and lowest number there is.
The table looks something like this...
id | name | col_to_compare1 | col_to_compare2 | col_to_compare3 | col_to_compare4
1 | John | 5 | 5 | 2 | 1
2 | Peter | 3 | 2 | 4 | 1
3 | Josh | 3 | 5 | 1 | 3
Can you help me, please? Thanks!
You can do this using CROSS APPLY and the VALUES clause. Use VALUES to group all your compared columns and then select the max.
SELECT
MAX(d.data1) as MaxOfColumns
,MIN(d.data1) as MinOfColumns
,a.id
,a.name
FROM YOURTABLE as a
CROSS APPLY (
VALUES(a.col_to_compare1)
,(a.col_to_compare2)
,(a. col_to_compare3)
,(a.col_to_compare4)
,(a. col_to_compare5)
) as d(data1) --Name the Column
GROUP BY a.id
,a.name
Assuming you are looking for min/max per row
Declare #YourTable table (id int,name varchar(50),col_to_compare1 int,col_to_compare2 int,col_to_compare3 int,col_to_compare4 int)
Insert Into #YourTable values
(1,'John',5,5,2,1),
(2,'Peter',3,2,4,1),
(3,'Josh',3,5,1,3)
Select A.ID
,A.Name
,MinVal = min(B.N)
,MaxVal = max(B.N)
From #YourTable A
Cross Apply (Select N From (values(a.col_to_compare1),(a.col_to_compare2),(a.col_to_compare3),(a.col_to_compare4)) N(N) ) B
Group By A.ID,A.Name
Returns
ID Name MinVal MaxVal
1 John 1 5
3 Josh 1 5
2 Peter 1 4
These solutions keep the current rows and add additional columns of min/max.
select *
from t cross apply
(select min(col) as min_col
,max(col) as max_col
from (
values
(t.col_to_compare1)
,(t.col_to_compare2)
,(t.col_to_compare3)
,(t.col_to_compare4)
) c(col)
) c
OR
select *
,cast ('' as xml).value ('min ((sql:column("t.col_to_compare1"),sql:column("t.col_to_compare2"),sql:column("t.col_to_compare3"),sql:column("t.col_to_compare4")))','int') as min_col
,cast ('' as xml).value ('max ((sql:column("t.col_to_compare1"),sql:column("t.col_to_compare2"),sql:column("t.col_to_compare3"),sql:column("t.col_to_compare4")))','int') as max_col
from t
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| id | name | col_to_compare1 | col_to_compare2 | col_to_compare3 | col_to_compare4 | min_col | max_col |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 1 | John | 5 | 5 | 2 | 1 | 1 | 5 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 2 | Peter | 3 | 2 | 4 | 1 | 1 | 4 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 3 | Josh | 3 | 5 | 1 | 3 | 1 | 5 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
A way to do this is to "break" apart the data
declare #table table (id int, name varchar(10), col1 int, col2 int, col3 int, col4 int)
insert into #table values (1 , 'John' , 5 , 5 , 2 , 1)
insert into #table values (2 , 'Peter' , 3 , 2 , 4 , 1)
insert into #table values (3 , 'Josh' , 3 , 5 , 1 , 3)
;with stretch as
(
select id, col1 as col from #table
union all
select id, col2 as col from #table
union all
select id, col3 as col from #table
union all
select id, col4 as col from #table
)
select
t.id,
t.name,
agg.MinCol,
agg.MaxCol
from #table t
inner join
(
select
id, min(col) as MinCol, max(col) as MaxCol
from stretch
group by id
) agg
on t.id = agg.id
Seems simple enough
SELECT min(col1), max(col1), min(col2), max(col2), min(col3), max(col3), min(col4), max(col4) FROM table
Gives you the Min and Max for each column.
Following OP's comment, I believe he may be looking for a min/max grouped by the person being queried against.
So that would be:
SELECT name, min(col1), max(col1), min(col2), max(col2), min(col3), max(col3), min(col4), max(col4) FROM table GROUP BY name

SQL Pivot for multiple columns [duplicate]

This question already has answers here:
SQL Server Pivot Table with multiple column aggregates
(3 answers)
Closed 7 years ago.
I have the below table 'EmpTemp':
Name | Category | Value1 | Value 2
John | Cat1 | 11 | 33
John | Cat2 | 22 | 44
I would like to have the below output for the table:
Name | Cat1_Value1 | Cat2_Value1 | Cat1_Value2 | Cat2_Value2
John | 11 | 11 | 33 | 44
I guess this give a basic idea of what kind of transformation i'm expecting. I have tried the below query that gives me a partial solution:
SELECT
Name,
Cat1 AS 'Cat1_Value1',
Cat2 AS 'Cat2_Value1',
FROM EmpTemp AS T
PIVOT
(
MAX(Value1)
FOR Category IN (Cat1, Cat2)
) AS PVT
The above query gives me the following result:
Name | Cat1_Value1 | Cat2_Value1
John | 11 | 11
I am stuck how can I extend this pivot query. Any help is appreciated in advance.
Here is a way to do it using Pivot.
First you need to unpivot the data then do the pivot
;with cte as
(
select name,val,col_name from yourtable
cross apply (values
(value1,Category+'value1'),
(value2,Category+'value2')
) cs(val,col_name)
)
select * from cte
pivot(max(val) for col_name in([Cat1value1],
[Cat1value2],
[Cat2value1],
[Cat2value2]
)) p
Here is a simple way of doing it without using the pivot syntax:
select name,
max(case when category = 'Cat1' then value1 end) as cat1_value1,
max(case when category = 'Cat2' then value1 end) as cat2_value1,
max(case when category = 'Cat1' then value2 end) as cat1_value2,
max(case when category = 'Cat2' then value2 end) as cat2_value2
from EmpTemp
group by name

How to transform rows into column? [duplicate]

This question already has answers here:
Convert Rows to columns using 'Pivot' in SQL Server
(9 answers)
Closed 7 years ago.
I have a table like this and there are only two feature for all user in this table
+-------+---------+-----------+----------+
| User | Feature | StartDate | EndDate |
+-------+---------+-----------+----------+
| Peter | F1 | 2015/1/1 | 2015/2/1 |
| Peter | F2 | 2015/3/1 | 2015/4/1 |
| John | F1 | 2015/5/1 | 2015/6/1 |
| John | F2 | 2015/7/1 | 2015/8/1 |
+-------+---------+-----------+----------+
I want to transform to
+-------+--------------+------------+--------------+------------+
| User | F1_StartDate | F1_EndDate | F2_StartDate | F2_EndDate |
+-------+--------------+------------+--------------+------------+
| Peter | 2015/1/1 | 2015/2/1 | 2015/3/1 | 2015/4/1 |
| John | 2015/5/1 | 2015/6/1 | 2015/7/1 | 2015/8/1 |
+-------+--------------+------------+--------------+------------+
If you are using SQL Server 2005 or up by any chance, PIVOT is what you are looking for.
The best general way to perform this sort of operation is a simple group by statement. This should work across all major ODBMS:
select user,
max(case when feature='F1' then StartDate else null end) F1_StartDate,
max(case when feature='F1' then EndDate else null end) F1_EndDate,
max(case when feature='F2' then StartDate else null end) F2_StartDate,
max(case when feature='F2' then EndDate else null end) F2_EndDate
from table
group by user
Note: as mentioned in the comments, this is often bad practice, as depending on your needs, it can make the data harder to work with. However, there are cases where it makes sense, when you have a small, limited number of values.
This is a bit of a hack with a CTE:
;WITH CTE AS (
SELECT [User], [Feature] + '_StartDate' AS [Type], StartDate AS [Date]
FROM Table1
UNION ALL
SELECT [User], [Feature] + '_EndDate' AS [Type], EndDate AS [Date]
FROM Table1)
SELECT * FROM CTE
PIVOT(MAX([Date]) FOR [Type] IN ([F1_StartDate],[F2_StartDate], [F1_EndDate], [F2_EndDate])) PIV
Use UNPIVOT & PIVOT like this:
Test data:
DECLARE #t table
(User1 varchar(20),Feature char(2),StartDate date,EndDate date)
INSERT #t values
('Pete','F1','2015/1/1 ','2015/2/1'),
('Pete','F2','2015/3/1 ','2015/4/1'),
('John','F1','2015/5/1 ','2015/6/1'),
('John','F2','2015/7/1 ','2015/8/1')
Query:
;WITH CTE AS
(
SELECT User1, date1, Feature + '_' + Seq cat
FROM #t as p
UNPIVOT
(date1 FOR Seq IN
([StartDate], [EndDate]) ) AS unpvt
)
SELECT * FROM CTE
PIVOT
(MIN(date1)
FOR cat
IN ([F1_StartDate],[F1_EndDate],[F2_StartDate],[F2_EndDate])
) as p
Result:
User1 F1_StartDate F1_EndDate F2_StartDate F2_EndDate
John 2015-05-01 2015-06-01 2015-07-01 2015-08-01
Pete 2015-01-01 2015-02-01 2015-03-01 2015-04-01