Apply PIVOT operator multiple times on same column - sql

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;

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

SQL Pivot Half of table

I have a table that consists of time information. It's basically:
Employee, Date, Seq, Time In, Time Out.
They can clock out multiple times a day, so I'm trying to get all of the clock outs in a day on one row. My result would be something like:
Employee, Date, TimeIn1, TimeOut1, TimeIn2, TimeOut2, TimeIn3, TimeOut3....
Where the 1, 2, and 3 are the sequence numbers. I know I could just do a bunch of left joins to the table itself based on employee=employee, date=date, and seq=seq+1, but is there a way to do it in a pivot? I don't want to pivot the employee and date fields, just the time in and time out.
The short answer is: Yes, it's possible.
The exact code will be updated if/when you provide sample data to clarify some points, but you can absolutely pivot the times out while leaving the employee/work date alone.
Sorry for the wall of code; none of the fiddle sites are working from my current computer
declare #test table (
pk int,
workdate date,
seq int,
tIN time,
tOUT time
)
insert into #test values
(1, '2020-11-25', 1, '08:00', null),
(1, '2020-11-25', 2, null, '11:00'),
(1, '2020-11-25', 3, '11:32', null),
(1, '2020-11-25', 4, null, '17:00'),
(2, '2020-11-25', 5, '08:00', null),
(2, '2020-11-25', 6, null, '09:00'),
(2, '2020-11-25', 7, '09:15', null),
-- new date
(1, '2020-11-27', 8, '08:00', null),
(1, '2020-11-27', 9, null, '08:22'),
(1, '2020-11-27', 10, '09:14', null),
(1, '2020-11-27', 11, null, '12:08'),
(1, '2020-11-27', 12, '01:08', null),
(1, '2020-11-27', 13, null, '14:40'),
(1, '2020-11-27', 14, '14:55', null),
(1, '2020-11-27', 15, null, '17:00')
select *
from (
/* this just sets the column header names and condenses their values */
select
pk,
workdate,
colName = case when tin is not null then 'TimeIn' + cast(empDaySEQ as varchar) else 'TimeOut' + cast(empDaySEQ as varchar) end,
colValue = coalesce(tin, tout)
from (
/* main query */
select
pk,
workdate,
/* grab what pair # this clock in or out is; reset by employee & date */
empDaySEQ = (row_number() over (partition by pk, workdate order by seq) / 2) + (row_number() over (partition by pk, workdate order by seq) % 2),
tin,
tout
from #test
) i
) a
PIVOT (
max(colValue)
for colName
IN ( /* replace w/ dynamic if you don't know upper boundary of max in/out pairs */
[TimeIn1],
[TimeOut1],
[TimeIn2],
[TimeOut2],
[TimeIn3],
[TimeOut3],
[TimeIn4],
[TimeOut4]
)
) mypivotTable
generates these results.
(I would provide a fiddle demo but they're not working for me today)

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'

Not using Where statement in a context

Goal:
If you have the input data that is -10 then you should not use the WHERE statement in function.
Problem:
I do not know how to solve it in this context. You have to use WHERE and not WHERE depending on what input data you retrieve
Info:
If you use -10 as a input data then you should retrieve all data based on [dbo].[testing] and it is okay to retrieve data that is null in [dbo].[testing2] in relation to LEFT JOIN.
*The code and its data is a sample from production phase.
Thank you!
CREATE TABLE [dbo].[testing](
[id] [int] NULL,
[value] [varchar](30) NULL,
[category] [int] NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[testing2](
[id] [int] NULL,
[value] [varchar](30) NULL,
[category] [int] NULL,
[test_id] [int] NULL,
[id_type] [int] NOT NULL
) ON [PRIMARY]
CREATE FUNCTION dbo.testt (
#data int
)
RETURNS TABLE
AS
RETURN
(
SELECT
a.[id],
a.[value],
a.[category],
b.[id_type]
FROM [dbo].[testing] a left join [dbo].[testing2] b on a.id = b.[id]
where b.[id_type] = #data
)
INSERT INTO [test].[dbo].[testing] VALUES
(1, '', 2), (2, '', 3), (3, 'a', 2), (4, 'a', 2),
(5, 'b', 2), (6, 'b', 2), (7, 'c', 2), (8, 'c', 2),
(9, 'c', 2), (10, 'c', 2);
INSERT INTO [test].[dbo].[testing2] VALUES
(3, 'a' ,2 ,11 ,1), (4, 'a' ,2 ,11 ,1),
(5, 'a' ,2 ,11 ,0), (6, 'a' ,2 ,11 ,2);
select
s.[id],
s.[value],
s.[category],
s.[id_type]
from dbo.testt(1) s
Have your WHERE clause check if #data is either -10 or matches b.[id_type].
WHERE (#data = -10) OR (b.[id_type] = #data)
What about where b.[id_type] = #data OR #data = -10 in the testt function ?
So your function would be:
CREATE FUNCTION dbo.testt (
#data int
)
RETURNS TABLE
AS
RETURN
(
SELECT
a.[id],
a.[value],
a.[category],
b.[id_type]
FROM [dbo].[testing] a
LEFT JOIN [dbo].[testing2] b on a.id = b.[id]
WHERE b.[id_type] = #data OR #data = -10
)

Flip SQL Result

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'