Pivot without aggregate - again - sql

I have a table where I want to pivot some rows. There are lots of these questions on here, but I am still struggling.
Here is the table that I am starting from.
Here is where I want to get to

Based on your sample data, you can easily get the result using an aggregate function with a CASE expression:
select userlicenseid,
startdate,
max(case when name = 'Other' then value end) Other,
max(case when name = 'Pathways' then value end) Pathways,
max(case when name = 'Execution' then value end) Execution,
max(case when name = 'Focus' then value end) Focus,
max(case when name = 'Profit' then value end) Profit
from yourtable
group by userlicenseid, startdate;
See SQL Fiddle with Demo. Since you are converting string values into columns, then you will want to use either the min() or max() aggregate.
You could use the PIVOT function to get the result as well:
select userlicenseid, startdate,
Other, Pathways, Execution, Focus, Profit
from
(
select userlicenseid, startdate,
name, value
from yourtable
) d
pivot
(
max(value)
for name in (Other, Pathways, Execution, Focus, Profit)
) piv;
See SQL Fiddle with Demo

Related

How to transform rows to columns using Postgres SQL?

I have the data in the following structure
Desired Output
Postgres (starting in 9.4) supports the filter syntax for conditional aggregation. So I would recommend:
SELECT customer_id,
MAX(value) FILTER (WHERE name = 'age') as age,
MAX(value) FILTER (WHERE name = 'marketing_consent') as marketing_consent,
MAX(value) FILTER (WHERE name = 'gender') as gender
FROM t
GROUP BY customer_id
SELECT customer_id,
MAX(CASE WHEN name='age' THEN value ELSE NULL END) AS age,
MAX(CASE WHEN name='marketing_consent' THEN value ELSE NULL END) AS marketing_consent,
MAX(CASE WHEN name='gender' THEN value ELSE NULL END) AS gender
FROM table
GROUP BY customer_id;
You just group by customer_id and pick out each value in its own column. You need an aggregate function on the various values columns for syntactical reasons, which is why each column has a Max function.
Not e that it would of course be better to store the data in a normalized fashion. Also, with newer versions of postgres, you can use filter instead of case, which I find a bit more readable.

Aggregate the long tail of a group by query into "others"

I have a table with one dimension and one metric:
name metric
A 4
A 9
B 27
C 9
D 6
I want to group by the dimension and then group the long tail of the results into an 'others' or 'the rest of the data' label.
For example my query should return all the names that the sum of their metrics are greater than 10 and group the rest into 'others':
name metric
A 13
others 15
B 27
I can get this result by aggregating twice:
with T as (
select
name
, (case when sum(metric) > 10 then name else 'others' end) as group_name
, sum(metric) as metric
from MyData
group by name
)
select
group_name as name
, sum(metric) as metric
from T
group by group_name
order by metric
Can I do this in a single operation without using sub queries?
SQL Snippet
I'm pretty certain this requires two levels of aggregation, because the original data doesn't have the information for grouping the names. You need one aggregation to classify the names and one to calculate the final results.
That said, I would write this as:
select (case when sum_metric > 10 then name else 'others' end) as group_name,
sum(sum_metric) as metric
from (select name, sum(metric) as sum_metric
from mydata
group by name
) t
group by group_name;
That said, you could use select distinct and window function for something inscrutable such as:
select distinct (case when sum(metric) > 10 then name else 'others' end),
sum(sum(metric)) over (partition by (case when sum(metric) > 10 then name else 'others' end)) as metric
from mydata
group by name;
However, select distinct is really doing another aggregation. So this eliminates the subquery but not the work.

combining two sql rows into one

In the data tabel when the PRICE_TYPE = MSRP, the amount should be added to column msrp and when PRICE_TYPE = SELP the amount should be added selp column,
How can i write a query to get the above task done , thanks in advance.
the first image should be the desired output.
One of the ways to get your expected result:
select MATERIAL_NUMBER, [MSRP],[SELP]
FROM
(
SELECT
MATERIAL_NUMBER,
PriceType,
Sum(Amount)
FROM Org_table
GROUP BY
MATERIAL_NUMBER,
PriceType
) tbl
pivot
(
sum(Amount)
for PriceType in ([MSRP],[SELP])
)
hope it helps :)
You seem like want to get pivot, you can try to use condition aggregate function.
CASE WHEN with aggregate function.
SELECT MATERIAL_NUMBER,
SUM(CASE WHEN PRICE_TYPE = 'MSRP' THEN AMOUNT END ) MSRP,
SUM(CASE WHEN PRICE_TYPE = 'SELP' THEN AMOUNT END ) SELP
FROM T
GROUP BY
MATERIAL_NUMBER
sqlfiddle
NOTE
because your Amount seem like a float value, some I use SUM

Pivot for the same gender

How do i have multiple pivot. I would like to achieve the result as highlighted below.
For each Grade and each Gender, i would like to have the TotalA and Total B values aligned in 4 columns in a single row. My final result need to contain all 10 columns shown below.
My desired output [Need to contain 2 rows with GENDER column remained]:
I tried with below: But the script removed my Gender column and unable to pivot 2 columns (TotalA, TotalB) into 4 additional columns at the same time.
SELECT *,
[TotalA_Male] = [M],
[TotalB_Female] = [F]
FROM
(
SELECT * FROM table) AS s
PIVOT
(
MAX(TotalA) FOR [Gender] IN ([M],[F])
) AS p
I don't think you want a pivot at all. You are looking to find the partial sum of your total column, grouped by some key columns (it looks like Country and Grade in this case) . Window functions let you perform this partial sum. However, they won't filter by gender. You'll need to use a CASE expression inside the SUM() to only include male or female in your partial sums:
SELECT *,
SUM(CASE WHEN Gender = 'M' THEN TotalA ELSE 0 END) OVER(PARTITION BY Country, Grade) AS TotalA_Male,
SUM(CASE WHEN Gender = 'F' THEN TotalA ELSE 0 END) OVER(PARTITION BY Country, Grade) AS TotalA_Female,
SUM(CASE WHEN Gender = 'M' THEN TotalB ELSE 0 END) OVER(PARTITION BY Country, Grade) AS TotalB_Male,
SUM(CASE WHEN Gender = 'F' THEN TotalB ELSE 0 END) OVER(PARTITION BY Country, Grade) AS TotalB_Female
FROM totals
See also: https://msdn.microsoft.com/en-us/library/ms189461.aspx
Basically, the window functions let you do a GROUP BY as part of a single column expression in the SELECT list. The result of the aggregate and group by is included in every row, just as if it were any other expression. Note how there is no GROUP BY or PIVOT in the rest of the query. The PARTITION BY in the OVER() clause works like a GROUP BY, specifying how to group the rows in the resultset for the purposes of performing the specified aggregation (in this case, SUM()).
You can only pivot on a single column so what you need to to is unpivot those TotalA and TotalB columns into rows and then generate a single column based on gender and the total and use that in a pivot...
select * from (
select
grade,
/* combine the columns for a pivot */
total_gender_details = details + '_' + gender,
totals
from
(values
(1, 'F', cast(7.11321 as float), cast(15.55607 as float)),
(1, 'M', 6.31913, 15.50801),
(2, 'F', 5.26457, 6.94687),
(2, 'M', 6.34666, 9.29783)
) t(grade,gender,totalA,totalB)
/* unpivot the totals into rows */
unpivot (
totals
for details in ([totalA], [totalB])
) up
) t
pivot (
sum(totals)
for total_gender_details in ([totalA_M],[totalA_F],[totalB_M],[totalB_F])
) p

Specific Pivot Query

I have been looking at pivoting columns and would be grateful of any help. I see plenty of examples for summing a row on the pivot but I have a different scenario. I have a field that is JSON which is parsed and the output gets placed in a view as so.
ID Name StringValue
1 type user
1 name aeolos smith
1 access admin
2 type user
2 name ramuh smith
2 access author
I would like to Pivot this somehow to end up like the following.
type name access
user aeolos smith admin
user ramuh smith author
and so on for any entries with the identifier being the ID.
Is this possible?
You did not specify what database you are using, but it your database supports windowing functions like row_number() then you can use an aggregate function with a CASE expression along with the row number to get the final result:
select
max(case when name = 'type' then stringvalue end) type,
max(case when name = 'name' then stringvalue end) name,
max(case when name = 'access' then stringvalue end) access
from
(
select id, name, stringvalue,
row_number() over(partition by name order by id) seq
from yourtable
) d
group by seq;
See SQL Fiddle with Demo
If your database supports the PIVOT function, then you will still use the row_number() windowing function along with pivot to get the final result:
select type, name, access
from
(
select name nm, stringvalue,
row_number() over(partition by name order by id) seq
from yourtable
) d
pivot
(
max(stringvalue)
for nm in (type, name, access)
) piv;
See SQL Fiddle with Demo