SQL Server: How to convert column into one row? - sql

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.

Related

Left join from 2 tables with same ID

I have tabl1e1 and table2 with data
table1
location costA
a 5
a 10
a 15
b 11
b 12
table2
Location CostB
a 100
b 100
My goal to get the result
location costA costB
a 5 100
a 10
a 15
b 11 50
b 12
My query
select T1.location, T1.cost
from (
select location, cost
, row_number() over ( partition by location order by cost) rownumber
from table1
) T1 left join (
select location, cost
, row_number() over ( partition by location order by cost ) rownumber
from table2
) T2 on T2.location = T2.cost and T1.rownumber = T2.rownumber
I got
location costA missing costB column
a 5
a 10
a 15
b 11
b 12
Not sure why but can you point out the missing one. Thank you.
First of all you are expecting three columuns in result and your select statement contains only 2.
select T1.Location, T1.Cost
2nd the join should be
T2 on T1.[location] = T2.[location] and T1.rownumber = T2.rownumber
Below is the complete working example
DECLARE #table1 as table
(
[location] char,
costA int
)
DECLARE #table2 as table
(
[location] char,
costB int
)
INSERT INTO #table1
VALUES
('a', 5)
,('a', 10)
,('a', 15)
,('b', 11)
,('b', 12)
INSERT INTO #table2
VALUES
('a', 100)
,('b', 100)
select T1.[location], T1.costA, T2.costB
from (
select [location], costA
, row_number() over ( partition by location order by costA) rownumber
from #table1
) T1 left join (
select [location], costB
, row_number() over ( partition by location order by costB ) rownumber
from #table2
) T2 on T1.[location] = T2.[location] and T1.rownumber = T2.rownumber
The join
T2 on T2.location = T2.cost and T1.rownumber = T2.rownumber
should be on
T2 on T1.location = T2.location and T1.rownumber = T2.rownumber
select
T1.location,
T1.costA,
T2.costB
from (
select
location,
costA,
row_number() over ( partition by location order by costA) rownumber
from table1
) T1
left join (
select
location,
costB,
row_number() over ( partition by location order by costB ) rownumber
from table2
) T2
on T1.location = T2.location and T1.rownumber = T2.rownumber
GO
location | costA | costB
:------- | ----: | ----:
a | 5 | 100
a | 10 | null
a | 15 | null
b | 11 | 100
b | 12 | null
db<>fiddle here

Is it possible to find (in an ordered table) multiple rows in sequence? [duplicate]

This question already has an answer here:
Compare Current Row with Previous/Next row in SQL Server
(1 answer)
Closed 4 years ago.
If I have a table ordered by ID like so:
|---------------------|------------------|
| ID | Key |
|---------------------|------------------|
| 1 | Foo |
|---------------------|------------------|
| 2 | Bar |
|---------------------|------------------|
| 3 | Test |
|---------------------|------------------|
| 4 | Test |
|---------------------|------------------|
Is there a way to detect two rows that match a where clause in sequence?
For example, in the table above, I would like to see if any two rows in succession have a Key of 'test'.
Is this possible in SQL?
Another option is a variation of Gaps-and-Islands
Example
Declare #YourTable Table ([ID] int,[Key] varchar(50))
Insert Into #YourTable Values
(1,'Foo')
,(2,'Bar')
,(3,'Test')
,(4,'Test')
Select ID_R1 = min(ID)
,ID_R2 = max(ID)
,[Key]
From (
Select *
,Grp = ID-Row_Number() over(Partition By [Key] Order by ID)
From #YourTable
) A
Group By [Key],Grp
Having count(*)>1
Returns
ID_R1 ID_R2 Key
3 4 Test
EDIT - Just in case the IDs are NOT Sequential
Select ID_R1 = min(ID)
,ID_R2 = max(ID)
,[Key]
From (
Select *
,Grp = Row_Number() over(Order by ID)
-Row_Number() over(Partition By [Key] Order by ID)
From #YourTable
) A
Group By [key],Grp
Having count(*)>1
You can try to use ROW_NUMBER window function check the gap.
SELECT [Key]
FROM (
SELECT *,ROW_NUMBER() OVER(ORDER BY ID) -
ROW_NUMBER() OVER(PARTITION BY [Key] ORDER BY ID) grp
FROM T
)t1
GROUP BY [Key]
HAVING COUNT(grp) = 2
You can do a self join as
CREATE TABLE T(
ID INT,
[Key] VARCHAR(45)
);
INSERT INTO T VALUES
(1, 'Foo'),
(2, 'Bar'),
(3, 'Test'),
(4, 'Test');
SELECT MIN(T1.ID) One,
MAX(T2.ID) Two,
T1.[Key] OnKey
FROM T T1 JOIN T T2
ON T1.[Key] = T2.[Key]
AND
T1.ID <> T2.ID
GROUP BY T1.[Key];
Or a CROSS JOIN as
SELECT MIN(T1.ID) One,
MAX(T2.ID) Two,
T1.[Key] OnKey
FROM T T1 CROSS JOIN T T2
WHERE T1.[Key] = T2.[Key]
AND
T1.ID <> T2.ID
GROUP BY T1.[Key]
Demo
You can use the LEAD() window function, as in:
with
x as (
select
id, [key],
lead(id) over(order by id) as next_id,
lead([key]) over(order by id) as next_key
from my_table
)
select id, next_id from x where [key] = 'test' and next_key = 'test'

Transform rows to columns(probably pivot)

Here is my requirement:
create table #TEMP
(
KEY_VALUE VARCHAR(100)
,NAME VARCHAR(100)
,AMOUNT INT
,QUANTITY INT
)
INSERT INTO #TEMP
VALUES
('K1','ABC',100,10000),
('K2','XYZ',200,20000),
('K1','ABC',50,5000),
('K2','XYZ',300,30000),
('K3','MNO',50,500)
select * from #TEMP
Because the KEY_VALUE COLUMN matches for 2 rows(K1 and K2), I want to transform it to something as below:
KEY_VALUE NAME AMOUNT_1 AMOUNT_2 QUANTITY_1 QUANTITY_2
K1 ABC 100 50 10000 5000
K2 XYZ 200 300 20000 30000
K3 MNO 50 NULL 500 NULL
What/How do I do that? Please let me know if my question is not clear.
You can use ROW_NUMBER() & do conditional aggregation :
SELECT KEY_VALUE, NAME,
MAX(CASE WHEN seq = 1 THEN AMOUNT END) AS AMOUNT_1,
MAX(CASE WHEN seq = 2 THEN AMOUNT END) AS AMOUNT_2,
MAX(CASE WHEN seq = 1 THEN QUANTITY END) AS QUANTITY_1,
MAX(CASE WHEN seq = 2 THEN QUANTITY END) AS QUANTITY_2
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY KEY_VALUE ORDER BY AMOUNT) AS seq
FROM #TEMP t
) t
GROUP BY KEY_VALUE, NAME;
EDIT : If you want to do further calculation then you can use CTE :
WITH CTE AS (
<query>
)
SELECT C.*,
C.AMOUNT_1 - C.AMOUNT_1 AS Diff_Amt
FROM CTE C;
Prepare data
CREATE TABLE #t (
key_value varchar(10),
name varchar(10),
amount int,
quantity int
);
INSERT INTO #t
VALUES
('K1', 'ABC', 100, 10000),
('K2', 'XYZ', 200, 20000),
('K1', 'ABC', 50, 5000),
('K2', 'XYZ', 300, 30000),
('K3', 'MNO', 50, 500);
Querying
WITH t1 (id, key_value, name, amount, quantity)
AS (
SELECT ROW_NUMBER() OVER (ORDER BY key_value), key_value, name, amount, quantity FROM #t
),
t2
AS (
SELECT MIN(id) AS min_id, MAX(id) AS max_id, key_value, name
FROM t1
GROUP BY key_value, name
),
t3
AS (
SELECT t2.key_value, t2.name,
t11.amount AS amount_1, t11.quantity AS quantity_1,
t12.amount AS amount_2, t12.quantity AS quantity_2
FROM t2
INNER JOIN
t1 t11 ON t11.key_value = t2.key_value AND t11.name = t2.name
AND t11.id = t2.min_id
LEFT JOIN
t1 t12 ON t12.key_value = t2.key_value AND t12.name = t2.name
AND t12.id = t2.max_id AND t12.id <> t2.min_id
)
SELECT * FROM t3
Result
key_value name amount_1 quantity_1 amount_2 quantity_2
---------- ---------- ----------- ----------- ----------- -----------
K1 ABC 100 10000 50 5000
K2 XYZ 300 30000 200 20000
K3 MNO 50 500 NULL NULL

Pivot query is returning all the values in T-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

How to select a value on the first row and last on SQL?

I have a table like this:
ID | persid | value1 | value2
---+--------+---------+---------
1 | A | string1 | string2
2 | A | string3 | string4
3 | A | string5 | string6
I need to return something like this using GROUP BY:
Field1 | field2 | field3
-------+---------+----------
A | string1 | string6
I'm using SQL server 2008,
Is this possible?
I would use conditional aggregation:
select persid,
max(case when seqnum_asc = 1 then value1 end) as value1,
max(case when seqnum_desc = 1 then value2 end) as value2
from (select t.*,
row_number() over (partition by persid order by id) as seqnum_asc,
row_number() over (partition by persid order by id desc) as seqnum_desc
from t
) t
group by persid;
You can try something like this:
create table tbl (id int, persid varchar(10), value1 varchar(10), value2 varchar(10));
insert into tbl (id, persid, value1, value2) values (1, 'A', 'string1', 'string2');
insert into tbl (id, persid, value1, value2) values (2, 'A', 'string3', 'string4');
insert into tbl (id, persid, value1, value2) values (3, 'A', 'string5', 'string6');
select tbl.persid, tbl.value1, persmax.value2
from tbl
inner join tbl persmax on persmax.id = (select max(id) from tbl where persid='A')
where tbl.persid='A'
and tbl.id = (select min(id) from tbl where persid='A')
I think you can use a query like this:
select top(1) yourTable.persid field1, yourTable.value1 field2, t.value2 field3
from yourTable
cross join (
select top(1) value2
from yourTable
order by ID desc
) t
order by ID;
How about this?
select distinct a, b, c from
(select mytbl.persid as a, mytbl.value1 as b from mytbl,
(select persid, min(id) as id from mytbl group by persid) as t1
where mytbl.id = t1.id and mytbl.persid = t1.persid) as t2,
(select mytbl.persid as d, mytbl.value2 as c from mytbl,
(select persid, max(id) as id from mytbl group by persid) as t3
where mytbl.id = t3.id and mytbl.persid = t3.persid) as t4
where t2.a = t4.d