Unpivot SQL Table with multiple columns - sql

I would like to unpivot a SQL table around multiple columns.
I have tried a normal UNPIVOT statement but that only ppivots around one value.
See this link for example: https://codingsight.com/understanding-pivot-unpivot-and-reverse-pivot-statements.
I have tried to illustrate my data as well as my desired outcome in the picture below.
The top table is a sample of the data in the SQL table. I have used 3 materials but in reality there are 20.
The bottom table is my desired outcome.
The data is on a SQL 2008-r2 server.
Any pointers on how to go about this task?

Consider using cross apply, like so:
select t.date, t.product, x.*
from mytable t
cross apply (values
(container1material, container1amount),
(container2material, container2amount),
(container3material, container3amount)
) x(material, amount)

Use apply for unpivoting:
select t.date, t.product, v.*
from t cross apply
(values (container1amount, container1material),
(container2amount, container2material),
(container3amount, container3material)
) v(containeramount, containermaterial);
unpivot is bespoke syntax (not-standard) and it only does one thing. By contrast, lateral joins are very powerful and unpivoting is only one thing that you can do with them. Apply is worth learning about.

Related

Flatten and group a set of data using PIVOT twice

I'm trying to flatten a set of data from a SQL query (using MS SQL Server) and I need to do it twice.
This is the example data I have.
This is the data I would like to show
I managed to get one height and one area for each building using PIVOT but what I need is to pivot again in order to have one row for every building that contains all the related data.
I think the solution requires the use of both PIVOT and CROSS APPLY but I cannot find the right way to use them.
Thanks for your help!
No need for a CROSS APPLY, a simple PIVOT or Conditional Aggregation would do. Just remember to "FEED" your pivot with the minimum number of required columns.
Select *
From (
Select Building
,Item = concat([Floor],MeasureName)
,Value = MeasureValue
From YourTable
) src
Pivot ( max( Value ) for Item in ( [floor1height]
,[floor1area]
,[floor2height]
,[floor2area]
) ) Pvt

Cross joining to an unnested mapping field in HQL (works in athena, not in Hive)

So I have two (mapping) fields I need to unpack and break out into rows. In athena, I can use the following approach (to unpack iether of them:
SELECT
unique_id,
key,
value
FROM
(
select
unique_id,
purchase_history
from table
)
CROSS JOIN unnest(purchase_history) t(key,value)
This works perfectly in athena, I get 1 row for each purchase along with their unique identifier. However, when I try to test it in Hive it doesn't work. Is there anything specific in here that doesn't fly in HQL? I think cross joins are allowed, but perhaps the way I am calling the field isn't working? Or is it the "unnest"? Please let me know if you need further explanation.
You can do the same in Hive using lateral view explode, if purchase_history is of type map, this will work:
SELECT
s.unique_id,
t.key,
t.value
FROM
(
select
unique_id,
purchase_history
from table
) s --alias for sub-queries is a must in Hive
lateral view explode(s.purchase_history) t as key,value

How to stack two columns into rows in a single column?

I have following table named 'model', I want sql server code to achieve the result.
My desired output should be as follows,
Any help would be appreciated.
You can try this - just another simple way :)
SELECT ModelA, Discount
from table1
UNION
SELECT ModelB, Discount
from table1
I like to use apply for this purpose:
select v.model, t.discount
from t cross apply
(values (model1), (model2)) v(model);
apply is a very powerful construct that implements something called "lateral joins". There are other methods to unpivot such data, but I unpivoting is a good introduction to lateral joins.
It's UNION ALL that you need:
select modela as model, discount from model
union all
select modelb, discount from model

SQL Pivot Table Multiple Columns

I have a table that looks like this:
Title01 Title02 Title03 Title04 Title05
Number Title Division Department IFC
And I am wanting to turn the columns into rows so it loos like this:
Field
Number
Title
Division
Department
IFC
Is it possible to do this using the PIVOT function in SQL?
I like to use CROSS APPLY for this:
select v.field
from t cross apply
(values (title01), (title02), (title03), (title04), (title05)
) v(field);
CROSS APPLY implements the lateral join. You can think of it as an extension of correlated subqueries -- but the subquery can return multiple columns and multiple rows. Unpivoting data happens to be a simple introduction to the concept.

SQL convert compatibility matrix into mapping table (columns into rows)

I have the following table:
I want to convert the matrix to this format:
where the new mapping table represents the compatibility between a set of options and a set of models that use those options, and the numeric values represent the price of an option for that specific model.
Bare in mind that this is just a small sample from a much bigger table, so the query needs to be more or less dynamic and not hardcoded based on the number of options or models provided in this example.
Does anyone know how I can achieve this?
Best regards,
UnPivot or Gordon's answer would be more performant, but here is an option that will "dynamically" unpivot your data or query without actually using dynamic SQL.
Example
Select A.OptionID
,ModelName = C.Item
,Price = C.Value
From YourTable A
Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
Cross Apply (
Select Item = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','varchar(max)') --<< Change to desired datatype
From B.XMLData.nodes('/row') as C1(n)
Cross Apply C1.n.nodes('./#*') as C2(a)
Where a.value('local-name(.)','varchar(100)') not in ('OptionID','OtherFieldsToExclude')
) C
Returns
I like to do this using outer apply:
select v.*
from t outer apply
(values (option_id, 'model1', model1),
(option_id, 'model2', model2),
(option_id, 'model3', model3)
) v(option_id, model_name, price);
You would just add more rows to the values list for each item you want in the final table.
You can also do this using union all, cross join/case, and unpivot.
However, this method uses lateral joins, and these are very powerful for other purposes as well. This is worthwhile syntax to master.
EDIT:
I'm not really sure what "dynamic" means in this context. You table has columns or it doesn't. You can use dynamic SQL to generate the code based on the input parameters or the layout of a single table. It would follow the same logic as above.