select row values as column based on GROUP BY column - sql

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.

Related

How can I combine 2 rows to 1 row in 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.

SQL Server RowNumbering

SrNo TextCol
--------------
NULL ABC
NULL ABC
NULL ASC
NULL qwe
I want to update the SrNo column with numbers 1,2,3,4 without changing sequence of other columns.
It only makes sense to speak of using row number if there exist a column which can provide ordering. Assuming the ordering is specified by the TextCol column, then we can try the following:
WITH cte AS (
SELECT SrNo, TextCol, ROW_NUMBER() OVER (ORDER BY TextCol) rn
FROM yourTable
)
UPDATE cte
SET SrNo = rn;
Tables are unordered, so you cannot rely on "existing sequence". However a "trick" is to use select null which in effect does nothing to the row order. While it works you should not rely on it as a permanent solution.
WITH cte AS (
SELECT SrNo, TextCol
, ROW_NUMBER() OVER (ORDER BY (select NULL)) rn
FROM yourTable
)
UPDATE cte
SET SrNo = rn;

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 Get row values as columns in SQL?

I have a table Test with two columns.
Id Value
1 A
1 B
1 C
I want to get the result like below,
Id Value1 Value2 value3
1 A B C
How can I done this in SQL Server.
This is a pivot, but you don't have a column for the pivoting. row_number() can provide that. I usually use conditional aggregations for this.
select id,
max(case when seqnum = 1 then value end) as value1,
max(case when seqnum = 2 then value end) as value2,
max(case when seqnum = 3 then value end) as value3
from (select t.*,
row_number() over (partition by id order by (select null)) as seqnum
from t
) t
group by id;
Note that SQL tables represent unordered sets. So, there is no information about ordering and the values could be in any order. If a column does specify the ordering, then include that in the order by rather than select null.

How to get the first not null value from a column of values in Big Query?

I am trying to extract the first not null value from a column of values based on timestamp. Can somebody share your thoughts on this. Thank you.
What have i tried so far?
FIRST_VALUE( column ) OVER ( PARTITION BY id ORDER BY timestamp)
Input :-
id,column,timestamp
1,NULL,10:30 am
1,NULL,10:31 am
1,'xyz',10:32 am
1,'def',10:33 am
2,NULL,11:30 am
2,'abc',11:31 am
Output(expected) :-
1,'xyz',10:30 am
1,'xyz',10:31 am
1,'xyz',10:32 am
1,'xyz',10:33 am
2,'abc',11:30 am
2,'abc',11:31 am
You can modify your sql like this to get the data you want.
FIRST_VALUE( column )
OVER (
PARTITION BY id
ORDER BY
CASE WHEN column IS NULL then 0 ELSE 1 END DESC,
timestamp
)
Try this old trick of string manipulation:
Select
ID,
Column,
ttimestamp,
LTRIM(Right(CColumn,20)) as CColumn,
FROM
(SELECT
ID,
Column,
ttimestamp,
MIN(Concat(RPAD(IF(Column is null, '9999999999999999',STRING(ttimestamp)),20,'0'),LPAD(Column,20,' '))) OVER (Partition by ID) CColumn
FROM (
SELECT
*
FROM (Select 1 as ID, STRING(NULL) as Column, 0.4375 as ttimestamp),
(Select 1 as ID, STRING(NULL) as Column, 0.438194444444444 as ttimestamp),
(Select 1 as ID, 'xyz' as Column, 0.438888888888889 as ttimestamp),
(Select 1 as ID, 'def' as Column, 0.439583333333333 as ttimestamp),
(Select 2 as ID, STRING(NULL) as Column, 0.479166666666667 as ttimestamp),
(Select 2 as ID, 'abc' as Column, 0.479861111111111 as ttimestamp)
))
As far as I know, Big Query has no options like 'IGNORE NULLS' or 'NULLS LAST'. Given that, this is the simplest solution I could come up with. I would like to see even simpler solutions.
Assuming the input data is in table "original_data",
select w2.id, w1.column, w2.timestamp
from
(select id,column,timestamp
from
(select id,column,timestamp, row_number()
over (partition BY id ORDER BY timestamp) position
FROM original_data
where column is not null
)
where position=1
) w1
right outer join
original_data as w2
on w1.id = w2.id
SELECT id,
(SELECT top(1) column FROM test1 where id=1 and column is not null order by autoID desc) as name
,timestamp
FROM yourTable
Output :-
1,'xyz',10:30 am
1,'xyz',10:31 am
1,'xyz',10:32 am
1,'xyz',10:33 am
2,'abc',11:30 am
2,'abc',11:31 am