Dynamic Row Data into Column - sql

I have a column which has 100 rows of data. I need to get the top 4 but in instead of rows I need to convert it into columns. Like Col1, Col2, Col3 and Col4.
I have tried
SELECT
MAX (CASE
WHEN rss_name = 'BBC-Sports'
THEN rss_name
END) AS col1,
MAX (CASE
WHEN rss_name = 'Talk Sports'
THEN rss_name
END) AS col2,
MAX (CASE
WHEN rss_name = 'Sky Sports'
THEN rss_name
END) AS col3,
MAX (CASE
WHEN rss_name = 'Crick Info'
THEN rss_name
END) AS col4
FROM
RSS
but it only works with static values:
I need
Col1, Col2, Col3, Col4
Sports,Talk Sports,Sky Sports,Crick Info
but since this is not constant data it will change and the values in Col keep changing.

You could use a derived table to set your column order then use your conditional aggregation on that.
SELECT
MAX(CASE WHEN Col_Rn = 1 THEN Rss_Name END) AS Col1,
MAX(CASE WHEN Col_Rn = 2 THEN Rss_Name END) AS Col2,
MAX(CASE WHEN Col_Rn = 3 THEN Rss_Name END) AS Col3,
MAX(CASE WHEN Col_Rn = 4 THEN Rss_Name END) AS Col4
FROM (
SELECT Rss_Name,
Row_Number() OVER (ORDER BY Rss_Name) AS Col_Rn -- set your order here
FROM RSS
) t

You need to use Dynamic Pivot. But in your case besides you need an extra column for Column names in Pivot like COL_1, COL_2....
Schema: (From your Image. Its better if you provide this sample data in Text).
CREATE TABLE #TAB (Rss_Name VARCHAR(50))
INSERT INTO #TAB
SELECT 'Sports'
UNION ALL
SELECT 'Talk Sports'
UNION ALL
SELECT 'Sky Sports'
UNION ALL
SELECT 'Crick Info'
Now Prepare your dynamic query as below
DECLARE #SQL VARCHAR(MAX)='',#PVT_COL VARCHAR(MAX)='';
--Preparing Dynamic Column List
SELECT #PVT_COL =#PVT_COL
+ '[COL_'+CAST(ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS VARCHAR(4))+'],'
FROM #TAB
SELECT #PVT_COL = LEFT(#PVT_COL,LEN(#PVT_COL)-1)
SELECT #SQL =
'SELECT * FROM (
SELECT Rss_Name
,''COL_''+CAST(ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS VARCHAR(4)) AS COL_NME
FROM #TAB
)AS A
PIVOT
(
MAX(Rss_Name) FOR COL_NME IN ('+#PVT_COL+')
)PVT'
EXEC (#SQL)
Result:
+--------+-------------+------------+------------+
| COL_1 | COL_2 | COL_3 | COL_4 |
+--------+-------------+------------+------------+
| Sports | Talk Sports | Sky Sports | Crick Info |
+--------+-------------+------------+------------+

Related

Select rows to columns from table

How to simply SELECT rows from VALUE column to get a two-dimensional table with 6 columns and 2 rows.
Below is the source table:
Here's a solution using SQL Pivot.
How to create the example dataset:
IF OBJECT_ID('SourceTable') Is Not Null Drop Table SourceTable
Create Table SourceTable (
COL_IDX int,
ROW_IDX int,
VALUE nvarchar(100)
)
Insert into SourceTable (COL_IDX, ROW_IDX, VALUE)
Values
(1,1,'after 45 min')
,(2,1,'98')
,(3,1,'95')
,(4,1,'99')
,(5,1,'1.1')
,(6,1,'12')
,(1,2,'after 60 min')
,(2,2,'98')
,(3,2,'96')
,(4,2,'101')
,(5,2,'1.4')
,(6,2,'12')
How to pivot the data:
SELECT ROW_IDX AS MyIndex, [1],[2],[3],[4],[5],[6]
FROM
(SELECT * FROM SourceTable) AS q
PIVOT (
MAX(Value)
FOR COL_IDX IN ([1],[2],[3],[4],[5],[6])
) AS PivotedTable
Result:
MyIndex 1 2 3 4 5 6
1 after 45 min 98 95 99 1.1 12
2 after 60 min 98 96 101 1.4 12
A pivot in SQL uses an aggregate function; here I used "max" but it doesn't really matter because there aren't multiple values for a ROW_IDX/COL_IDX combo.
It sounds like you might need to dynamically generate your columns, in which case you can use dynamic SQL to get the same result:
DROP TABLE IF EXISTS #headings
SELECT DISTINCT COL_IDX='[' + Cast(COL_IDX AS NVARCHAR(6)) + ']'
INTO #headings
FROM SourceTable
DECLARE #Columns nvarchar(1000) = (SELECT string_agg(COL_IDX, ', ') FROM #headings)
DECLARE #MyCommand nvarchar(max) =
'SELECT *
FROM SourceTable
PIVOT (
MAX(VALUE)
FOR COL_IDX IN (~Columns~)
) PivotTable'
SET #MyCommand = Replace(#MyCommand, '~Columns~', #Columns)
EXEC (#MyCommand)
DROP TABLE IF EXISTS #headings
A typical solution to pivot a table over a fixed set of columns is to use conditional aggregation:
select
row_idx,
max(case when col_idx = 1 then value end) col1,
max(case when col_idx = 2 then value end) col2,
max(case when col_idx = 3 then value end) col3,
max(case when col_idx = 4 then value end) col4,
max(case when col_idx = 5 then value end) col5,
max(case when col_idx = 6 then value end) col6
from mytable
group by row_idx
order by row_idx

Concatenate rows into columns (NO FOR XML PATH('') and recursive CTEs) - SQL Server 2012

I have a very particular problem at hand.
Brief introduction: I have two columns at a database that I need to "group concatenate", in MySQL I would simply use GROUP_CONCAT to get the desired result, in SQL Server 2017 and on I would use STRING_AGG, the problem that I have is in the SQL Server 2012, which doesn't have this function.
Now, under normal circumstances I would use FOR XML PATH('') to get the solution, this is not viable since I'm running the query from the editor inside a third source application, the error that I get is
FOR XML PATH('') can't be used inside a cursor
For the sake of the argument let's assume that it's completely out of question to use this function.
I have tried using recursive CTE, however, it's not viable due to execution time, UNION ALL takes too much resources and can't execute properly (I am using the data for reporting).
I will no post the screenshots of the data due to the sensitivity of the same, imagine just having two columns, one with an id (multiple same id's), and a column with the data that needs to be concatenated (some string). The goal is to concatenate the second columns for all of the same id's in the first columns, obviously make it distinct in the process.
Example:
Input:
col1 col2
1 a
1 b
2 a
3 c
Output:
col1 col2
1 a/b
2 a
3 c
Does anyone have a creative idea on how to do this?
If you know the maximum number of values that need to be concatenated together, you can use conditional aggregation:
select col1,
stuff( concat(max(case when seqnum = 1 then '/' + col2 end),
max(case when seqnum = 2 then '/' + col2 end),
max(case when seqnum = 3 then '/' + col2 end),
max(case when seqnum = 4 then '/' + col2 end),
max(case when seqnum = 5 then '/' + col2 end)
), 1, 1, ''
) as col2s
from (select t.*,
row_number() over (partition by col1 order by col2) as seqnum
from t
) t
group by col1;
You can get the maximum number using:
select top (1) count(*)
from t
group by col1;
Your sample output seems wrong as 'a/b' should come for value 2.
try the following:
declare #t table (col1 int, col2 varchar(100))
insert into #t select 1, 'a'
insert into #t select 2, 'b'
insert into #t select 2, 'a'
insert into #t select 3, 'c'
declare #final_table table (col1 int, col2 varchar(100), col2_all varchar(1000))
insert into #final_table (col1, col2)
select * from #t
declare #col2_all varchar(1000)
declare #Name sysname
update #final_table
SET #col2_all = col2_all = COALESCE(CASE COALESCE(#Name, '')
WHEN col1 THEN #col2_all + '/' + col2
ELSE col2 END, ''),
#Name = col1;
select col1, col2_grouped = MAX(col2_all)
from #final_table
group by col1
Using CTE:
;with cte(col1,col2_grouped,rn)
as
(
select col1, col2 , rn=ROW_NUMBER() over (PARTITION by col1 order by col1)
from #t
)
,cte2(col1,final_grouped,rn)
as
(
select col1, convert(varchar(max),col2_grouped), 1 from cte where rn=1
union all
select cte2.col1, convert(varchar(max),cte2.final_grouped+'/'+cte.col2_grouped), cte2.rn+1
from cte2
inner join cte on cte.col1 = cte2.col1 and cte.rn=cte2.rn+1
)
select col1, MAX(final_grouped) col2_grouped from cte2 group by col1
Please see db<>fiddle here.

Getting Unique values from a row - SQL Server

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.

combine distinct row values into a string - sql

I would like to take cells in every row and make them into a string of names... My method already deals with casing.
For example, the table;
'john' | | 'smith' | 'smith'
'john' | 'paul' | | 'smith'
'john' | 'john' | 'john' |
returns:
'john smith'
'john paul smith'
'john'
This would need to run postgreSQL 8.2.15 of postgres so I can't make use of potentially useful functions like CONCAT, and data is in a greenplum db.
Alternatively, a method to directly delete duplicate tokens in a list of strings would let me achieve the larger objective. For example:
'john smith john smith'
'john john smith'
'smith john smith'
returns
'john smith'
'john smith'
'smith john'
The order of the tokens is not important, as long as all the unique values are returned, once only.
Thanks
Normalize your table structure, select distinct name values from that table, create a function to aggregate strings (see, e.g., How to concatenate strings of a string field in a PostgreSQL 'group by' query?), and apply that function. Except for the aggregate function creation, this could all be done in a single statement or view.
I've come up with a solution for you! :)
The following query returns the four columns (which I named col_1,2,3and 4) and removes the duplicates by joining the test_table with itself.
Here is the code:
SELECT t1.col_1, t2.col_2, t3.col_3, t4.col_4
FROM (
SELECT id, col_1
FROM test_table
) AS t1
LEFT JOIN (
SELECT id, col_2
FROM test_table
) as t2
ON (t2.id = t1.id and t2.col_2 <> t1.col_1)
LEFT JOIN (
SELECT id, col_3
FROM test_table
) as t3
ON (t3.id = t1.id and t3.col_3 <> t1.col_1 and t3.col_3 <> t2.col_2)
LEFT JOIN (
SELECT id, col_4
FROM test_table
) as t4
ON (t4.id = t1.id and t4.col_4 <> t1.col_1 and t4.col_4 <> t2.col_2 and t4.col_4 <> t3.col_3);
If you want to obtain the final string, you just substitute the "SELECT" row with this one:
SELECT trim(both ' ' FROM (COALESCE(t1.col_1, '') || ' ' || COALESCE(t2.col_2, '') || ' ' || COALESCE(t3.col_3, '') || ' ' || COALESCE(t4.col_4, '')))
this should work with your version of postgres, according with the docs:
[for the trim and concatenation functions]
https://www.postgresql.org/docs/8.2/static/functions-string.html
//***************************************************
[for the coalesce function]
https://www.postgresql.org/docs/8.2/static/functions-conditional.html
Please let me know if I've been of help :)
P.S. Your question sounds like a bad database design: I would have those columns moved on a table in which you could do this operation by using a group by or something similar. Moreover I would do the string concatenation on a separate script.
But that's my way of doing :)
I would do this by unpivoting the data and then reaggregation:
select id, string_agg(distinct col)
from (select id, col1 from t union all
select id, col2 from t union all
select id, col3 from t union all
select id, col4 from t
) t
where col is not null
group by id;
This assumes that each row has an unique id.
You can also use a giant case:
select concat_ws(',',
col1,
(case when col2 <> col1 then col2 end),
(case when col3 <> col2 and col3 <> col1 then col3 end),
(case when col4 <> col3 and col4 <> col2 and col4 <> col1 then col4 end)
) as newcol
from t;
In ancient versions of Postgres, you can phrase this as:
select trim(leading ',' from
(coalesce(',' || col1, '') ||
(case when col2 <> col1 then ',' || col2 else '' end) ||
(case when col3 <> col2 and col3 <> col1 then ',' || col3 else '' end),
(case when col4 <> col3 and col4 <> col2 and col4 <> col1 then ',' || col4 else '' end)
)
) as newcol
from t;

Grouping between consecutive rows in a table without cursor

I trying to get a grouping done between 2 rows without a cursor, can some one help me reg this
Col1(int) Col2(int)
--------- ---------
1 20
2 30
3 40
I want output like this
Col1 Col2
---- ----
1-2 50
2-3 70
Are you sure you aren't missing any rows...
Select cast(a.col1 as varchar(10)) + '-' + cast(b.col1 as varchar(10)) as col1,
a.col2 + b.Col2 as Col2
From mytable a
Inner Join mytable b on b.col1 = (a.col1 + 1)
if you might be missing rows, you might need to be more complicated.
That's a tricky one if you don't want to repeat the rows (1-2, 2-3) and you can expect there to be some missing ids (as would be normal if you have an identity field).
Try this:
CREATE TABLE #temp (id INT, value INT)
INSERT INTO #temp
SELECT 1,2
UNION ALL
SELECT 2,8
UNION ALL
SELECT 3,8
UNION ALL
SELECT 5,19
SELECT id, value, ROW_NUMBER() OVER (ORDER BY id) AS rownumber
INTO #temp2
FROM #temp
SELECT * FROM #temp2
SELECT CAST(b.id AS VARCHAR(10)) + '-' + CAST(a.id AS VARCHAR(10)) AS col1,
a.value + b.value as Col2
FROM #temp2 a
JOIN #temp2 b
ON a.rownumber = b.rownumber+1
WHERE ABS(a.rownumber)%2 = 0
Assuming that col1 is integer
SELECT CAST(a.col1 as VARCHAR(10))+ '-' + CAST(b.col1 as VARCHAR(10)), COALESCE(a.col2,0)+COALESCE(b.col2,0)
FROM table a
JOIN table b a.col1 = b.col1 + 1
you can test following query also...
I have oracle in my machine that's why I can run and say only oracle queries..
please check whether this will work on sql server also or not and tell me about ...
select * from
(Select lag (col1) over (order by col1)|| '-' || col1 as col1
col2 + lag (col2) over (order by col1) as Col2
From mytable
)
where col2 is not null;
in oracle lag () function used to fatch last row values.. and if it is first row then this function will give null values.. so that by appling addition on null values you will get null only
by this concept we will get desired output...