Flip SQL Result - sql

How to do this in sql ?
I have select and the result are :
1 a
2 b
3 c
4 d
5 e
then I want the result to display like
1 2 3 4 5
a b c d e
Thanks Guys!

You can use pivot
select [1],[2],[3],[4],[5]
from
(
select column1, column2
from mytable
) d
pivot
(
max(column2)
for column1 in ([1],[2],[3],[4],[5])
) piv;
SQL FIDDLE DEMO

Looks like you need to use pivot statement, https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
-- Potentially your case example:
DECLARE #t TABLE (id int, val CHAR(1));
INSERT INTO #t VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')
-- Pivot table with one row and five columns
SELECT 'Value' AS 'Id',
[1], [2], [3], [4], [5]
FROM #t AS SourceTable
PIVOT
(
max(val)
FOR id IN ([1], [2], [3], [4], [5])
) AS PivotTable;

You can use PIVOT:
-- Create sample data
CREATE TABLE Test
(
A INT,
B NVARCHAR(10)
)
INSERT INTO Test (A, B) VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')
-- Pivot query
SELECT [1],[2],[3],[4],[5]
FROM
(
SELECT A, B
FROM Test
) x
PIVOT
(
MIN(B)
FOR A IN ([1],[2],[3],[4],[5])
) piv;
OUTPUT:
1 2 3 4 5
a b c d e
SQL FIDDLE
As you asked, dynamic pivot should be like that:
SELECT #cols += ([Name]) + ','
FROM (
SELECT Name
FROM Table
) a
SET #cols = LEFT(#cols, LEN(#cols) - 1)
SET #sql =
'SELECT *
FROM
(
SELECT Col1 as [NamePiv],
Col2 as [Val]
FROM Table1
) x
PIVOT
(
MIN (Val)
FOR NamePiv IN (' + #cols +')
) p'

Related

Convert rows to columns using pivot

I try to convert this procedure to PIVOT, but I can't. Does anyone have a solution to help?
I have a table has ItemID, StoreID,Stock
I want to convert it to ItemID, Store1,Store2,Store3...,Stock
sum the stock according to itemID and StoreID then inserts the result as a row.
Many thanks
CREATE table #test222
([Id] int,[ItemID] INT, [storeid] int, [stock] decimal(18,2)
);
INSERT INTO #test222
([Id],[ItemID], [storeid], [stock])
VALUES
(1, 1, 3,10),
(2, 1,1, 20),
(3, 1,1, 30),
(4, 2,1, 40),
(5, 2,2,50),
(6, 2,2,60),
(7, 3,2,70),
(8, 4,2,80),
(9, 4,2,90),
(10, 5,2,100);
select * from #test222;
select ItemID, store1,store2,storeid3,storeid4,storeid5,storeid6,storeid7,storeid8,storeid9,storeid10 stock
from
(
select ItemID, storeid, stock
from #test222
) d
pivot
(
max(stock)
for storeid in (1,2,3,4,5,6,7,8,9,10)
) piv;
Give error:
Msg 102 Level 15 State 1 Line 9
Incorrect syntax near '1'.
Here is a simple PIVOT. Just remember to "feed" your pivot with just only the required columns
Example
Select *
From (
Select ItemID
,Col = 'store'+left(storeid,10)
,val = stock
From #test222
) src
Pivot ( max(val) for Col in ( store1,store2,storeid3,storeid4,storeid5,storeid6,storeid7,storeid8,storeid9,storeid10 ) ) src
Results

Apply PIVOT operator multiple times on same column

I have the next table structure:
And need the data in the next format:
But the result is the following:
My code:
SELECT * FROM (
SELECT
record,
field AS real_field,
real_value,
field AS string_field,
string_value
FROM my_table ) AS source
PIVOT ( MAX(string_value) FOR string_field IN ([1],[2],[3],[4]) ) AS string_pivot
PIVOT ( MAX(real_value) FOR real_field IN ([5],[6]) ) AS real_pivot;
Any ideas? Thank you.
You could simply use coalesce, no?
DECLARE #test TABLE(record INT,
field INT,
real_value DECIMAL NULL,
string_value VARCHAR(10) NULL);
INSERT INTO #test(record, field, real_value, string_value)
VALUES(1, 1, NULL, 'A'),
(1, 2, NULL, 'B'),
(1, 3, NULL, 'C'),
(1, 4, NULL, 'D'),
(1, 5, 1.0, NULL),
(1, 6, 2.0, NULL);
SELECT *
FROM(
SELECT record, field, COALESCE(CONVERT(VARCHAR(10), real_value, 0), string_value) AS value
FROM #test
) AS source
PIVOT(MAX(value)
FOR field IN([1], [2], [3], [4], [5], [6])
) AS real_pivot;
Well, it was actually simpler than I thought:
SELECT record, MAX([1]), MAX([2]), MAX([3]), MAX([4]), MAX([5]), MAX([6]) FROM ( SELECT record, field AS real_field, real_value, field AS string_field, string_value FROM my_table ) AS source
PIVOT ( MAX(string_value) FOR string_field IN ([1],[2],[3],[4]) ) AS string_pivot
PIVOT ( MAX(real_value) FOR real_field IN ([5],[6]) ) AS real_pivot
GROUP BY record;

Pivot values on column based on grouped columns in SQL

I want to pivot values to columns based on a group. However, I do not know the values beforehand.
A query gives me this result.
Id Code EntityId
----------- ------------ -------------
3 22209776 1
4 143687971 3
4 143687971 4
4 143687971 5
4 143687971 15
5 143658155 7
5 143658155 8
I would like to output this
Id Code EntityId1 EntityId2 EntityId3 EntityId4
----------- ------------ ------------- ------------- ------------- -------------
3 22209776 1 NULL NULL NULL
4 143687971 3 4 5 15
5 143658155 7 8 NULL NULL
If you do now know how many column you are going to have in the result, you need to use dynamic T-SQL statement to build the PIVOT. For example:
IF OBJECT_ID('tempdb..#DataSource') IS NOT NULL
BEGIN;
DROP TABLE #DataSource;
END;
CREATE TABLE #DataSource
(
[id] INT
,[Code] INT
,[EntityId] INT
);
DECLARE #DynamicTSQLStatement NVARCHAR(MAX)
,#Columns NVARCHAR(MAX);
DECLARE #MaxColumns INT;
INSERT INTO #DataSource ([id], [Code], [EntityId])
VALUES (3, 22209776 , 1)
,(4, 143687971, 3)
,(4, 143687971, 4)
,(4, 143687971, 5)
,(4, 143687971, 15)
,(5, 143658155, 7)
,(5, 143658155, 8)
,(4, 143687971, 25)
,(4, 143687971, 26);
-- we need to know how many columns are going to be shown
SELECT TOP 1 #MaxColumns = COUNT(*)
FROM #DataSource
GROUP BY [Code]
ORDER BY COUNT(*) DESC;
-- we are building here the following string '[1],[2],[3],[4],[5],[6]';
-- this will change depending the input data
WITH gen AS
(
SELECT 1 AS num
UNION ALL
SELECT num+1
FROM gen
WHERE num+1<=#MaxColumns
)
SELECT #Columns = STUFF
(
(
SELECT ',[EntityId' + CAST([num] AS VARCHAR(12)) + ']'
FROM gen
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
OPTION (maxrecursion 10000);
SET #DynamicTSQLStatement = N'
SELECT *
FROM
(
SELECT [id]
,[Code]
,[EntityId]
,''EntityId'' + CAST(ROW_NUMBER() OVER(PARTITION BY [Code] ORDER BY [EntityId]) AS VARCHAR(12))
FROM #DataSource
) DS ([id], [Code], [EntityId], [RowID])
PIVOT
(
MAX([EntityId]) for [RowID] in (' + #Columns +')
) PVT;';
EXEC sp_executesql #DynamicTSQLStatement;
You could try using the pivot function:
declare #tmp TABLE (id int, Code int, EntityId NVARCHAR(10))
insert into #tmp (id, Code, EntityId)
values (3, 22209776 , 1),
(4, 143687971, 3),
(4, 143687971, 4),
(4, 143687971, 5),
(4, 143687971, 15),
(5, 143658155, 7),
(5, 143658155, 8)
select
pvt.id
,pvt.Code
,[1] as EntityID1
,[2] as EntityID2
,[3] as EntityID3
,[4] as EntityID4
from (
select
id, Code, EntityId
,ROW_NUMBER() over(partition by code order by EntityId) as RowNum
from
#tmp
) a
pivot (MAX(EntityId) for RowNum in ([1], [2], [3], [4])) as pvt

Identifying/comparing sets of rows within groups

I have a matter which seemed simple to solve but now I find it troublesome.
In simplification - I need to find a way to identify unique sets of rows within groups defined by another column. In basic example the source table contains only two columns:
routeID nodeID nodeName
1 1 a
1 2 b
2 1 a
2 2 b
3 1 a
3 2 b
4 1 a
4 2 c
5 1 a
5 2 c
6 1 a
6 2 b
6 3 d
7 1 a
7 2 b
7 3 d
So, the routeID column refers to set of nodes which define a route.
What I need to do is to somehow group the routes, so that there will be only one unique sequence of nodes for one routeID.
In my actual case I tried to use window function to add columns which help to identify nodes sequence, but I still have no idea how to get those unique sequences and group routes.
As a final effect I want to get only unique routes - for example routes 1,2 and 3 aggregated to one route.
Do you have any idea how to help me ?
EDIT:
The other table which I would like to join with the one from the example may look like that:
journeyID nodeID nodeName routeID
1 1 a 1
1 2 b 1
2 1 a 1
2 2 b 1
3 1 a 4
3 2 c 4
...........................
...........................
You can try this idea:
DECLARE #DataSource TABLE
(
[routeID] TINYINT
,[nodeID] TINYINT
,[nodeName] CHAR(1)
);
INSERT INTO #DataSource ([routeID], [nodeID], [nodeName])
VALUES ('1', '1', 'a')
,('1', '2', 'b')
,('2', '1', 'a')
,('2', '2', 'b')
,('3', '1', 'a')
,('3', '2', 'b')
,('4', '1', 'a')
,('4', '2', 'c')
,('5', '1', 'a')
,('5', '2', 'c')
,('6', '1', 'a')
,('6', '2', 'b')
,('6', '3', 'd')
,('7', '1', 'a')
,('7', '2', 'b')
,('7', '3', 'd');
SELECT DS.[routeID]
,nodes.[value]
,ROW_NUMBER() OVER (PARTITION BY nodes.[value] ORDER BY [routeID]) AS [rowID]
FROM
(
-- getting unique route ids
SELECT DISTINCT [routeID]
FROM #DataSource DS
) DS ([routeID])
CROSS APPLY
(
-- for each route id creating CSV list with its node ids
SELECT STUFF
(
(
SELECT ',' + [nodeName]
FROM #DataSource DSI
WHERE DSI.[routeID] = DS.[routeID]
ORDER BY [nodeID]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) nodes ([value]);
The code will give you this output:
So, you simple need to filter by rowID = 1. Of course, you can change the code as you like in order to satisfy your bussness criteria (for example showing no the first route ID with same nodes, but the last).
Also, ROW_NUMBER function cannot be used directly in the WHERE clause, so you need to wrap the code before filtering:
WITH DataSource AS
(
SELECT DS.[routeID]
,nodes.[value]
,ROW_NUMBER() OVER (PARTITION BY nodes.[value] ORDER BY [routeID]) AS [rowID]
FROM
(
-- getting unique route ids
SELECT DISTINCT [routeID]
FROM #DataSource DS
) DS ([routeID])
CROSS APPLY
(
-- for each route id creating CSV list with its node ids
SELECT STUFF
(
(
SELECT ',' + [nodeName]
FROM #DataSource DSI
WHERE DSI.[routeID] = DS.[routeID]
ORDER BY [nodeID]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) nodes ([value])
)
SELECT DS2.*
FROM DataSource DS1
INNER JOIN #DataSource DS2
ON DS1.[routeID] = DS2.[routeID]
WHERE DS1.[rowID] = 1;
ok, let's use some recursion to create a complete node list for each routeID
First of all let's populate source table and journeyes tale
-- your source
declare #r as table (routeID int, nodeID int, nodeName char(1))
-- your other table
declare #j as table (journeyID int, nodeID int, nodeName char(1), routeID int)
-- temp results table
declare #routes as table (routeID int primary key, nodeNames varchar(1000))
;with
s as (
select *
from (
values
(1, 1, 'a'),
(1, 2, 'b'),
(2, 1, 'a'),
(2, 2, 'b'),
(3, 1, 'a'),
(3, 2, 'b'),
(4, 1, 'a'),
(4, 2, 'c'),
(5, 1, 'a'),
(5, 2, 'c'),
(6, 1, 'a'),
(6, 2, 'b'),
(6, 3, 'd'),
(7, 1, 'a'),
(7, 2, 'b'),
(7, 3, 'd')
) s (routeID, nodeID, nodeName)
)
insert into #r
select *
from s
;with
s as (
select *
from (
values
(1, 1, 'a', 1),
(1, 2, 'b', 1),
(2, 1, 'a', 1),
(2, 2, 'b', 1),
(3, 1, 'a', 4),
(3, 2, 'c', 4)
) s (journeyID, routeID, nodeID, nodeName)
)
insert into #j
select *
from s
now let's exctract routes:
;with
d as (
select *, row_number() over (partition by r.routeID order by r.nodeID desc) n2
from #r r
),
r as (
select d.*, cast(nodeName as varchar(1000)) Names, cast(0 as bigint) i2
from d
where nodeId=1
union all
select d.*, cast(r.names + ',' + d.nodeName as varchar(1000)), r.n2
from d
join r on r.routeID = d.routeID and r.nodeId=d.nodeId-1
)
insert into #routes
select routeID, Names
from r
where n2=1
table #routes will be like this:
routeID nodeNames
1 'a,b'
2 'a,b'
3 'a,b'
4 'a,c'
5 'a,c'
6 'a,b,d'
7 'a,b,d'
an now the final output:
-- the unique routes
select MIN(r.routeID) routeID, nodeNames
from #routes r
group by nodeNames
-- the unique journyes
select MIN(journeyID) journeyID, r.nodeNames
from #j j
inner join #routes r on j.routeID = r.routeID
group by nodeNames
output:
routeID nodeNames
1 'a,b'
4 'a,c'
6 'a,b,d'
and
journeyID nodeNames
1 'a,b'
3 'a,c'

Summarize the list into a comma-separated string

This is the current result that can be changed from day to day
(int) (nvarchar)
Number Grade
--------------
1 a
1 c
2 a
2 b
2 c
3 b
3 a
What I need help is to achieve this result below.
Number Grade
-----------------
1 a, c
2 a, b, c
3 b, a
Use:
declare #t table(Number int, Grade varchar)
insert #t values(1, 'a'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c'),
(3, 'b'), (3, 'a')
select t1.Number
, stuff((
select ',' + Grade
from #t t2
where t2.Number = t1.Number
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, '') [values]
from #t t1
group by t1.Number
You'll need to replace dbo.tablename with your actual table. Also I'm assuming you're using SQL Server 2005 or better - always useful to specify.
SELECT Number, Grades = STUFF((
SELECT N', ' + Grade FROM dbo.tablename
WHERE Number = x.Number
FOR XML PATH(''),
TYPE).value(N'./text()[1]', N'nvarchar(max)'), 1, 2, N'')
FROM dbo.tablename AS x
GROUP BY Number;
In SQL Server 2017 and Azure SQL Database, you can use the new aggregation function STRING_AGG(), which is a lot tidier in this case:
SELECT Number, Grades = STRING_AGG(Grade, N', ')
FROM dbo.tablename
GROUP BY Number;