I have an sql query "Select * from tablename" whose output is
col1 col2
A 1
B 2
C 3
I want to modify the above output to below as following
A B C
1 2 3
Please let me know how could I achieve this
You will need to perform a PIVOT. There are two ways to do this with PIVOT, either a Static Pivot where you code the columns to transform or a Dynamic Pivot which determines the columns at execution.
Static Pivot:
SELECT *
FROM
(
SELECT col1, col2
FROM yourTable
) x
PIVOT
(
min(col2)
for col1 in ([A], [B], [C])
)p
See SQL Fiddle with Demo
Dynamic Pivot:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(col1)
from t1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + ' from
(
select col1, col2
from t1
) x
pivot
(
min(col2)
for col1 in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
If you do not want to use the PIVOT function, then you can perform a similar type of query with CASE statements:
select
SUM(CASE WHEN col1 = 'A' THEN col2 END) as A,
SUM(CASE WHEN col1 = 'B' THEN col2 END) as B,
SUM(CASE WHEN col1 = 'C' THEN col2 END) as C
FROM t1
See SQL Fiddle with Demo
You want to use PIVOT or COALESCE in sql.Here is a nice examples how to converting rows to columns.
Five methods converting rows to columns
.
Related
I have a table like this:
id
cols
val
1
date
01-01-01
1
name
abc
1
flag
True
1
end_date
null
2
date
01-01-02
2
name
abcd
2
flag
False
2
end_date
01-01-03
And I need to create a table that looks like
id
date
name
flag
end_date
1
01-01-01
abc
True
null
2
01-01-02
abcd
False
01-01-03
I can use select/with only. No functions or create/update
Thanks for your help
An interesting technique to remember is that you can use MAX(CASE WHEN... on text values to achieve results like this:
SELECT id
, MAX(CASE WHEN cols = 'date' THEN val ELSE NULL END) AS dt
, MAX(CASE WHEN cols = 'name' THEN val ELSE NULL END) AS nm
, MAX(CASE WHEN cols = 'flag' THEN val ELSE NULL END) AS flag
, MAX(CASE WHEN cols = 'end_date' THEN val ELSE NULL END) AS end_date
FROM #t
GROUP BY id
I've tried to avoid using SQL keywords as column names, so you have dt and nm instead of date and name.
You will probably want to apply some type casting as well to get it into a more usable format.
dbfiddle.uk
You can use the PIVOT and UNPIVOT relational operators to change a table-valued expression into another table. PIVOT rotates a table-valued expression by turning the unique values from one column in the expression into multiple columns in the output.
https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-ver15
U can use this query pivot it table value
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.cols)
FROM yourtablename c
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Id, ' + #cols + ' from
(
select id
,val
from yourtablename
) x
pivot
(
val
for cols in (' + #cols + ')
) p '
execute(#query);
I have 25 (numeric) columns in one table in MSSQL and I need select lowest value, but not NULL or 0 value.
Columns are named like "%_price" (aaa_price, bbb_price, ccc_price...).
Some columns contains 0 or NULL value.
Example:
table (aaa_price, bbb_price, ccc_price, ddd_price, eee_price, fff_price)
value (NULL, 0, 324.23, 162.50, NULL, 1729.72 )
Right result:
162.50
I can use some "brute force" method like:
SELECT CASE
WHEN Col1 <= Col2 AND Col1 <= Col3 AND Col1 <= Col4 AND Col1 <= Col5 THEN Col1
WHEN Col2 <= Col3 AND Col2 <= Col4 AND Col2 <= Col5 THEN Col2
WHEN Col3 <= Col4 AND Col3 <= Col5 THEN Col3
WHEN Col4 <= Col5 THEN Col4
ELSE Col5
END AS [Min Value] FROM [Your Table]
But its insane with 25 columns... is there any better solution?
Thank You!
Cross apply can be good option in this case:
select
*
from
myTable
cross apply (select
minVal = min(val)
from (
values (aaa_price),(bbb_price),(...)
) t(val) where val > 0
) q
Edit:
You have to use dynamic SQL if you want to get column names from INFORMATION_SCHEMA.COLUMNS table.
declare #sql varchar(8000)
declare #cols varchar(8000)
select #cols =
stuff((
SELECT
',(' + COLUMN_NAME + ')'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'mytable'
AND TABLE_SCHEMA='dbo'
AND COLUMN_NAME LIKE '%price'
for xml path('')
), 1, 1, '')
set #sql = 'select
*
from
mytable
cross apply (select
minVal = min(val)
from (
values ' + #cols + '
) t(val) where val > 0
) q'
exec (#sql)
You can create a dynamic SQL statement and execute it in the following form
declare #tablename sysname = 'MultipleNumericColumns'
declare #sql nvarchar(max)
select #sql = isnull(#sql + ' union all ','') + '
SELECT ' + name + ' as colname from ' + #tablename
from sys.all_columns
where
object_id = OBJECT_ID(#tablename)
set #sql = '
select min(colname)
from (
' + #sql + '
) t
where colname > 0'
EXECUTE(#sql)
You can realize that first I get the column names from system view
You can exclude columns that you don't want or use a pattern like name like '%price% etc at this step
Then I build a dynamic SQL query into a string variable as sql command
Please note that I use WHERE clause for greater than 0, etc
Final step is execution with EXECUTE command
Use UNPIVOT
SELECT Min(VALUE)
FROM (
SELECT Col1, Col2, Col3...
FROM YourTable
) t
UNPIVOT (VALUE FOR ITEM IN (Col1, Col2, Col3...)) u
WHERE VALUE != 0
I have columns something like this:
col1 | col2 | col3 | col4 | col5 | col6 |
-----+------+------+------+------+------+
a | b | c | a | c | c
I am trying to get unique values in the column it self PER row.
So ideally, I want a,b,c to be returned
I tried doing PIVOT and applying a DISTINCT but that doesn't go well as there are other columns that I couldn't show in the question.
So is there another way that this could be obtained?
Thanks in advance
Is this what you are looking for ..?
IF OBJECT_ID('tempdb..#Pivot_data')IS NOT NULL
DROP TABLE #Pivot_data
IF OBJECT_ID('tempdb..#tab')IS NOT NULL
DROP TABLE #tab
select * into #tab from
(select 'a'AS COL1,'b'AS COL2,'c'AS COL3,'a'AS COL4,'c'AS COL5,'c' COL6)AS A
DECLARE #Columns nvarchar(max) ,#QUERY NVARCHAR(MAX)
;WITH CTE AS (
SELECT COLUMNSS,COL_VALUES,ROW_NUMBER()OVER(PARTITION BY COL_VALUES ORDER BY (SELECT 1))RN FROM (
SELECT * FROM #tab
)AS A
UNPIVOT(COL_VALUES FOR COLUMNSS IN([col1],[col2],[col3],[col4],[col5],[col6])) AS B
)
,FINAL_Result as (select COLUMNSS,COL_VALUES from CTE where RN=1)
SELECT * INTO #Pivot_data FROM FINAL_Result
SET #Columns= (SELECT STUFF((SELECT ',['+COLUMNSS+']' FROM #Pivot_data FOR XML PATH('')),1,1,''))
SET #QUERY=N'SELECT * FROM (
SELECT * FROM #Pivot_data
) AS A
PIVOT (MAX(COL_VALUES)FOR COLUMNSS IN('+#Columns+'))
AS B'
PRINT #QUERY
EXEC (#QUERY)
Logic :
From the given table i did unpivot and generated a Row_number() based on the column values and considered only which are Row_num=1 i.e Distinct columns values . and finally, i Pivoted the resultant data .
It seems to be CROSS APPLY would be work here
select a.Col from table t
cross apply (
values (t.Col1), (t.Col2), (t.Col3),
(t.Col4), (t.Col5), (t.Col6)
)a(Col)
group by a.Col
AND, do the PIVOT with your own way
Try this code
IF OBJECT_ID('tempdb..#Temptab')IS NOT NULL
DROP TABLE #Temptab
;With cte(col1 , col2 , col3 , col4 , col5 , col6 )
AS
(
SELECT 'a' , 'b' , 'c' , 'a' , 'c' , 'c'
)
SELECT DISTINCT AllColumn
INTO #Temptab FROM cte
CROSS APPLY ( VALUES (col1),( col2) , (col3) , (col4) , (col5) , (col6)
) AS A (AllColumn)
DECLARE #SqlQuery nvarchar(max)
,#Sql nvarchar(max)
SELECT #SqlQuery='SELECT DISTINCT '+STUFF((SELECT ', '+ReqColumn FROM
(
SELECT ''''+AllColumn +'''' +' AS Col'+ CAST(Seq AS VARCHAR(2)) As ReqColumn
FROm
(
SELECT ROW_NUMBER()OVER(Order by AllColumn) AS Seq,AllColumn FROM #Temptab
)dt
)dte FOR XML PATH ('')),1,1,'') +' From #Temptab'
PRINT #SqlQuery
EXEC(#SqlQuery)
Result
Col1 Col2 Col3
----------------------
a b c
You can first union all columns to get unique values and then use the GROUP_CONCAT() function in MySql or if your database is Oracle then use list_agg() function:
Please try the below SQL:
select GROUP_CONCAT(col1 SEPARATOR ', ') from (
select '1' as 'serial', col1 from pivot
union
select '1' as 'serial',col2 from pivot
union
select '1' as 'serial',col3 from pivot
union
select '1' as 'serial',col4 from pivot
union
select '1' as 'serial',col5 from pivot
union
select '1' as 'serial' ,col6 from pivot
)derived
GROUP BY serial;
Output:
Note : I have make the 'serial' column in my query to do the group by. You can use the your identifier field in group by clause if you have any.
I've been stuck with this for a while and I've not found something on the website that answers something like this so please point me to the right direction if an existing question exists. In SQL Server 2012, I have a table with ID as the primary key:
ID col1 col2 col3 ....
--- ---- ----- -----
1 a z k
2 g b p
3 k d a
I don't know the length of the table nor the amount of columns/ column names
but I want to be able to get a table that gives me something like:
ID ColName Value
--- ---- -----
1 col1 a
1 col2 z
1 col3 k
2 col1 g
2 col2 b
2 col3 p
3 col1 k
3 col2 d
3 col3 a
...
I know that
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'table'
gets me my columns and I've tried trying to use that to create a temp table to
insert my desired format into a temp table but I'm not sure how to go through each row in a table and then grab the desired values dynamically for each column name and display it. I've been able to kind of achieve this with double cursors but that is painfully slow and I'm not sure how else to approach this since I'm relatively new at SQL. Any help would be appreciated!
Edit
Thank you so very much Lamak! I did have varying data types and coverting them to varchars for now shows me that the concept does work. However, I have 4 common datatypes (varchar, float, int, datetime) that I want to account for so I have 4 value fields for each of those where I would insert the column value into one of those 4 depending on it and leave the other 3 blank. I know that INFORMATION_SCHEMA.COLUMNS also provides the data_types so I was wondering what the syntax would be to convert the datatype in the "STUFF" variables based on a simple IF statement. I tried mapping the data_types to the column names but having any type of conditional statement breaks the query. If anyone has a simple example, that would be great :)
Edit
Actually, I've been able to figure out that I would need to create 4 variables to each data type rather than do them all in just one of them. Thank you all for your help!
As the comments said, you'll need to use dynamic unpivot.
If every column aside ID have the same datatype, you can use the following query:
DECLARE #colsUnpivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SELECT #colsUnpivot = STUFF((SELECT ', ' + QUOTENAME(C.name)
FROM sys.columns as C
WHERE C.object_id = object_id('table') AND
C.name <> 'ID'
FOR XML PATH('')), 1, 1, '');
SET #query = '
SELECT ID,
ColName,
Value
FROM
(
SELECT *
FROM dbo.table
) x
UNPIVOT
(
Value FOR ColName IN (' + #colsunpivot + ')
) u
';
EXEC(#query);
Now, if the datatypes are different, then you'll need to first convert every column to a common datatype. In the following example, I'll use NVARCHAR(1000), but you'll need to convert them to the right datatype:
DECLARE #colsUnpivot1 AS NVARCHAR(MAX),
#colsUnpivot2 as NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SELECT #colsUnpivot1 = STUFF((SELECT ', ' + QUOTENAME(C.name)
FROM sys.columns as C
WHERE C.object_id = object_id('table') AND
C.name <> 'ID'
FOR XML PATH('')), 1, 1, '');
SELECT #colsUnpivot2 = STUFF((SELECT ', CONVERT(NVARCHAR(1000),' + QUOTENAME(C.name)
+ ') ' + QUOTENAME(C.name)
FROM sys.columns as C
WHERE C.object_id = object_id('table') AND
C.name <> 'ID'
FOR XML PATH('')), 1, 1, '');
SET #query = '
SELECT ID,
ColName,
Value
FROM
(
SELECT ID, ' + #colsUnpivot2 + '
FROM dbo.table
) x
UNPIVOT
(
Value FOR ColName IN ('+ #colsunpivot1 +')
) u
';
EXEC(#query);
You don't have to go DYNAMIC. Another Option with with a CROSS APPLY and a little XML.
UnPivot is more performant, but you will find the performance of this approach very respectable.
An added bonus of this approach is that the From #YourTable A could be any query (not limited to a table). For example From ( -- Your Complex Query --) A
Declare #YourTable table (ID int,Col1 varchar(25),Col2 varchar(25),Col3 varchar(25))
Insert Into #YourTable values
(1,'a','z','k')
,(2,'g','b','p')
,(3,'k','d','a')
Select C.*
From #YourTable A
Cross Apply (Select XMLData=cast((Select A.* for XML Raw) as xml)) B
Cross Apply (
Select ID = r.value('#ID','int')
,Item = attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)')
From B.XMLData.nodes('/row') as A(r)
Cross Apply A.r.nodes('./#*') AS B(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('ID','OtherFieldsToExclude')
) C
Returns
ID Item Value
1 Col1 a
1 Col2 z
1 Col3 k
2 Col1 g
2 Col2 b
2 Col3 p
3 Col1 k
3 Col2 d
3 Col3 a
I have the following table with the following sample data
ID Language Question SubQuestion SubSubQuestion TotalCount TotalPercent
3 E 9 0 1 88527 73%
3 E 9 0 2 19684 16%
3 E 9 0 3 12960 11%
3 E 9 0 9 933 1%
I want all in one row like this
ID Language TotalCount901 TotalPercent901 TotalCount902 TotalPercent902 TotalCount903 TotalPercent903
3 E 88527 73% 19684 16% 12960 11%
I've tired using the pivot command, but it dosnt to work for me.
I made a few assumptions based on your column names, but it looks like you want to use something similar to this. This applies both an UNPIVOT and then a PIVOT to get the values in the columns you requested:
select *
from
(
select id,
language,
col + cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)) col,
value
from
(
select id, language,
cast(TotalCount as varchar(10)) TotalCount,
totalPercent,
question, subquestion, SubSubQuestion
from yourtable
) usrc
unpivot
(
value
for col in (totalcount, totalpercent)
) un
) srcpiv
pivot
(
max(value)
for col in ([TotalCount901], [totalPercent901],
[TotalCount902], [totalPercent902],
[TotalCount903], [totalPercent903],
[TotalCount909], [totalPercent909])
) p
See SQL Fiddle with Demo
Note: when performing the UNPIVOT the columns need to be of the same datatype. If they are not, then you will need to convert/cast to get the datatypes the same.
If you have an unknown number of values to transform, you can use dynamic sql:
DECLARE #query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX)
select #colsPivot
= STUFF((SELECT ','
+ QUOTENAME(c.name +
cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)))
from yourtable t
cross apply sys.columns as C
where C.object_id = object_id('yourtable') and
C.name in ('TotalCount', 'TotalPercent')
group by c.name, t.question, t.subquestion, t.subsubquestion
order by t.SubSubQuestion
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'select *
from
(
select id,
language,
col + cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)) col,
value
from
(
select id, language,
cast(TotalCount as varchar(10)) TotalCount,
totalPercent,
question, subquestion, SubSubQuestion
from yourtable
) usrc
unpivot
(
value
for col in (totalcount, totalpercent)
) un
) srcpiv
pivot
(
max(value)
for col in (' + #colsPivot + ')
) p '
execute(#query)
See SQL Fiddle with Demo