Pivot table in SQL-Server 2008 - sql

I have a table (SQL Server 2008) with shipments, so "Stage 0" means "New shipment" and consecutive stages are diferent stages of shipment tracking.
I'm trying to pivot the following table:
CREATE TABLE TableName
([ID] int, [IDShip] int, [Date] datetime, [Stage] int, [Arg] int)
;
INSERT INTO TableName
([ID], [IDShip], [Date], [Stage], [Arg])
VALUES
(1, 1, '2013-10-03 08:36:00', 0, Null),
(2, 1, '2013-10-03 08:37:25', 1, 1),
(3, 2, '2013-10-03 08:38:25', 0, Null),
(4, 1, '2013-10-03 08:39:25', 2, 1),
(5, 2, '2013-10-03 08:40:25', 1, 3)
;
("Arg" is ID of Stage0. Select * would be:)
ID IDShip Date Stage Arg
1 1 2013-10-03 08:36:00 0 Null
2 1 2013-10-03 08:37:25 1 1
3 2 2013-10-03 08:38:25 0 Null
4 1 2013-10-03 08:39:25 2 1
5 2 2013-10-03 08:40:25 1 3
into something like:
ID0 IDShip DateShipment ID1 DateFirstStage ID2 DateSecondStage
1 1 2013-10-03 08:36:00 2 2013-10-03 08:37:25 4 2013-10-03 08:39:25
3 2 2013-10-03 08:38:25 5 2013-10-03 08:40:25
Any help? Thanks

Turned out to be a bit more messy than I hoped for but here it is:
SELECT MAX([0]) AS ID0,
IDShip,
(SELECT [Date] FROM TableName T1 WHERE T1.ID = MAX([0]) AND T1.IDShip = Y.IDShip) AS DateShipment,
MAX([1]) AS ID1,
(SELECT [Date] FROM TableName T2 WHERE T2.ID = MAX([1]) AND T2.IDShip = Y.IDShip) AS DateFirstStage,
MAX([2]) AS ID2,
(SELECT [Date] FROM TableName T3 WHERE T3.ID = MAX([2]) AND T3.IDShip = Y.IDShip) AS DateSecondStage
FROM
(SELECT * FROM TableName
PIVOT (MAX([ID]) FOR Stage IN ([0], [1], [2])) AS X) Y
GROUP BY IDShip
You first pivot the table into ID's of 3 stages and then select each stage and its date.

It appears you need to pivot more than one column at the same time. However, standard PIVOT syntax does not support multi-columnar pivoting. You could use it to pivot one of the columns, then, using those as look-up values, pull the other column values with a series of correlated subqueries.
My take on the approach was similar to #Szymon's, except I managed to avoid grouping in the outer query, although I made things more messy at other stages. Here is my attempt:
SELECT
IDShip,
ID0,
ID1,
ID2,
DateShipment = (SELECT Date FROM TableName WHERE ID = p.ID0),
DateFirstStage = (SELECT Date FROM TableName WHERE ID = p.ID1),
DateSecondStage = (SELECT Date FROM TableName WHERE ID = p.ID2)
FROM (
SELECT
ID,
IDShip,
StageID = 'ID' + CAST(Stage AS varchar(10))
FROM TableName
) AS s
PIVOT (
MAX(ID) FOR StageID IN (ID0, ID1, ID2)
) AS p
;
With proper indexing, it may not be too bad, although you could also try this alternative, which uses the older pivoting technique of grouping with conditional aggregation:
SELECT
IDShip,
ID0 = MAX(CASE Stage WHEN 0 THEN ID END),
ID1 = MAX(CASE Stage WHEN 1 THEN ID END),
ID2 = MAX(CASE Stage WHEN 2 THEN ID END),
DateShipment = MAX(CASE Stage WHEN 0 THEN Date END),
DateFirstStage = MAX(CASE Stage WHEN 1 THEN Date END),
DateSecondStage = MAX(CASE Stage WHEN 2 THEN Date END)
FROM TableName
GROUP BY IDShip
;
This SQL Fiddle demo will let you try both solutions.

Related

T-SQL - Copying & Transposing Data

I'm trying to copy data from one table to another, while transposing it and combining it into appropriate rows, with different columns in the second table.
First time posting. Yes this may seem simple to everyone here. I have tried for a couple hours to solve this. I do not have much support internally and have learned a great deal on this forum and managed to get so much accomplished with your other help examples. I appreciate any help with this.
Table 1 has the data in this format.
Type Date Value
--------------------
First 2019 1
First 2020 2
Second 2019 3
Second 2020 4
Table 2 already has the Date rows populated and columns created. It is waiting for the Values from Table 1 to be placed in the appropriate column/row.
Date First Second
------------------
2019 1 3
2020 2 4
For an update, I might use two joins:
update t2
set first = tf.value,
second = ts.value
from table2 t2 left join
table1 tf
on t2.date = tf.date and tf.type = 'First' left join
table1 ts
on t2.date = ts.date and ts.type = 'Second'
where tf.date is not null or ts.date is not null;
use conditional aggregation
select date,max(case when type='First' then value end) as First,
max(case when type='Second' then value end) as Second from t
group by date
You can do conditional aggregation :
select date,
max(case when type = 'first' then value end) as first,
max(case when type = 'Second' then value end) as Second
from table t
group by date;
After that you can use cte :
with cte as (
select date,
max(case when type = 'first' then value end) as first,
max(case when type = 'Second' then value end) as Second
from table t
group by date
)
update t2
set t2.First = t1.First,
t2.Second = t1.Second
from table2 t2 inner join
cte t1
on t1.date = t2.date;
Seems like you're after a PIVOT
DECLARE #Table1 TABLE
(
[Type] NVARCHAR(100)
, [Date] INT
, [Value] INT
);
DECLARE #Table2 TABLE(
[Date] int
,[First] int
,[Second] int
)
INSERT INTO #Table1 (
[Type]
, [Date]
, [Value]
)
VALUES ( 'First', 2019, 1 )
, ( 'First', 2020, 2 )
, ( 'Second', 2019, 3 )
, ( 'Second', 2020, 4 );
INSERT INTO #Table2 (
[Date]
)
VALUES (2019),(2020)
--Show us what's in the tables
SELECT * FROM #Table1
SELECT * FROM #Table2
--How to pivot the data from Table 1
SELECT * FROM #Table1
PIVOT (
MAX([Value]) --Pivot on this Column
FOR [Type] IN ( [First], [Second] ) --Make column where [Value] is in one of this
) AS [pvt] --Table alias
--which gives
--Date First Second
------------- ----------- -----------
--2019 1 3
--2020 2 4
--Using that we can update #Table2
UPDATE [tbl2]
SET [tbl2].[First] = pvt.[First]
,[tbl2].[Second] = pvt.[Second]
FROM #Table1 tbl1
PIVOT (
MAX([Value]) --Pivot on this Column
FOR [Type] IN ( [First], [Second] ) --Make column where [Value] is in one of this
) AS [pvt] --Table alias
INNER JOIN #Table2 tbl2 ON [tbl2].[Date] = [pvt].[Date]
--Results from #Table 2 after updated
SELECT * FROM #Table2
--which gives
--Date First Second
------------- ----------- -----------
--2019 1 3
--2020 2 4

Finding row of "duplicate" data with greatest value

I have a table setup as follows:
Key || Code || Date
5 2 2018
5 1 2017
8 1 2018
8 2 2017
I need to retrieve only the key and code where:
Code=2 AND Date > the other record's date
So based on this data above, I need to retrieve:
Key 5 with code=2
Key 8 does not meet the criteria since code 2's date is lower than code 1's date
I tried joining the table on itself but this returned incorrect data
Select key,code
from data d1
Join data d2 on d1.key = d2.key
Where d1.code = 2 and d1.date > d2.date
This method returned data with incorrect values and wrong data.
Perhaps you want this:
select d.*
from data d
where d.code = 2 and
d.date > (select d2.date
from data d2
where d2.key = d.key and d2.code = 1
);
If you just want the key, I would go for aggregation:
select d.key
from data d
group by d.key
having max(case when d2.code = 2 then date end) > max(case when d2.code <> 2 then date end);
use row_number, u can select rows with dates in ascending order. This is based on your sample data, selecting 2 rows
DECLARE #table TABLE ([key] INT, code INT, DATE INT)
INSERT #table
SELECT 5, 2, 2018
UNION ALL
SELECT 5, 2, 2018
UNION ALL
SELECT 8, 1, 2018
UNION ALL
SELECT 8, 2, 2017
SELECT [key], code, DATE
FROM (
SELECT [key], code, DATE, ROW_NUMBER() OVER (
PARTITION BY [key], code ORDER BY DATE
) rn
FROM #table
) x
WHERE rn = 2

Putting unique results of SELECT rows in one row

I have a query returning the results I have but I am not sure how to approach changing it to a convetion that my program uses to send data:
SELECT
[contract_member_brg_attr].attr_val AS 'field_properties',
[contract_attr].attr_val AS 'contract_number',
[other_contract_attr].attr_val AS 'supplier_number',
[MFR].ITEM_NAME AS 'supplier_name'
FROM [contract_member_brg_attr]
INNER JOIN [contract_member_brg]
ON [contract_member_brg_attr].item_id =
[contract_member_brg].item_id
INNER JOIN [contract_attr]
ON [contract_attr].item_id =
[contract_member_brg].[contract_item_id]
AND [contract_attr].field_id = 413
INNER JOIN [contract_attr] AS [other_contract_attr]
ON [other_contract_attr].item_id =
[contract_member_brg].[contract_item_id]
AND [other_contract_attr].field_id = 234
INNER JOIN [MFR] as [MFR]
ON [MFR].ITEM_PK =
[other_contract_attr].attr_val;
Results:
My issue is that I want all unique values from these results to be on 1 row. SO in this case, it would be all of the field_properties and one of each contract_number, supplier_number, supplier_name.
How would I do this what approaches are available?
EDIT:
THis is how I would want it to look:
all on one row:
column1= 388
column2 = FEB 22 2017
column3 = FEB 22 2017
column4 = test 2
column5 = test 3
column6 = true
column7 = b5v5b5b5bb5
column8 = A180
column9 = ABBOTT NUTRITION
Please look to my question about pivot. It has few successfull answers:
How to apply pivot to result of query
SELECT *
FROM (
SELECT
'id',
'field_properties',
'contract_number',
'supplier_number',
'supplier_name'
FROM (
SELECT
row_number() over (ORDER BY [contract_member_brg_attr].id) AS 'id' \\should be some analog for your DB
[contract_member_brg_attr].attr_val AS 'field_properties' ...
\\your original query
)
)
pivot (
MIN('field_properties') \\any agregation function
FOR ID IN (1, 2, 3, 4, 5, 6)
) pvt

i want to display data according to type in same query

i have following table "vehicle_data" :
ID ALERT_TYPE VALUE
58 2 1
58 1 1
104 1 1
104 2 1
Here alert_type = 2 is for GPS value and alert_type=1 is for engine_value .
so if alert_type=2 and its value is =1 then it means its value is correct.
when alert_type=2 and its value is =0 then it means its value is wrong.
same for alert_type=1
so now here i want the following output:
ID gps engine_value
58 1 1
104 1 1
how can i perform this query??
You can do it like this.
SELECT ID
,CASE WHEN [ALERT_TYPE]=2 and [value ]=1 THEN 1 ELSE 0 END as gps
,CASE WHEN [ALERT_TYPE]=1 and [value ]=1 THEN 1 ELSE 0 END as engine
FROM vehicle_data
SELECT ID, alert_type=2 AS gps, alert_type=1 AS [engine] FROM vehicle_data WHERE value=1;
EDITED to account for your explanation of VALUE.
Schema
CREATE TABLE table3 (id int, ALERT_TYPE int)
INSERT table3 VALUES (58, 1), (58, 2), (104, 1), (104, 2)
Query
SELECT *
FROM (
SELECT ID
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY id
) AS row_num
,gps = CASE
WHEN ALERT_TYPE = 2
THEN 1
ELSE 0
END
,engine = CASE
WHEN ALERT_TYPE = 1
THEN 1
ELSE 0
END
FROM table3
) a
WHERE a.row_num = 1
Output
ID gps engine
58 1 0
104 0 1
One possible way using subqueries :
select
Ids.ID
, gps.VALUE 'gps'
, engine_value.VALUE 'engine_value'
from (select distinct ID from vehicle_data) Ids
left join
(select ID, VALUE from vehicle_data where ALERT_TYPE = 2) gps
on gps.ID = Ids.ID
left join
(select ID, VALUE from vehicle_data where ALERT_TYPE = 1) engine_value
on engine_value.ID = Ids.ID
[SQL Fiddle demo]
I hope this should work for you,
Select ID,sum(gps) as gps ,sum(engine) as engine from
(SELECT ID
,CASE WHEN [ALERT_TYPE]=2 and [value ]=1 THEN 1 ELSE 0 END as gps
,CASE WHEN [ALERT_TYPE]=1 and [value ]=1 THEN 1 ELSE 0 END as engine
FROM vehicle_data
)a
group by id
select x.id,x.alert_type as GPS,x.value as engine_value from (
select ID,alert_type,value,ROW_NUMBER() over (partition by id order by alert_type ) as Rnk from mytable
)x
where Rnk=1
Please check this query in SQL :
create table mytable (id int, alert_type int, value int);
insert into mytable (id, alert_type, value)
values (58, 2, 1),
(58, 1, 1),
(104, 1, 1),
(104, 2, 1);
SELECT distinct ID
,(select count (id) from mytable mt where mt.id=mytable.id and mt.[ALERT_TYPE]=2 and mt.[value ]=1) as gps
,(select count (id) from mytable mt where mt.id=mytable.id and mt.[ALERT_TYPE]=1 and mt.[value ]=1) as engine
FROM mytable
BASED ON YOUR QUESTION I BELIEVE YOU WANT THE DATA IN COLUMN AND TO SUIT YOUR REQUIREMENT I HAVE MADE A SQL FIDDLE WORKING - CODE IS ALSO MENTIONED BELOW -
HERE YOU GO WITH THE WORKING FIDDLE -
WORKING DEMO
SQL CODE FOR REFERNECE -
CREATE TABLE ALERTS (ID INT, ALERT_TYPE INT, VALUE INT)
INSERT INTO ALERTS VALUES (58,2,1)
INSERT INTO ALERTS VALUES (58,1,0)
INSERT INTO ALERTS VALUES (104,1,1)
INSERT INTO ALERTS VALUES (104,2,0)
CREATE TABLE ALERTSVALUE (ID INT, gps INT,engine INT)
INSERT INTO ALERTSVALUE VALUES (58,1,0)
INSERT INTO ALERTSVALUE VALUES (104,0,1)
SELECT A.ID,
CASE A.ALERT_TYPE WHEN 2 THEN 1 ELSE 0 END AS GPS,
CASE A.ALERT_TYPE WHEN 1 THEN 1 ELSE 0 END AS ENGINE_VALUE,
A.VALUE FROM ALERTS A WHERE A.VALUE = 1
EDIT BASED ON COMMENT - TO MERGE THE ROWS FOR BOTH GPS AND ENGINE_VALUE:
SELECT X.ID,X.ALERT_TYPE as GPS,X.VALUE as ENGINE_VALUE
FROM (
SELECT ID,ALERT_TYPE ,VALUE ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY alert_type ) AS [Rank] FROM ALERTS
)X
WHERE [Rank]=1
SQL FIDDLE DEMO

how to find date difference when dates are places in different rows in same table?

I have a table::
ItemID VersionNo CreatedDate
-------------------------------
1 3 7/9/2010
1 2 7/3/2010
1 1 5/3/2010
1 0 3/3/2010
2 0 4/4/2010
3 1 4/5/2010
3 0 3/4/2010
...where Version 0 means .. its a newly produced item. Here I need to find time,(time gap between two versions) and add a column as process time.
like::
ItemID VersionNo CreatedDate ProcessTime
-------------------------------------------
1 3 7/9/2010 6Days or 6*24Hrs
1 2 7/3/2010 60Days
1 1 5/3/2010 2Days
1 0 3/3/2010 ''
2 0 4/4/2010 ''
3 1 4/5/2010 31Days
3 0 3/4/2010 ''
VersionNo's are not Fixed..means with time, it could increase... How to acheive the desire result in MS Access or in SQL-Server.
Thanks in advance for all your sincere efforts.
Thanks
How about (Access):
SELECT t.ItemID,
t.VersionNo,
t.CreatedDate, (
SELECT Top 1
CreatedDate
FROM Versions v
WHERE v.ItemID=t.ItemID
And v.VersionNo<t.VersionNo
ORDER BY VersionNo DESC) AS LastDate,
DateDiff("h",[LastDate],[CreatedDate]) AS DiffHrs,
DateDiff("d",[LastDate],[CreatedDate]) AS DiffDays
FROM Versions t
Join the table with itself, like this (SQL Server):
-- create the table and your data
create table #x (ItemID int, VersionNo int, CreatedDate datetime)
go
insert into #x
select 1, 3 ,'7/9/2010'
union all select 1 ,2 ,'7/3/2010'
union all select 1 ,1 ,'5/3/2010'
union all select 1 ,0 ,'3/3/2010'
union all select 2 ,0 ,'4/4/2010'
union all select 3 ,1 ,'4/5/2010'
union all select 3 ,0 ,'3/4/2010'
go
-- The query
select v2.ItemID, v2.VersionNo, datediff(dd, v1.CreatedDate, v2.CreatedDate)
from #x v1, #x v2
where v1.ItemID = v2.ItemID and v1.VersionNo + 1 = v2.VersionNo
Here it is in Access SQL, using 3 queries, one for each step.
Query1, self-join on itemID where versionNo is smaller:
SELECT t1.itemID, t1.versionNo, t1.created, t2.versionNo AS t2Version
FROM Table1 AS t1 INNER JOIN Table1 AS t2 ON t1.itemID = t2.itemID
WHERE (t2.versionNo)<[t1].[versionNo];
Query2, limit to max of smaller versionNos:
SELECT q1.itemID, q1.versionNo, q1.created, Max(q1.t2Version) AS MaxOft2Version
FROM Query1 AS q1
GROUP BY q1.itemID, q1.versionNo, q1.created;
Query3, now do datediff:
SELECT q2.itemID, q2.versionNo, q2.created, q2.MaxOft2Version, t1.created,
DateDiff("d",[t1].[created],[Q2].[created]) AS daysdiff
FROM Query2 AS q2 INNER JOIN Table1 AS t1
ON (q2.MaxOft2Version = t1.versionNo)
AND (q2.itemID = t1.itemID);
SQL Server 2005, to handle the case where there are gaps in VersionNo.
-- Declare a query that extends your table with a new column
-- that is the sequentially numbered representation of VersionNo.
-- This could be a view, but I used a CTE. I am going to use this
-- query twice below.
WITH Sequential AS (select *,
RANK() over (partition by ItemId order by VersionNo) as SequentialVersionNo
from #T as x
)
select
v.ItemID, v.VersionNo, v.SequentialVersionNo, v.CreatedDate,
DATEDIFF(day, vPrior.CreatedDate, v.CreatedDate) as ProcessTime
from Sequential as v
left outer join Sequential as vPrior
on v.ItemID=vPrior.ItemID
and v.SequentialVersionNo = vPrior.SequentialVersionNo+1;