Pivot query is returning all the values in T-SQL - sql

I am having a requirement that I want to build a pivot query for my table which is shown in screen shot.
This is my table
I want to make the rows values into columns.
My try:
SELECT rno, NO,Description,CarNo,ID from
(
select row_number()over(partition by columnname order by rno)rno, columnname,value
from mytable
group by rno, columnname,value
) x
pivot
(
max(value)
for columnname in (NO,Description,CarNo,ID)
) p
Actual Output:
Expected Output:
NO | Description | CradNo | ID
---------------------------------------------
Part1 | desc1 | Card1 | 1
Part2 | desc2 | Card1 | 1

You can use this.
DECLARE #mytable TABLE(columnname VARCHAR(20), value VARCHAR(10), rno INT)
INSERT INTO #mytable
VALUES
('ID', '1', 1),
('NO', 'Part1', 1),
('NO', 'Part2', 1),
('Description', 'desc1', 1),
('Description', 'desc2', 1),
('CarNo', 'car1', 1)
;WITH CTE AS
(SELECT * FROM
(SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY columnname Order BY rno, value)
FROM #mytable) SRC
PIVOT ( MAX(value) FOR columnName IN ([ID], [NO], [Description], [CarNo]) ) PVT
)
SELECT
ISNULL( T1.NO, T2.NO) NO
, ISNULL( T1.Description, T2.Description) Description
, ISNULL( T1.CarNo, T2.CarNo) CarNo
, ISNULL( T1.ID, T2.ID) ID
FROM CTE T1 OUTER APPLY ( SELECT TOP 1 * FROM CTE WHERE CTE.RN < T1.RN ) T2
Result:
NO Description CarNo ID
---------- ----------- ---------- ----------
Part1 desc1 car1 1
Part2 desc2 car1 1

Related

How to get first value from first row and last value from last row in SQL Server

I have a table in SQL Server as below
+---+-------------+-------------+-------------+
|Id | FromCity | ToCity | RequestId |
+---+-------------+-------------+-------------+
| 1 | Mysore | Atlanta | 12 |
+---+-------------+-------------+-------------+
| 2 | Atlanta | Singapore | 12 |
+---+-------------+-------------+-------------+
| 3 | Singapore | Pakistan | 12 |
+---+-------------+-------------+-------------+
| 4 | Pakistan | Myscot | 12 |
+---+-------------+-------------+-------------+
| 5 | Rome | Singapore | 13 |
+---+-------------+-------------+-------------+
Here, I am trying to get a value as From and To Cities (Round Trip Data) (i.e. Mysore - > Myscot for the RequestId =12). Ho to get this value?
When I pass RequestId=12, I should get the value as Mysore -> Myscot
I Tried as below:
SELECT MIN(FromCity) [From],
MAX(ToCity) [To]
FROM MyTable
WHERE RequestId = 12
But I'm getting Mysore -> Singapore (based on string MIN and MAX).
Check This :
SELECT M.FromCity [From],
M2.ToCity [To]
FROM #MyTable M
Inner join (select Min(ID) min ,Max(ID) max from #MyTable M2 WHERE M2.RequestId = 12 )
A on A.Min =ID
Inner join #MyTable M2 on M2.ID=A.Max
Output :
SELECT RequestId,
f.FromCity [From],
t.ToCity [To]
FROM MyTable t
CROSS APPLY (
SELECT TOP (1) FromCity
FROM MyTable
WHERE RequestId = t.RequestId
ORDER BY Id ASC
) f
CROSS APPLY (
SELECT TOP (1) ToCity
FROM MyTable
WHERE RequestId = t.RequestId
ORDER BY Id DESC
) t
WHERE RequestId = 12
The only logic I see is using the Id from your table and do something like this. Using CTE you will find the MIN and MAX id per request, that means from city - to city.
And after that you join your table to the CTE to find the actual values.
declare #tbl as table
([Id] int, [FromCity] varchar(9), [ToCity] varchar(9), [Date Created] datetime, [RequestId] int)
;
INSERT INTO #tbl
([Id], [FromCity], [ToCity], [Date Created], [RequestId])
VALUES
(1, 'Mysore', 'Atlanta', '2018-10-05 15:10:00', 12),
(2, 'Atlanta', 'Singapore', '2018-10-06 15:10:00', 12),
(3, 'Singapore', 'Pakistan', '2018-10-07 15:10:00', 12),
(4, 'Pakistan', 'Myscot', '2018-10-07 15:10:00', 12),
(5, 'UK', 'Atlanta', '2018-10-06 15:10:00', 13),
(6, 'Atlanta', 'Singapore', '2018-10-06 15:10:00', 13),
(7, 'Singapore', 'Italy', '2018-10-23 15:10:00', 13);
;with cte as (
select
MIN(Id) as [start]
,MAX(Id) as [end]
,RequestId
from #tbl
group by requestID
)
select
t1.FromCity
,t1.[Date Created]
,t2.ToCity
,t2.[Date Created]
from cte
inner join #tbl t1
on t1.Id = cte.[start]
and t1.RequestId = cte.RequestId
inner join #tbl t2
on t2.Id = cte.[end]
and t2.RequestId = cte.RequestId
Update: based on #Panagiotis Kanavos comment you can simplify the query like this
;with cte as (
select
MIN(Id) as [start]
,MAX(Id) as [end]
from #tbl
where RequestId = 12 ---> here you can use a variable containing the requestID
)
select
t1.FromCity
--,t1.[Date Created]
,t2.ToCity
--,t2.[Date Created]
from cte
inner join #tbl t1
on t1.Id = cte.[start]
inner join #tbl t2
on t2.Id = cte.[end]
If Id in main table is used to describe travel order then something like this will work:
SELECT startLocation.FromCity [From], endLocation.ToCity AS [To]
FROM (
SELECT MIN(Id) AS StartLocationId, MAX(Id) AS EndLocationId
FROM MyTable
WHERE RequestId = 12
) AS a
INNER JOIN MyTable AS startLocation ON a.StartLocationId = startLocation.Id
INNER JOIN MyTable AS endLocation ON a.EndLocationId = endLocation.Id
This is a example of solution for situation when ids are just ids and don`t match travel order:
declare #tbl as table
([Id] int, [FromCity] varchar(9), [ToCity] varchar(9), [Date Created] datetime, [RequestId] int)
;
INSERT INTO #tbl
([Id], [FromCity], [ToCity], [Date Created], [RequestId])
VALUES
(19, 'Mysore', 'Atlanta', '2018-10-05 15:10:00', 12),
(22, 'Atlanta', 'Singapore', '2018-10-06 15:10:00', 12),
(1, 'Singapore', 'Pakistan', '2018-10-07 15:10:00', 12),
(4, 'Pakistan', 'Myscot', '2018-10-07 15:10:00', 12),
(5, 'UK', 'Atlanta', '2018-10-06 15:10:00', 13),
(0, 'Atlanta', 'Singapore', '2018-10-06 15:10:00', 13),
(-1, 'Singapore', 'Italy', '2018-10-23 15:10:00', 13)
;
select * from #tbl
declare #Id int = 12
declare #FromStart nvarchar(255), #ToStart nvarchar(255)
declare #StartResult nvarchar(255), #ToResult nvarchar(255)
declare #StartResultFound bit = 0, #ToResultFound bit = 0
-- select random starting point
select #FromStart = [FromCity], #ToStart = [ToCity] from #tbl where [RequestId] = #Id
ORDER BY NEWID()
select #FromStart, #ToStart
while (#StartResultFound = 0)
begin
if exists (select top 1 1 from #tbl where [RequestId] = #Id and [ToCity] = #FromStart)
begin
select top 1 #FromStart = [FromCity] from #tbl where [RequestId] = #Id and [ToCity] = #FromStart
end
else
begin
set #StartResultFound = 1
set #StartResult = #FromStart
end
end
while (#ToResultFound = 0)
begin
if exists (select top 1 1 from #tbl where [RequestId] = #Id and [FromCity] = #ToStart)
begin
select top 1 #ToStart = [ToCity] from #tbl where [RequestId] = #Id and [FromCity] = #ToStart
end
else
begin
set #ToResultFound = 1
set #ToResult = #ToStart
end
end
select #StartResult, #ToResult
maybe this is whar you need
DECLARE
#t TABLE (
ID INT
, FromCity VARCHAR(250)
, ToCity VARCHAR(250)
, RequestId INT
)
INSERT INTO #t VALUES
(1,'Mysore','Atlanta',12)
,(2,'Atlanta','Singapore',12)
,(3,'Singapore','Pakistan',12)
,(4,'Pakistan','Myscot',12)
,(5,'Rome','Singapore',13)
SELECT DISTINCT
ISNULL(FromCity, ISNULL(NextCity, PreCity) ) FromCity
, ISNULL(ToCity, ISNULL(NextCity2, PreCity2) ) FromCity
FROM (
SELECT
CASE WHEN RN = 1 THEN FromCity END FromCity
, CASE WHEN RN = CNT THEN ToCity END ToCity
, LEAD(CASE WHEN RN = 1 THEN FromCity END) OVER(PARTITION BY RequestId ORDER BY ID) NextCity
, LEAD(CASE WHEN RN = CNT THEN ToCity END) OVER(PARTITION BY RequestId ORDER BY ID) NextCity2
, LAG(CASE WHEN RN = 1 THEN FromCity END) OVER(PARTITION BY RequestId ORDER BY ID) PreCity
, LAG(CASE WHEN RN = CNT THEN ToCity END) OVER(PARTITION BY RequestId ORDER BY ID) PreCity2
FROM (
SELECT
*, ROW_NUMBER() OVER(PARTITION BY RequestId ORDER BY ID) RN
, COUNT(ToCity) OVER(PARTITION BY RequestId) CNT
FROM #t
) D
WHERE
RN = 1 OR RN = CNT
) C
A recursive version, not depending on Ids order. The idea is to build the biggest chain of trip connected by To / From cities.
WITH cte1 AS (
-- start with all trips as 1 hop chain
SELECT [FromCity], [ToCity], 1 AS HOP
FROM Mytable
WHERE REQUESTID = 12
UNION ALL
-- recursively add one hop if we find a trip ending in start city of the chain
SELECT T.[FromCity], cte1.[ToCity], cte1.HOP + 1
FROM cte1
JOIN Mytable t
ON t.[ToCity] = cte1.[FromCity]
WHERE t.REQUESTID = 12
)
, cte2 AS (
-- order by number of hops
SELECT [FromCity], [ToCity], ROW_NUMBER() OVER (ORDER BY HOP DESC) AS N
FROM cte1
)
-- choose the chain with the more hops
SELECT [FromCity], [ToCity]
FROM cte2
WHERE N = 1
try this code
SELECT MIN(Id) [From],
MAX(Id) [To]
FROM MyTable
WHERE RequestId = 12

How to get row values in to columns in sql server with based on id

I have the following table:
id Prefix FisrtName LastName
--------------------------------------------
123 Mr Lynn Berg
123 Ms Madeline Owen
123 Mrs Zelenia Sellers
101 Mrs Jesse Vincent
101 Mr Chaim Long
The result table should look like this
id name1 name2 name2
-----------------------------------------------------------
123 Mr Lynn Berg Ms MadelineOwen Mrs Zelenia Sellers
101 Mrs Jesse Vincent Mr Chaim Long
How could I achieve this result in SQL Server? Can I use pivot function?
Please help
You can also do conditional aggregation :
select id, max(case when seq = 1 then Name end) as Name1,
max(case when seq = 2 then Name end) as Name2,
max(case when seq = 3 then Name end) as Name3
from (select id, concat(Prefix,' ',FisrtName,' ',LastName) as Name,
row_number() over (partition by id order by (select null)) as seq
from table
) t
group by id;
Try this simple pivot:
declare #tbl table (id int, Prefix varchar(3), FirstName varchar(20), LastName varchar(20));
insert into #tbl values
(123, 'Mr', 'Lynn', 'Berg'),
(123, 'Ms', 'Madeline', 'Owen'),
(123, 'Mrs', 'Zelenia', 'Sellers'),
(101, 'Mrs', 'Jesse', 'Vincent'),
(101, 'Mr', 'Chaim', 'Long');
select id, [1] [Name1], [2] [Name2], [3] [Name3] from (
select id,
ROW_NUMBER() over (partition by id order by (select null)) rn,
Prefix + ' ' + FirstName + ' ' + LastName [FullName]
from #tbl
) a pivot (
max(fullname) for rn in ([1],[2],[3])
) b;
CREATE TABLE #Table1 (
id INT
,Prefix VARCHAR(3)
,FisrtName VARCHAR(8)
,LastName VARCHAR(7)
);
INSERT INTO #Table1
VALUES (
123
,'Mr'
,'Lynn'
,'Berg'
)
,(
123
,'Ms'
,'Madeline'
,'Owen'
)
,(
123
,'Mrs'
,'Zelenia'
,'Sellers'
)
,(
101
,'Mrs'
,'Jesse'
,'Vincent'
)
,(
101
,'Mr'
,'Chaim'
,'Long'
)
SELECT *
FROM #Table1
SELECT id
,[1] [Name1]
,[2] [Name2]
,[3] [Name3]
FROM (
SELECT id
,CONCAT (
PREFIX
,FISRTNAME
,LASTNAME
) AS Namm
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY (
id
)
) AS rn
FROM #TABLE1
) a
pivot(max(Namm) FOR rn IN (
[1]
,[2]
,[3]
)) b

Conditional selection of RowNum in SQL

I have written a query which returns me following data.
ID EmpFirstName EmpLastName RowNum
1 X Y 1
2 A B 1
3 A B 2
Now I want all records where RowNum is >1. For example, in this case I need 2 and 3 record in output.
If I put condition RowNum >1 then I will get only third record but I want 2 as well.
Assuming your query is this:
select ID, EmpFirstName, EmpLastName,
ROW_NUMBER() OVER (PARTITION BY EmpFirstName, EmpLastName ORDER BY ID) AS RowNum
FROM aTable
This is a classic query used to filter out any duplicate values.
In order effectively select all the records with the duplicate values I can suggest using the COUNT() window function:
;with a as (
select ID, EmpFirstName, EmpLastName,
ROW_NUMBER() OVER (PARTITION BY EmpFirstName, EmpLastName ORDER BY ID) AS RowNum,
COUNT(*) OVER (PARTITION BY EmpFirstName, EmpLastName) AS cnt
FROM aTable
)
SELECT * FROM a where cnt > 1
ORDER BY EmpFirstName, EmpLastName
To test it use this query:
drop table #tmp
CREATE table #tmp (ID int , EmpFirstName varchar(10) , EmpLastName varchar(10))
go
INSERT INTO #tmp VALUES
(1,'X','Y' )
,(2,'A','B')
,(3,'A','B')
,(4,'A','C')
,(5,'B','C')
,(6,'B','C')
;with a as (
select ID, EmpFirstName, EmpLastName,
ROW_NUMBER() OVER (PARTITION BY EmpFirstName, EmpLastName ORDER BY ID) AS RowNum,
COUNT(id) OVER (PARTITION BY EmpFirstName, EmpLastName) AS cnt
FROM #tmp
)
SELECT * FROM a where cnt > 1
ORDER BY EmpFirstName, EmpLastName
Result:
ID EmpFirstName EmpLastName RowNum cnt
----------- ------------ ----------- -------------------- -----------
2 A B 1 2
3 A B 2 2
5 B C 1 2
6 B C 2 2
I make sample data and use this query
CREATE table #tmp (ID int , EmpFirstName varchar(10) , EmpLastName varchar(10) ,RowNum int)
INSERT INTO #tmp VALUES
(1,'X','Y',1)
,(2,'A','B',1)
,(3,'A','B',2)
SELECT ID,EmpFirstName,EmpLastName,RowNum
FROM (
SELECT *
,ROW_NUMBER() OVER (ORDER BY ID) AS [NEWrownum]
FROM #tmp
) q
WHERE q.NEWrownum > 1
try this,
DECLARE #Result TABLE (ID INT, EmpFirstName VARCHAR(10), EmpLastName VARCHAR(10), RowNum INT)
INSERT INTO #Result
VALUES
(1, 'X', 'Y', 1)
,(2, 'A', 'B', 1)
,(3, 'A', 'B', 2)
SELECT r1.*
FROM #Result r1
INNER JOIN (SELECT * -- get duplicate records
FROM #Result
WHERE RowNum = 2
) as r2 ON r1.EmpFirstName = r2.EmpFirstName
AND r1.EmpLastName = r2.EmpLastName

SQL: Deleting row which values already exist

I have a table that look like this:
ID | DATE | NAME | VALUE_1 | VALUE_2
1 | 27.11.2015 | Homer | A | B
2 | 27.11.2015 | Bart | C | B
3 | 28.11.2015 | Homer | A | C
4 | 28.11.2015 | Maggie | C | B
5 | 28.11.2015 | Bart | C | B
I currently delete duplicate rows (thank to this thread) using this code :
WITH cte AS
(SELECT ROW_NUMBER() OVER (PARTITION BY [VALUE_1], [VALUE_2]
ORDER BY [DATE] DESC) RN
FROM [MY_TABLE])
DELETE FROM cte
WHERE RN > 1
But this code don't delete exactly the lines I want. I would like to delete only rows which values already exist so in my example I would like to delete only line 5 because line 2 have the same values and is older.
Code to create my table and insert values:
CREATE TABLE [t_diff_values]
([id] INT IDENTITY NOT NULL PRIMARY KEY,
[date] DATETIME NOT NULL,
[name] VARCHAR(255) NOT NULL DEFAULT '',
[val1] CHAR(1) NOT NULL DEFAULT '',
[val2] CHAR(1) NOT NULL DEFAULT '');
INSERT INTO [t_diff_values] ([date], [name], [val1], [val2]) VALUES
('2015-11-27','Homer', 'A','B'),
('2015-11-27','Bart', 'C','B'),
('2015-11-28','Homer', 'A','C'),
('2015-11-28','Maggie', 'C','B'),
('2015-11-28','Bart', 'C','B');
You need to add one more CTE where you will index all islands and then apply your duplicate logic in second CTE:
DECLARE #t TABLE
(
ID INT ,
DATE DATE ,
VALUE_1 CHAR(1) ,
VALUE_2 CHAR(1)
)
INSERT INTO #t
VALUES ( 1, '20151127', 'A', 'B' ),
( 2, '20151128', 'C', 'B' ),
( 3, '20151129', 'A', 'B' ),
( 4, '20151130', 'A', 'B' );
WITH cte1
AS ( SELECT * ,
ROW_NUMBER() OVER ( ORDER BY date)
- ROW_NUMBER() OVER ( PARTITION BY VALUE_1, VALUE_2 ORDER BY DATE) AS gr
FROM #t
),
cte2
AS ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY VALUE_1, VALUE_2, gr ORDER BY date) AS rn
FROM cte1
)
DELETE FROM cte2
WHERE rn > 1
SELECT *
FROM #t
Try this
CREATE TABLE [dbo].[Employee](
[ID] INT NOT NULL,
[Date] DateTime NOT NULL,
[VAL1] varchar(20) NOT NULL,
[VAL2] varchar(20) NOT NULL
)
INSERT INTO [dbo].[Employee] VALUES
(1,'2015-11-27 10:44:33.087','A','B')
INSERT INTO [dbo].[Employee] VALUES
(2,'2015-11-28 10:44:33.087','C','B')
INSERT INTO [dbo].[Employee] VALUES
(3,'2015-11-29 10:44:33.087','A','B')
INSERT INTO [dbo].[Employee] VALUES
(4,'2015-11-30 10:44:33.087','A','B')
with cte as(
select
*,
rn = row_number() over(partition by [VAL1], [VAL2]
ORDER BY [DATE] DESC),
cc = count(*) over(partition by [VAL1], [VAL2])
from [Employee]
)
delete
from cte
where
rn > 1 and rn < cc
select * from [Employee]
You could use this query:
WITH cte AS
(
SELECT RN = ROW_NUMBER() OVER (ORDER BY ID)
, *
FROM #data
)
DELETE FROM c1
--SELECT *
FROM CTE c1
INNER JOIN CTE c2 ON c1.RN +1 = c2.RN AND c1.VALUE_1 = c2.VALUE_1 AND c1.VALUE_2 = c2.VALUE_2
Here I order them by ID. If the next one (RN+1) has similar V1 and V2, it is deleted.
Output:
ID DATE VALUE_1 VALUE_2
1 2015-11-27 A B
2 2015-11-28 C B
4 2015-11-30 A B
Data:
declare #data table(ID int, [DATE] date, VALUE_1 char(1), VALUE_2 char(1));
insert into #data(ID, [DATE], VALUE_1, VALUE_2) values
(1, '20151127', 'A', 'B'),
(2, '20151128', 'C', 'B'),
(3, '20151129', 'A', 'B'),
(4, '20151130', 'A', 'B');

SQL Server: How to convert column into one row?

I have one column with different values depending on result.
I would like to return one row with fix columns.
The column output as follows:
Group | Item
--------------
G1 | 1
G1 | 2
G2 | 3
I would like to output as:
Group | Item1 | Item2 | Item3
-----------------------------
G1 | 1 | 2 | N/A
G2 | 3 | N/A | N/A
Please Help..
Thanks in Advance...
Because every group may have three items maximum, I would use PIVOT operator:
DECLARE #Test TABLE
(
RowID INT IDENTITY(1,1) PRIMARY KEY
,[Group]VARCHAR(10) NOT NULL
,Item INT NOT NULL
,UNIQUE ([Group], Item)
);
INSERT #Test VALUES ('G1', 1);
INSERT #Test VALUES ('G1', 2);
INSERT #Test VALUES ('G2', 3);
WITH PivotSource
AS
(
SELECT t.[Group], t.Item
,ROW_NUMBER() OVER(PARTITION BY t.[Group] ORDER BY t.RowID) RowNumber
FROM #Test t
)
SELECT pvt.[Group]
,Item1 = ISNULL( CONVERT(VARCHAR(11), pvt.[1]) , 'N/A')
,Item2 = ISNULL( CONVERT(VARCHAR(11), pvt.[2]) , 'N/A')
,Item3 = ISNULL( CONVERT(VARCHAR(11), pvt.[3]) , 'N/A')
FROM PivotSource src
PIVOT ( MAX(src.Item) FOR src.RowNumber IN ([1], [2], [3]) ) pvt;
Results:
Group Item1 Item2 Item3
---------- ----------- ----------- -----------
G1 1 2 N/A
G2 3 N/A N/A
I guess something like this will work:
SELECT t1.[Group], t1.Item, t2.Item, t3.Item
from tbl t1, tbl t2, tbl t3
where t1.[Group] = t2.[Group] and t1.[Group] = t3.[Group]
and t1.Item < t2.Item and t2.Item < t3.Item
and t1.[Group] in (select [Group] from tbl group by [Group] having COUNT(*) = 3)
union
SELECT t1.[Group], t1.Item, t2.Item, NULL
from tbl t1, tbl t2
where t1.[Group] = t2.[Group]
and t1.Item < t2.Item
and t1.[Group] in
(select [Group] from tbl group by [Group] having COUNT(*) = 2)
union
select tbl.[Group], tbl.Item, NULL, NULL
from tbl
where [Group] in (select [Group] from tbl group by [Group] having COUNT(*) = 1)
As you have a fixed number of output columns you can do some tricks with ROW_NUMBER() and over (partition by ... order by ...).
select child1.[Group], child1.Item as Item1, child2.Item as Item2, child3.Item as Item3 from
(select [Group], Item from
(select [Group], Item, (ROW_NUMBER() over(partition by [group] order by item)) as rownum from GroupTable) as child1_inner
where child1_inner.rownum = 1) as child1
left outer join
(select [Group], Item from
(select [Group], Item, (ROW_NUMBER() over(partition by [group] order by item)) as rownum from GroupTable) as child2_inner
where child2_inner.rownum = 2) as child2
on child1.[Group] = child2.[Group]
left outer join
(select [Group], Item from
(select [Group], Item, (ROW_NUMBER() over(partition by [group] order by item)) as rownum from GroupTable) as child3_inner
where child3_inner.rownum = 3) as child3
on child1.[Group] = child3.[Group]
This returns null instead of N/A, but you can fix that with COALESCE.