SQL - PIVOT for one column and add new column - mysql

I am fairly new to SQL. I have got this input table as
TypeId EventDescription FeedHeader FeedHeaderValue
---------------------------------------------------------
166 Financial AllocRule 130
166 Financial DealID 0
175 Partner Capital InvestorID OV_P1
175 Investment Querter Q1
175 Investment DealID offset
175 Investment InvestorID OV_P2
I need an output as follows
Financial value Partner Capital value Investment value
-------------------------------------------------------------------------------
AllocRule 130 InvestorID OV_P1 Querter Q1
DealID 0 DealID offset
InvestorID OV_P2
Not sure if that is even possible. I tried using pivot but its not giving desired output
select
[Financial] as FinancialHeader
, [Partner Capital] as PartnerCapitalHeader
, [Investment] as Investmentheader
from
(
select EventDescription, FeedHeader
from [Feeder]
) x
pivot
(
MAX(FeedHeader)
for EventDescription in([Financial], [Partner Capital], [Investment])
)p
Another approach i tried
Select
Min(Case [EventDescription] When 'Financial' Then [FeedHeader] End)
Financial,
Min(Case [EventDescription] When 'Financial' Then [FeedHeaderValue] End)
value,
Min(Case [EventDescription] When 'Partner Capital' Then [FeedHeader]
End) PartnerCapital,
Min(Case [EventDescription] When 'Partner Capital' Then
[FeedHeaderValue] End) value,
Min(Case [EventDescription] When 'Investment' Then [FeedHeader] End)
Investment,
Min(Case [EventDescription] When 'Investment' Then [FeedHeaderValue] End)
value
From [Feeder]
Group By EventDescription
Is there a another way to do it?

I was curious and did some research with PIVOT on SO and google and finally my luck clicked (at least what I think now)
The key point here is that you create new EventDescription values by appending 1 or 2 to the end depending on how many columns we want to PIVOT.
Without doing this, the pivot query won't work properly and would lead to error as per my experience with this task.
select max([Financial]) as FinancialHeader
, max([Financial1]) as FinancialHeaderValue
, max([Partner Capital]) as PartnerCapitalHeader
, max([Partner Capital1]) as PartnerCapitalHeaderValue
, max([Investment]) as InvestmentHeader
, max([Investment1]) as InvestmentHeaderValue
from
(select EventDescription,
EventDescription+'1' as EventDescription1,
FeedHeader,
FeedHeaderValue,
row_number() over (partition by EventDescription order by EventDescription) rn
from [testtable]
) x
pivot
(
MAX(FeedHeader)
for EventDescription in([Financial], [Partner Capital], [Investment])
) p
pivot
(
MAX(FeedHeaderValue)
for EventDescription1 in([Financial1], [Partner Capital1] , [Investment1] )
) v
group by [RN]
DEMO: db<>fiddle

Related

How to parse <first_value> aggregate in a group by statement [SNOWFLAKE] SQL

How do you rewrite this code correctly in Snowflake?
select account_code, date,
sum(box_revenue_recognition_amount) as box_revenue_recognition_amount
, sum(case when box_flg = 1 then box_sku_quantity end) as box_sku_quantity
, sum(box_revenue_recognition_refund_amount) as box_revenue_recognition_refund_amount
, sum(box_discount_amount) as box_discount_amount
, sum(box_shipping_amount) as box_shipping_amount
, sum(box_cogs) as box_cogs
, max(invoice_number) as invoice_number
, max(order_number) as order_number
, min(box_refund_date) as box_refund_date
, first (case when order_season_rank = 1 then box_type end) as box_type
, first (case when order_season_rank = 1 then box_order_season end) as box_order_season
, first (case when order_season_rank = 1 then box_product_name end) as box_product_name
, first (case when order_season_rank = 1 then box_coupon_code end) as box_coupon_code
, first (case when order_season_rank = 1 then revenue_recognition_reason end) as revenue_recognition_reason
from dedupe_sub_user_day
group by account_code, date
I have tried to apply window rule has explained in first_value Snowflake documentation to no avail with the SQLCompilation Error: ... is not a valid group by expression
select account_code, date,
first_value(case when order_season_rank = 1 then box_type end) over (order by box_type ) as box_type
first_value(case when order_season_rank = 1 then box_order_season end) over (order by box_order_season ) as box_order_season,
first_value(case when order_season_rank = 1 then box_product_name end) over (order by box_product_name ) as box_product_name,
first_value(case when order_season_rank = 1 then box_coupon_code end) over (order by box_coupon_code ) as box_coupon_code,
first_value(case when order_season_rank = 1 then revenue_recognition_reason end) over (order by revenue_recognition_reason ) as revenue_recognition_reason
, sum(box_revenue_recognition_amount) as box_revenue_recognition_amount
, sum(case when box_flg = 1 then box_sku_quantity end) as box_sku_quantity
, sum(box_revenue_recognition_refund_amount) as box_revenue_recognition_refund_amount
, sum(box_discount_amount) as box_discount_amount
, sum(box_shipping_amount) as box_shipping_amount
, sum(box_cogs) as box_cogs
, max(invoice_number) as invoice_number
, max(order_number) as order_number
, min(box_refund_date) as box_refund_date
from dedupe_sub_user_day
group by 1,2
First_value is not an aggregate function. But an window function, thus you get an error when you use it in relation to a GROUP BY. If you want to use it with a group up put an ANY_VALUE around it.
here is some data I will use below in a CTE:
with data(id, seq, val) as (
select * from values
(1, 1, 10),
(1, 2, 11),
(1, 3, 12),
(1, 4, 13),
(2, 1, 20),
(2, 2, 21),
(2, 3, 22)
)
So to show FIRST_VALUE is a window function we can just use it
select *
,first_value(val)over(partition by id order by seq) as first_val
from data
ID
SEQ
VAL
FIRST_VAL
1
1
10
10
1
2
11
10
1
3
12
10
1
4
13
10
2
1
20
20
2
2
21
20
2
3
22
20
So if we GROUP BY id, to avoid an error we have to wrap the FIRST_VALUE by an aggregate value, as given the are all equal, ANY_VALUE is a good pick, and it seems it needs to be in another layer of SQL:
select id
,count(*) as count
,any_value(first_val) as first_val
from (
select *
,first_value(val)over(partition by id order by seq) as first_val
from data
)
group by 1
order by 1;
ID |COUNT |FIRST_VAL
1 |4 |10
2 |3 |20
now MAX can be fun to use where used in relation to ROW_NUMBER() to pick the best value:
select id
,count(*) as count
,max(first_val) as first_val
from (
select *
,row_number() over (partition by id order by seq) as rn
,iff(rn=1, val, null) as first_val
from data
)
group by 1
order by 1;
but this is almost more complex than the ANY_VALUE solution, but I feel the performance would be better, but if they have the same magnitude of performance, I would always choose readable to you and your team, over a smaller performance difference.
With the way you've written your case statement, it leads me to believe that there is only one row with order_season_rank = 1 when grouping by account_code and date.
If that is true, then you can use several of Snowflake's aggregate functions and you will get what you want. Rather than trying to get the first value, you could use min, max, any_value, mode (or really any aggregate function that will ignore nulls) to return the only non-null value in the aggregation.
first() this link suggests first is only supported by MS ACCESS however you've tagged the question with MYSQL, Snowflake. Could you confirm the DBMS's you are using?
by moving the first_value() function outside the aggregation it seems to work fine

How to create a calculated row in sql or power bi?

I am trying to do a calculation on 2 rows on a SQL I wrote so I can have a 3 row that will be Profit and show the amount is it possible?
This dummy data not true to any company!
see below :
SELECT a.pcg_type GL_Acoount_Group,
Abs(sum(b.debit-b.credit)) GL_Amount
FROM dolibarr.llx_accounting_account a
JOIN dolibarr.llx_accounting_bookkeeping b
ON a.account_number = b.numero_compte
WHERE a.pcg_type IN ('INCOME', 'EXPENSE')
ANDa.fk_pcg_version = 'PCG99-BASE'
GROUP BY a.pcg_type
Results:
Group. Amt
INCOME 379200
EXPENSE 65700
Expected Results:
Group. Amt
INCOME 379200
EXPENSE 65700
PROFIT 313500
Use ROLLUP for adding an extra row and use CASE statement inside SUM() function for treating expense value as negative for calculation
--MySQL
SELECT COALESCE(acc_type, 'Profit') "Group"
, ABS(SUM(CASE WHEN acc_type = 'EXPENSE' THEN -amount ELSE amount END)) amt
FROM test
GROUP BY acc_type WITH ROLLUP
Another way by using UNION ALL
SELECT acc_type "Group"
, SUM(amount) Amt
FROM test
GROUP BY acc_type
UNION ALL
SELECT 'Profit' AS "Group"
, SUM(CASE WHEN acc_type = 'EXPENSE' THEN -amount ELSE amount END) Amt
FROM test
Please check this url https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=f859036ffcb3d808330bce5346daee1e

SQL Query SELECT and Group by with conditions

I have a table called item table.
Sample contents:
order product plan qty price term base_price
CO0300039921 ZZFEE0000 N 1 0.01 1 25
CO0300039921 ZZFEE0000 N 1 0.01 1 37.13
CO0300039921 ZZFEE0000 N 1 0.02 1 37.13
CO0300039921 ZZFEE0000 Y 1 0 1 1
CO0300039921 ZZFEE0000 Y 1 0 1 1
AO1000301407 VOSVC0002W0 Y 1 0 1 3
AO1000301407 VOACT0101 N 1 0 2 5.99
If plan is ā€˜Nā€™ Then
get SUM(qty) AS 'quantity'
get price AS 'rate'
If plan is ā€˜Yā€™ Then
get SUM(qty) AS 'quantity'
get (term * qty) AS 'rate'
get (base_price) AS 'base'
FROM item
WHERE order = 'CO0300039921'
GROUP BY product, price, base_price
EXPECTED RESULT:
order product plan qty price rate base_price
CO0300039921 ZZFEE0000 N 2 0.01 0.01 -
CO0300039921 ZZFEE0000 N 1 0.02 1 -
CO0300039921 ZZFEE0000 Y 2 - 1 1
Here is what I've tried:
SELECT CASE WHEN p8_plus_plan = 'Y' THEN
(
SUM(qty_ordered),
(p8_contract_term*qty_ordered) AS 'rate',
product_base_price
)
ELSE
(
SUM(qty_ordered),
price AS 'rate'
)
END
FROM ns_order_line
WHERE order_no = ?
GROUP BY product_id, price, p8_plus_plan, product_base_price, order_no;
But I'm having an error. Please help.
You would do this using conditional aggregation. Something like this:
select sum(case when plan = 'N' then qty end) as N_qty,
sum(case when plan = 'N' then price end) as N_rate,
sum(case when plan = 'Y' then qty end) as Y_qty,
sum(case when plan = 'Y' then term*qty end) as Y_rate,
sum(case when plan = 'Y' then base_price end) as Y_base
from item
where order = 'CO0300039921'
group by product;
I'm not sure what you really want to aggregate by. But the key idea where is the case inside the aggregation functions.
Something like this I imagine.
SELECT
`order`
, `product`
, `plan`
, CASE WHEN `plan` = 'Y' THEN base_price END AS base /* edit due to comment */
, SUM(qty) AS qty
, MAX(price) AS price
, SUM(CASE WHEN `plan` = 'N' THEN price
WHEN `plan` = 'Y' THEN (term * qty) END) AS rate
FROM item
WHERE `order` = 'CO0300039921'
GROUP BY
`order`
, `product`
, `plan`
, CASE WHEN `plan` = 'Y' THEN base_price END
btw: The column name "order" is not a good choice as it is a reserved word and very frequently used too. You need to use backticks when referencing that column.
It is possible to use a CASE EXPRESSION without an aggregate function, BUT that case expression then must* also be part of he GROUP BY clause.
*In MySQL (only) you can bypass this because MySQL has an unusual "extension" to group by syntax but this can be changed by server settings and relying on that extension is risky. I strongly urge you to include all columns (including case expressions) into the group by clause that do NOT use an aggregate function (sum/count/avg etc.)

How to do a SELECT for total from beginning until the specified date in MySQL?

I have entry table:
I need to do a SELECT to receive 'Date', 'Number of entries' (in that date), 'Total number of entries until that date'.
When I do the SELECT:
SELECT e1.*,
(select count(*) from entry where date(dateCreated) <= e1.date) as Total
from (
SELECT
DATE(e.dateCreated) as "Date",
count(e.dateCreated) as "No of Entries",
sum( case when e.premium='Y' then 1 else 0 end ) as Premium,
sum( case when e.free='Y' then 1 else 0 end ) as Free,
sum( case when e.affiliateID IS NOT NULL then 1 else 0 end) as Affiliate
FROM entry e
WHERE e.competitionID=166
GROUP BY DATE(e.dateCreated)
) as e1
ORDER BY Date DESC
I've got a result table
but the column 'Total' has a wrong data.
How the correct select should be? Is this logic of select is the best and more efficient one?
Here is a demo
If it is just the 5 vs 7 that is off I think it is because that subquery in your select list, which accesses the inline view e1 (which is filtered to competitionID = 166), is not itself filtered when also utilizing the original entry table (unfiltered). You have to filter the original table to that competitionID as well.
Notice line 3 in sql below (only change)
SELECT e1.*,
(select count(*) from entry where date(dateCreated) <= e1.date
and competitionID=166) as Total
from (
SELECT
DATE(e.dateCreated) as "Date",
count(e.dateCreated) as "No of Entries",
sum( case when e.premium='Y' then 1 else 0 end ) as Premium,
sum( case when e.free='Y' then 1 else 0 end ) as Free,
sum( case when e.affiliateID IS NOT NULL then 1 else 0 end) as Affiliate
FROM entry e
WHERE e.competitionID=166
GROUP BY DATE(e.dateCreated)
) as e1
ORDER BY Date DESC
Fiddle - http://sqlfiddle.com/#!9/e5e88/22/0

How to use GROUP BY and COUNT in SQL in this scenario

I am working on a voting system, there is a survey form, I record the data like this
survey
--------------
id
q1
q2
q3
where q1 means question 1, the possible value is 1, 2 , 3, (means the user select first , second ..choice) while the q2 means question 2 , the possible value is 1, 2, 3, 4, 5 ..etc
And I would like to have a query in this case, for each question, count the total number of each choice, so , the query result should be like this e.g.
q1 , first choice , 10
q1 , second, 50
q1 , third , 20...and so on
I think of this way but are there any other elegant approach ? Thanks
(SELECT COUNT(1) as q1_third FROM survey WHERE q1 = 3)...
One way to do it is with case statements within a COUNT or SUM() like you see below.
SELECT
SUM(CASE WHEN q1 = 1 THEN 1 ELSE 0 END) as q1_first,
SUM(CASE WHEN q1 = 2 THEN 1 ELSE 0 END) as q1_second,
SUM(CASE WHEN q1 = 3 THEN 1 ELSE 0 END) as q1_third
FROM survey
Maybe this will be more elegant:
select 'q1', q1, count(*) as n from survey group by q1
union all
select 'q2', q2, count(*) as n from survey group by q2
union all
select 'q3', q3, count(*) as n from survey group by q3
order by 1, 2