How can I combine 2 rows to 1 row in SQL? - sql

I have some data like this:
ID
Color
1
Pink
1
Blue
2
Red
2
Green
I want it to look like this:
ID
Color1
Color2
1
Pink
Blue
2
Red
Green
Any help is appreciated! Thank you!

If you have a known, or maximum number of columns a simple PIVOT or conditional aggregation should do the trick, otherwise, you would need Dynamic SQL
Example PIVOT
Select *
From ( Select ID
,Col = concat('Color',row_number() over (partition by ID order by ID)
,Val = Color
From YourTable
) src
Pivot (max(Val) for Col in ([Color1]
,[Color2]
)
) pvt
Example Conditional Aggregation
Select ID
,Color1 = max(case when RN=1 then Color end )
,Color2 = max(case when RN=2 then Color end )
From (
Select ID
,Color
,RN = row_number() over (partition by ID order by ID)
From YourTable
) A
Group By ID
Note:
The order by ID portion in row_number() could be any other column like Color ascending or descending.

Related

Selecting 3 rows into 3 columns in sql server

I am trying to select the 3 rows into 3 columns, but i get NULL values.
Here is my code so far:
SELECT * FROM
(
SELECT t_k
FROM m_t_k
WHERE p_id = 5 and t_k_id in (1,2,7)
) src
PIVOT(
MAX()
for t_k in ([1],[2],[3])
) piv
this is the result of the query without the PIVOT
and i want those rows to be on 3 columns
You could use ROW_NUMBER and a Cross Tab to achieve this. This is a bit of a guess, based on the query and image we have though, so it is untested:
SELECT MAX(CASE WHEN RN = 1 THEN sq.term_key END) AS term_key1,
MAX(CASE WHEN RN = 2 THEN sq.term_key END) AS term_key2,
MAX(CASE WHEN RN = 3 THEN sq.term_key END) AS term_key3
FROM (SELECT term_key,
ROW_NUMBER() OVER (ORDER BY term_key) AS RN
FROM mpos_term_key
WHERE profile_id = 5
AND term_keys_type_id IN (1, 2, 7)) sq;

select row values as column based on GROUP BY column

I have a table with data like this:
Id Value
-------------------------
01 Id01-Value1
01 Id01-Value2
02 Id02-Value1
02 Id02-Value2
03 Id03-Value1
What I want is
Id Value1 Value2
--------------------------------------
01 Id01-Value1 Id01-Value2
02 Id02-Value1 Id02-Value2
03 Id03-Value1
I tried sql PIVOT but it is not for this type of problem I think.
I think you can just use min() and max():
select id, min(value) as value1,
(case when min(value) <> max(value) then max(value) end) as value2
from t
group by id;
Try this Answer,
SELECT ID
,MAX(CASE WHEN RN=1 THEN Value ELSE '' END)Value1
,MAX(CASE WHEN RN=2 THEN Value ELSE '' END)Value2
FROM(
SELECT ID,Value
,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Value)RN
FROM Your_Table
)D
GROUP BY ID
ORDER BY ID
On the contrary, PIVOT is exactly what you need - if you can bear it's damned syntax! It is relatively flexible but also unwieldy.
SELECT
id
,[1] AS Value1
,[2] AS Value2
FROM
(
SELECT
id
,value
,ROW_NUMBER() OVER (PARTITION BY id ORDER BY value ASC) AS column_number
FROM
YOUR_TABLE_NAME
) AS src
PIVOT
(
MAX(value)
FOR column_number IN ([1],[2])
) AS pvt
ORDER BY
id
This will sort the rows in the value column alphabetically, and assigns column numbers accordingly in sequence (but you could include different logic, for example to nip the column number off the right-hand side of the value, or name the columns according to the value itself rather than numbering them). NULL values will be returned for any column that doesn't have a value.

SQL - sort of SUM with varchar

have a (weird) table looking like this
ID Version Value1 Value2 Value3
1 1 Shaft
1 2 steel xy
2 1 Knife somethins
2 3 Super
Want to merge, need to have this result, by using Value from the highest Version, that has content:
ID Value1 Value2 Value3
1 Shaft steel xy
2 Super Knife somethin
as far as I know Group using Max(Version) would bring the NULL values of highest Version row.
something like SUM?
Second try... There are probably shorter and nicer solutions, but it should work:
with
v1 as
(
select w1.id, w1.value1 from weird w1
where w1.value1 is not null
and w1.version=(select max(w11.version) from weird w11 where w11.id=w1.id and w11.value1 is not null)
),
v2 as
(
select w2.id, w2.value2 from weird w2
where w2.value2 is not null
and w2.version=(select max(w22.version) from weird w22 where w22.id=w2.id and w22.value2 is not null)
),
v3 as
(
select w3.id, w3.value3 from weird w3
where w3.value3 is not null
and w3.version=(select max(w33.version) from weird w33 where w33.id=w3.id and w33.value3 is not null)
)
select v1.id, v1.value1, v2.value2, v3.value3
from v1, v2, v3
where v1.id=v2.id and v1.id=v3.id;
We can use UNPIVOT and PIVOT creatively to construct the data you want:
declare #t table (ID int not null, Version int not null, Value1 varchar(20) null,
Value2 varchar(20) null, Value3 varchar(20) null)
insert into #t(ID,Version,Value1,Value2,Value3) values
(1,1,'Shaft',null,null),
(1,2,null,'steel','xy'),
(2,1,null,'Knife','somethins'),
(2,3,'Super',null,null)
;With Numberable as (
select *,ROW_NUMBER() OVER (PARTITION BY ID,Val ORDER BY Version desc) rn
from #t t
unpivot (tdata for Val in (Value1,Value2,Value3)) u
), Selected as (
select ID,tdata,Val
from Numberable where rn = 1
)
select
*
from Selected s
pivot (MAX(tdata) for Val in (Value1,Value2,Value3)) u
The UNPIVOT automatically removes the NULLs. The ROW_NUMBER() identifies the values we want to keep. The Selected CTE hides the columns we no longer need so that the PIVOT creates the final result we want:
ID Value1 Value2 Value3
----------- -------------------- -------------------- --------------------
1 Shaft steel xy
2 Super Knife somethins
(I'm using MAX in the pivot but that's just to satisfy the optimizer. Because we've only selected one row for each ID, Val combination, we know that at most one value will be selected to appear in a final position in the grid formed by the pivot)
The above does make the assumption that Value1,Value2 and Value3 all have the same, or at least compatible, data types.
You can rank the values with row_number. The following query first builds such ranks. rn1 is built per id and value1 is null/not null in the descending order of the version. So per ID we get #1 for the last null value and the last filled value. Later we use rn1 = 1 to get the maximum of the two, which is the last filled value. Same for rn2/value2 and rn3/value3.
select
id,
min(case when rn1 = 1 then value1 end) as value1,
min(case when rn2 = 1 then value2 end) as value2,
min(case when rn3 = 1 then value3 end) as value3
from
(
select
id, value1, value2, value3,
row_number() over (partition by id, case when value1 is null then 0 else 1 end order by version desc) as rn1,
row_number() over (partition by id, case when value2 is null then 0 else 1 end order by version desc) as rn2,
row_number() over (partition by id, case when value3 is null then 0 else 1 end order by version desc) as rn3
from mytable
) ranked
group by id
order by id;
Used CASE WHEN to SELECT max(version) where value is not null and not blank and then joinedwith the original table on those versions. You can see it in action in link provided below the query
Use this query.
Select distinct a.*, b.value1, c.value2, d.value3
from
(
Select id, max(case when (value1 is not null and value1 <> ' ') then version else 0 end) as ver1,
max(case when (value2 is not null and value2 <> ' ') then version else 0 end) as ver2,
max(case when (value3 is not null and value3 <> ' ') then version else 0 end) as ver3
from
your_table
group by id
) a
inner join
your_table b,
your_table c,
your_table d
where (a.ver1=b.version and a.id=b.id)
and (a.ver2=c.version and a.id=c.id)
and (a.ver3=d.version and a.id=d.id)
See it in action here at this link

How to create "subsets" as a result from a column in SQL

Let's suppose that I've got as a result from one query the next set of values of one column:
Value
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
Now, I would like to see this information with another order, establishing a limit to the number of values of every single subset. Now suppose that I choose 3 as a limit,the information will be given like this (one column for all the subsets):
Values
1 A, B, C
2 D, E, F
3 G, H, I
4 J,
Obviously, the last row will contain the remaining values when their number is smaller than the limit established.
Is it possible to perform a query like this in SQL?
What about if the limit is dynamic?. It can be chosen randomly.
create table dee_t (id int identity(1,1),name varchar(10))
insert into dee_t values ('A'),('B'),('c'),('D'),('E'),('F'),('g'),('H'),('I'),('J')
;with cte as
(
select (id-1)/3 +1 rno ,* from dee_t
) select rno ,
(select name+',' from cte where rno = c.rno for xml path('') )
from cte c group by rno
You can do this by using few calculations with row_number, like this:
select
GRP,
max(case when RN = 1 then Value end),
max(case when RN = 2 then Value end),
max(case when RN = 0 then Value end)
from (
select
row_number() over (order by Value) % 3 as RN,
(row_number() over (order by Value)+2) / 3 as GRP,
Value
from Table1
) X
group by GRP
The first row_number creates numbers for the columns (1,2,0,1,2,0...) and the second one creates numbers for the rows (1,1,1,2...). Those are then used to group the values into correct place using case, but you can also use pivot instead of it if you like it more.
If you want them into same column, of course just concatenate the cases instead of selecting them on different columns, but beware of nulls.
Example in SQL Fiddle
Thanks a lot for all your reply. Finally I've got a Solution with the help of Rajen Singh
This is the code than can be used:
WITH CTE_0 AS
(
SELECT DISTINCT column_A_VALUE AS id
FROM Table
WHERE column_A_VALUE IS NOT NULL
), CTE_1 AS
(
SELECT ROW_NUMBER() OVER (ORDER BY id) RN, id
FROM CTE_0
), CTE_2 AS
(
SELECT RN%30 GROUP, ID
FROM CTE_1
)SELECT STUFF(( SELECT ','''+CAST(ID AS NVARCHAR(20))+''''
FROM CTE_2
WHERE GROUP = A.GROUP
FOR XML PATH('')),1,1,'') IDS
FROM CTE_2 A
GROUP BY GROUP

Select all columns from table group by column

'test_table' contains a column called vendor, with char values from 1 to 9, 1 being highest priority. 'test_table' also has another column named match, with char values either 'I' or 'H', with 'I' receiving higher priority. I want to return all rows with unique values in ID, prioritized by match, then vendor.
Test_Table
ID Vendor Match
1 3 I
1 2 I
1 4 H
2 1 H
2 1 I
3 1 H
3 2 I
Results Desired
ID Vendor Match
1 2 I
2 1 I
3 2 I
SELECT *
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) AS RowNo, *
FROM test_table) x
WHERE RowNo = 1
ORDER BY ID
Any help would be greatly appreciated. Thank you!
You need to have an order by clause for match column ( character comparison happens based on ascii value) and another one on vendor column . You can even remove ascii keyowrd ( for the sake of clarity i have written) and simply use the column name in order by clause
Try this :
;With cte as
(
Select ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ascii(match) desc ,vendor )
AS RowNo, Id,Vendor ,Match
FROM test_table
)
Select Id,Vendor,Match from cte
WHERE RowNo = 1
SELECT Id,Vendor,Match
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID,Match Desc,Vendor Asc) AS RowNo, *
FROM test_table ) x
WHERE RowNo = 1
ORDER BY ID