Convert column value to row value - sql

In SQL Server, I am trying to convert the from table 1 to table 2. From reading other answers from stack overflow, I can do some sort of row_number(). But the problem is I need do some inner join after the conversion because the following script use max() aggregate function, it kind force other fields from other tables to have some sort of aggregate function as well. So I was wondering if there is an alternative approach to solve this problem? Or if there is a way to handle this aggregate function when do join with another table.
select max(case when key = 'ab' then Value end) as ab,
max(case when key = 'cd' then Value end) as cd
from (select t.*,
row_number() over (partition by key order by Value) as seq
from table t
) t
group by seq;
table 1
table 2

You can try with this below script-
SELECT id,
MAX(CASE WHEN name = 'car1' THEN name END) car1,
MAX(CASE WHEN name = 'car2' THEN name END) car2,
MAX(CASE WHEN name = 'car3' THEN name END) car3
FROM your_table
GROUP BY id

You can go for PIVOT feature.
;WITH src as
(
SELECT *
FROM
(
VALUES
(1, 'Car1', 'nissan'),
(1, 'Car2', 'audi'),
(1, 'Car3', 'toyota')
) as t (id, name, value)
)
SELECT *
FROM src
PIVOT
(
max(VALUE) FOR NAME IN ([Car1], [Car2], [Car3])
) as pvt
+----+--------+------+--------+
| id | Car1 | Car2 | Car3 |
+----+--------+------+--------+
| 1 | nissan | audi | toyota |
+----+--------+------+--------+

Related

Select SUM and column with max

I looking best or simplest way to SELECT type, user_with_max_value, SUM(value) GROUP BY type. Table look similar
type | user | value
type1 | 1 | 100
type1 | 2 | 200
type2 | 1 | 50
type2 | 2 | 10
And result look:
type1 | 2 | 300
type2 | 1 | 60
Use window functions:
select type, max(case when seqnum = 1 then user end), sum(value)
from (select t.*,
row_number() over (partition by type order by value desc) as seqnum
from t
) t
where seqnum = 1;
Some databases have functionality for an aggregation function that returns the first value. One method without a subquery using standard SQL is:
select distinct type,
first_value(user) over (partition by type order by value desc) as user,
sum(value) over (partition by type)
from t;
You can use window function :
select t.*
from (select t.type,
row_number() over (partition by type order by value desc) as seq,
sum(value) over (partition by type) as value
from table t
) t
where seq = 1;
Try below query.
It will help you.
SELECT type, max(user), SUM(value) from table1 GROUP BY type
use analytical functions
create table poo2
(
thetype varchar(5),
theuser int,
thevalue int
)
insert into poo2
select 'type1',1,100 union all
select 'type1',2,200 union all
select 'type2',1,50 union all
select 'type2',2,10
select thetype,theuser,mysum
from
(
select thetype ,theuser
,row_number() over (partition by thetype order by thevalue desc) r
,sum(thevalue) over (partition by thetype) mysum from poo2
) ilv
where r=1

Aligning offset data values sql join

Currently, I've got two rows of data pivoted using case statement into two columns. Data is not aligning. Within the case statement, is there a way to align? All values in Column A has a corresponding value in Column B.
+----------+----------+
| Column A | Column B |
+----------+----------+
| Null | 0 |
+----------+----------+
| 40 | Null |
+----------+----------+
| Null | 0 |
+----------+----------+
| 50 | Null |
+----------+----------+
Expected Output:
+----------+----------+
| Column A | Column B |
+----------+----------+
| 40 | 0 |
+----------+----------+
| 50 | 0 |
+----------+----------+
SELECT (CASE WHEN t.[column A] = 'Column A' THEN t.value END) AS [Column A],
(CASE WHEN t.[column B] = 'Column B' THEN t.value END) AS [Column B]
FROM t INNER JOIN t1 ON t.ID = t1.ID
WHERE t1.string = '123455'
cross apply should work for this requirement since there's no conditions.
select t.colA, max(t.ColB) from(
select t1.colA, t2.colB from testA t1
cross apply testA t2
where t1.colA is not null) t
group by t.colA
sql fiddle
SQL tables represent unordered sets. In a sense, there is no way to answer your question, although this would work:
select a, 0 as b
from t
where a is not null;
However, I suspect that you want to align values based on the ordering. For that, you need an ordering column. Something like this should work:
select max(a) as a, max(b) as b
from (select t.*, row_number() over (order by <ordering col>) as seqnum
from t
) t
group by floor( (seqnum - 1) / 2 ); -- floor() is not really needed
For this to work, though, you need a column that specifies the ordering.
Try this:
select t2.weight, t1.invalid
from mytable t1
join mytable t2 on t2.sequence = t1.sequence
where t1.weight is null
and t2.invalid is null
I have put sample data and tried the below. It is working fine. You can use Window functions & GROUP BY aggregate to arrive at the result.
CREATE TABLE #test1 (seq int, columna int, columnb int)
INSERT INTO #test1
values (0,99,null), (0,null,0)
select * from #test1
select seq, MAX(case when rnk_columna = 1 then columna else 0 end) as columna, MAX(case when rnk_columnb = 1 then columnb else 0 end) as columna
from
(select seq, columna, columnb, Row_number() over(partition by seq order by columna desc) as rnk_columna, Row_number() over(partition by seq order by columnb desc) as rnk_columnb
from #test1) as t
group by seq

Convert Table to another format in MSSQL

I am facing a problem with MS-SQL in getting output from a table in a particular format.
Name | StringValue | Parent_ID
FieldName | TestHeader1 | 3
FieldValue | ValueForTestHeader1 | 3
FieldName | TestHeader2 | 6
FieldValue | ValueForTestHeader2 | 6
And I want to select data from this table as follows:
TestHeader1 | TestHeader2
ValueForTestHeader1 | ValueForTestHeader2
Any help would be highly appreciated!
use conditional aggregation
select max(case when parent_id=3 then stringvalue) as col1,
max(case when parent_id=6 then stringvalue) as col2
from tablename
group by parent_id
You can do conditional aggregation by using row_number() & dense_rank():
select max(case when seq1 = 1 then stringvalue end),
max(case when seq1 = 2 then stringvalue end)
from (select t.*,
dense_rank() over (order by parent_id) as seq1,
row_number() over (partition by parent_id order by stringvalue) seq2
from table t
) t
group by seq2;

Pivot group multi column SQL

In SQL, how can I merge multiple columns into one column with multiple rows?
Example:
name | age | gender
------+-------+---------
John | 20 | M
Jill | 21 | F
Exam | 22 | M
I want to get this table:
Exam | John | Jill
------+-------+---------
22 | 21 | 20
M | F | M
You can do like this.
Use two PIVOT query with UNION ALL to combine them
SELECT CAST(Exam AS VARCHAR(10)) Exam,
CAST(Jill AS VARCHAR(10)) Jill,
CAST(John AS VARCHAR(10)) John
FROM
(
select age,name
from T
) as x
PIVOT
(
MAX(Age) FOR name IN ([Exam],[John],[Jill])
)AS P1
UNION ALL
SELECT Exam,Jill,John FROM
(
select name,gender
from T
) as x
PIVOT
(
MAX(gender) FOR name IN ([Exam],[John],[Jill])
)AS P1
sqlfiddle:http://sqlfiddle.com/#!18/a437d/6
You can do this using a single query -- basically unpivot and conditional aggregation:
select max(case when v.name = 'Exam' then v.val end) as exam,
max(case when v.name = 'John' then v.val end) as john,
max(case when v.name = 'Jill' then v.val end) as jill
from t cross apply
(values (t.name, cast(t.age as varchar(10)), 1),
(t.name, t.gender, 2)
) v(name, val, which)
group by which;
Here is the SQL Fiddle.
You can convert the values to whatever character type you like for compatibility among the values. You want to put numeric values and strings in the same column, so they have to have the same type.

Display multiple rows and column values into a single row, multiple column values

I have to show multiple incomes, type of income and employer name values for a single individual in a single row. So, if 'A' has three different incomes from three different sources,
id | Name | Employer | IncomeType | Amount
123 | XYZ | ABC.Inc | EarningsformJob | $200.00
123 | XYZ | Self | Self Employment | $300.00
123 | XYZ. | ChildSupport| Support | $500.00
I need to show them as
id | Name | Employer1 | Incometype1| Amount1 | Employer2 | incometype2 | Amount2| Employer3 | Incometype3| Amount3.....
123 |XYZ | ABC.Inc |EarningsformJob | $200.00|Self | Self Employment | $300.00|ChildSupport| Support | $500.00.....
I need both 'fixed number of columns' (where we know how many times employer, incometype and amount colums are going to repeat)logic and 'dynamic display of columns' ( unknown number of times these columns are going to repeat)
Thanks.
Since you are using SQL Server there are several ways that you can transpose the rows of data into columns.
Aggregate Function / CASE: You can use an aggregate function with a CASE expression along with row_number(). This version would require that you have a known number of values to become columns:
select id,
name,
max(case when rn = 1 then employer end) employer1,
max(case when rn = 1 then IncomeType end) IncomeType1,
max(case when rn = 1 then Amount end) Amount1,
max(case when rn = 2 then employer end) employer2,
max(case when rn = 2 then IncomeType end) IncomeType2,
max(case when rn = 2 then Amount end) Amount2,
max(case when rn = 3 then employer end) employer3,
max(case when rn = 3 then IncomeType end) IncomeType3,
max(case when rn = 3 then Amount end) Amount3
from
(
select id, name, employer, incometype, amount,
row_number() over(partition by id order by employer) rn
from yourtable
) src
group by id, name;
See SQL Fiddle with Demo.
PIVOT/UNPIVOT: You could use the UNPIVOT and PIVOT functions to get the result. The UNPIVOT converts your multiple columns of Employer, IncomeType and Amount into multiples rows before applying the pivot. You did not specific what version of SQL Server, assuming you have a known number of values then you could use the following in SQL Server 2005+ which uses CROSS APPLY with UNION ALL to unpivot:
select id, name,
employer1, incometype1, amount1,
employer2, incometype2, amount2,
employer3, incometype3, amount3
from
(
select id, name, col+cast(rn as varchar(10)) col, value
from
(
select id, name, employer, incometype, amount,
row_number() over(partition by id order by employer) rn
from yourtable
) t
cross apply
(
select 'employer', employer union all
select 'incometype', incometype union all
select 'amount', cast(amount as varchar(50))
) c (col, value)
) src
pivot
(
max(value)
for col in (employer1, incometype1, amount1,
employer2, incometype2, amount2,
employer3, incometype3, amount3)
) piv;
See SQL Fiddle with Demo.
Dynamic Version: Lastly, if you have an unknown number of values then you will need to use dynamic SQL to generate the result.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+cast(rn as varchar(10)))
from
(
select row_number() over(partition by id order by employer) rn
from yourtable
) d
cross apply
(
select 'employer', 1 union all
select 'incometype', 2 union all
select 'amount', 3
) c (col, so)
group by col, rn, so
order by rn, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, name,' + #cols + '
from
(
select id, name, col+cast(rn as varchar(10)) col, value
from
(
select id, name, employer, incometype, amount,
row_number() over(partition by id order by employer) rn
from yourtable
) t
cross apply
(
select ''employer'', employer union all
select ''incometype'', incometype union all
select ''amount'', cast(amount as varchar(50))
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query);
See SQL Fiddle with Demo. All versions give a result:
| ID | NAME | EMPLOYER1 | INCOMETYPE1 | AMOUNT1 | EMPLOYER2 | INCOMETYPE2 | AMOUNT2 | EMPLOYER3 | INCOMETYPE3 | AMOUNT3 |
-------------------------------------------------------------------------------------------------------------------------------------
| 123 | XYZ | ABC.Inc | EarningsformJob | 200 | ChildSupport | Support | 500 | Self | Self Employment | 300 |