Assistance with mysql case - mysql

I have the following table1 invoice
1 | 2022-12-05 | 20
2 | 2022-12-06 | 100
3 | 2022-12-07 | 100
And table2 invoice_payment
1 | 1 | 20 | cash
2 | 2 | 100 | POS
3 | 3 | 25 | Cash
4 | 3 | 50 | POS
5 | 3 | 25 | Cash
I am trying to get each invoice with the amount paid and the breakdown of the payments
I have tried
`SELECT
invoice_id,
count(invoice_id) as transactions,
(CASE WHEN payment_type = 'Cash' THEN SUM(paid) END) AS paid_cash,
(CASE WHEN payment_type = 'Insurance' THEN SUM(paid) END) AS paid_ins,
(CASE WHEN payment_type = 'POS' THEN SUM(paid) END) AS paid_pos,
(CASE WHEN payment_type = 'Chq' THEN SUM(paid) END) AS paid_chq,
(CASE WHEN payment_type = 'BT' THEN SUM(paid) END) AS paid_bt
FROM
invoice_payment ipc
INNER JOIN
invoice i
ON ipc.invoice_id = i.id
GROUP BY invoice_id DESC`
This is the output from the above query
1 | 1 | 20 |NULL | NULL |NULL |NULL
2 | 1 | NULL |NULL | 100 |NULL |NULL
3 | 3 | 100 |NULL | NULL |NULL |NULL
However, the desired output would look like
1 | 1 | 20 |NULL | NULL |NULL |NULL
2 | 1 | NULL |NULL | 100 |NULL |NULL
3 | 3 | 50 |NULL | 50 |NULL |NULL
How do i adjust this code the get the desired output?
Currently the written code is not getting the sum of the different payment types.

SELECT invoice_id, count(invoice_id) as transactions,
sum(CASE WHEN payment_type = 'Cash' THEN paid END) AS paid_cash,
sum(CASE WHEN payment_type = 'Insurance' THEN paid END) AS paid_ins,
sum(CASE WHEN payment_type = 'POS' THEN paid END) AS paid_pos,
sum(CASE WHEN payment_type = 'Chq' THEN paid END) AS paid_chq,
sum(CASE WHEN payment_type = 'BT' THEN paid END) AS paid_bt
FROM
invoice_payment ipc
INNER JOIN
invoice i
ON ipc.invoice_id = i.id
GROUP BY invoice_id DESC`

Related

MySQL ranking with datetime and sum

I have a database where purchases are stored:
| user_id | product | price | datetime |
-----------------------------------------
| 1 | 1 | -0.75 | 2022-01-01 |
| 2 | 1 | -0.75 | 2022-01-01 |
| 3 | 2 | -0.65 | 2022-01-01 |
| 2 | 1 | -0.75 | 2022-01-01 |
| 1 | 1 | -0.75 | 2022-01-02 |
| 1 | 3 | -1.50 | 2022-01-02 |
| 1 | 2 | -0.65 | 2022-01-02 |
| 2 | 1 | -0.75 | 2022-01-02 |
| 3 | 2 | -0.65 | 2022-01-02 |
| 3 | 3 | -1.50 | 2022-01-02 |
| 3 | 3 | -1.50 | 2022-01-02 |
N.B. Time is not important in this question.
What I want is a ranking per day for each user like this for user 1:
| datetime | product1 | product2 | product3 | total | ranking |
--------------------------------------------------------------------
| 2022-01-01 | 1 | 0 | 0 | 0.75 | 2 |
| 2022-01-02 | 1 | 1 | 1 | 2.90 | 2 |
Note that the ranking is calculated for each day.
The next query gives part of the table:
SELECT
DATE(`datetime`) AS datetime,
SUM(CASE WHEN product = 1 THEN 1 ELSE 0 END) AS product1,
SUM(CASE WHEN product = 2 THEN 1 ELSE 0 END) AS product2,
SUM(CASE WHEN product = 3 THEN 1 ELSE 0 END) AS product3,
SUM(CASE WHEN product = 1 THEN 0.75 ELSE 0 END)+SUM(CASE WHEN product = 2 THEN 0.65 ELSE 0 END)+SUM(CASE WHEN product = 3 THEN 1.5 ELSE 0 END) as total,
FROM `history`
WHERE user_id=1
GROUP BY DATE(`datetime`)
My question is very similar to this one: MySQL ranking, but I can't get it exactly how I want it. It is only possible to make a ranking for the day with all users. If I add the given rank feature it will look to the table and make 2022-01-02 as the first ranking (because 2.90 is higher than 0.75). How can I make the rank look to each day?
The question isn't completely clear. However, what I think you're asking is how to rank the purchases for all users, by day:
history_date
user_id
DailyTotal
totalRank
2022-01-01
2
1.50
1
2022-01-01
1
0.75
2
2022-01-01
3
0.65
3
2022-01-02
3
3.65
1
2022-01-02
1
2.90
2
2022-01-02
2
0.75
3
Then display the results for a single user. So the rankings for user_id = 1 would be:
history_date
user_id
DailyTotal
totalRank
2022-01-01
1
0.75
2
2022-01-02
1
2.90
2
One way is using window functions. Aggregate the total purchases per user, by day and rank the overall total with DENSE_RANK().
Note, instead of hard coding price values, use ABS() to obtain positive numbers.
WITH cte AS (
SELECT ttl.*
, DENSE_RANK() OVER(
PARTITION BY history_date
ORDER BY DailyTotal DESC
) AS TotalRank
FROM (
SELECT user_id
, product
, price
, CAST(`datetime` AS DATE) AS history_date
, SUM( ABS(price) ) OVER(
PARTITION BY user_id, CAST(`datetime` AS DATE)
) AS DailyTotal
FROM history
WHERE product IN (1,2,3)
) ttl
)
SELECT user_id
, history_date
, SUM(CASE WHEN product = 1 THEN 1 ELSE 0 END) AS product1
, SUM(CASE WHEN product = 2 THEN 1 ELSE 0 END) AS product2
, SUM(CASE WHEN product = 3 THEN 1 ELSE 0 END) AS product3
, DailyTotal
, TotalRank
FROM cte
WHERE user_id = 1
GROUP BY user_id
, history_date
, DailyTotal
, TotalRank
;
Results:
user_id
history_date
product1
product2
product3
DailyTotal
TotalRank
1
2022-01-01
1
0
0
0.75
2
1
2022-01-02
1
1
1
2.90
2
db<>fiddle here

Group data by foreign key and date with total by date

I need help to select daily payments made and group by the organization and date.
Group by date, then the total number of payments and the sum total amount of payments for each day
Tables are as follows,
organizations
-----------------------------
| id | name |
+-------------+-------------+
| 1 | org_1 |
+-------------+-------------+
| 2 | org_2 |
+-------------+-------------+
| 3 | org_2 |
-----------------------------
payments
------------------------------------------------------------
| id | org_id | amount | date_created |
+-----------+------------+-------------+-------------------+
| 1 | 2 | 20 | 2020-11-06 |
+-----------+------------+-------------+-------------------+
| 2 | 2 | 10 | 2020-11-06 |
+-----------+------------+-------------+-------------------+
| 3 | 1 | 50 | 2020-11-05 |
+-----------+------------+-------------+-------------------+
| 4 | 2 | 10 | 2020-11-05 |
------------------------------------------------------------
Expected Result
----------------------------------------------------------------------------------------------
| date_created | total_amount | num_payments | org_1 | org_2 | org_3 |
+----------------+----------------+-------------------+-----------+-------------+------------+
| 2020-11-06 | 30.00 | 2 | 0 | 2 | 0 |
+----------------+----------------+-------------------+-----------+-------------+------------+
| 2020-11-05 | 60.00 | 2 | 1 | 1 | 0 |
+----------------+----------------+-------------------+-----------+-------------+------------+
Use conditional aggregation:
select p.date_created,
sum(p.amount) as total_amount,
count(*) as num_payments,
sum(case when o.name = 'org_1' then p.amount else 0 end) as org_1,
sum(case when o.name = 'org_2' then p.amount else 0 end) as org_2,
sum(case when o.name = 'org_3' then p.amount else 0 end) as org_3
from payments p
inner join organizations o on o.id = p.org_id
group by p.date_created

Convert Row with duplicate values to Column - MySQL

I have a table 'A' that looks something like:
_______________________________________________________________
|query_id | query | response |user_response_count |
|---------------------------------------------------------------
| 1 | acne | BothBad | 2 |
| 1 | acne | BothGood | 1 |
| 2 | asthma | BothBad | 1 |
| 2 | asthma | product 1 | 1 |
| 2 | asthma | BothGood | 1 |
| 3 | bell palsy | product 2 | 2 |
| 3 | bell palsy | BothGood | 1 |
---------------------------------------------------------------
I want to write a query to get something that looks like:
__________________________________________________________________________________
| query_id | query | BothGood | BothBad | Product 1 | Product 2 |
-----------------------------------------------------------------------------------
| 1 | acne | 1 | 2 | 0 | 0 |
| 2 | asthma | 1 | 1 | 1 | 0 |
| 3 | bell palsy| 1 | 0 | 0 | 2 |
-----------------------------------------------------------------------------------
That "user_response_count" column actually says, 2 users selected "BothBad" option for "acne" query.
I know, by using max, I can change my rows to the column, but here it would be difficult to the max. Any Thoughts?
Conditional aggregation:
select query_id, query,
sum(case when response = 'BothGood' then cnt else 0 end) as BothGood,
sum(case when response = 'BothBad' then cnt else 0 end) as BothBad,
sum(case when response = 'product 1' then cnt else 0 end) as product1,
sum(case when response = 'product 2' then cnt else 0 end) as product2
from a
group by query_id, query;
You can use a conditional aggregation as
select query_id, query,
max( coalesce(case when response = 'BothGood' then user_response_count end,0) )
as BothGood,
max( coalesce(case when response = 'BothBad' then user_response_count end,0) )
as BothBad,
max( coalesce(case when response = 'product 1' then user_response_count end,0) )
as Product_1,
max( coalesce(case when response = 'product 2' then user_response_count end,0) )
as Product_2
from tableA
group by query_id, query
Demo

MYSQL: transpose the result of a SQL query

please I have problem with transposing an sql query result.
My sql query is:
SELECT atop.name AS top, aseco.name AS abei, aseco.abeiId AS CI, aseco.location AS pac, axrt.frk AS FRK
FROM atop INNER JOIN (aseco INNER JOIN axrt ON (aseco.abeiId = axrt.abeiId) AND (aseco.BCFId = axrt.BCFId) AND (aseco.topId = axrt.topId)) ON atop.topId = aseco.topId
WHERE (((axrt.ctp)<>8) AND ((aseco.abeiish)=5)) OR (((axrt.ctp)<>3) AND ((aseco.abeiish)=2) AND ((aseco.hpnmd)=2));
the result is like this:
----------------------------------------------------------------
top | abei | CI | pac | FRK |
----------------------------------------------------------------
A | b | e | 1 | 12 |
A | b | e | 1 | 13 |
A | b | e | 1 | 14 |
A | c | t | 2 | 45 |
A | c | t | 2 | 56 |
A | c | t | 2 | 23 |
A | c | t | 2 | 29 |
A | c | t | 2 | 50 |
b | c | t | 1 | 11 |
b | c | t | 1 | 56 |
b | c | t | 1 | 78 |
----------------------------------------------------------------
I want to transpose this result and I want it like this :
----------------------------------------------------------------------------------
top | abei | CI | pac | FRK | frk1 | frk2 | frk3 | frk4 |...
---------------------------------------------------------------- -----------------
A | b | e | 1 | 12 | 13 | 14 | | |...
A | c | t | 2 | 45 | 56 | 23 | 29 | 50 |...
b | c | t | 1 | 11 | 56 | 56 | 78 | |...
----------------------------------------------------------------------------------
I tried this code but doesn't work!
SELECT atop.name AS top, aseco.name AS abei, aseco.abeiId AS CI, aseco.location AS pac, axrt.frk AS FRK,frk1,frk2,frk3,frk4,frk5,frk6,frk7,frk8,frk9,frk10,frk11,frk12,
max(case when row=1 then data end) frk1,
max(case when row=2 then data end) frk2,
max(case when row=3 then data end) frk3,
max(case when row=4 then data end) frk4,
max(case when row=5 then data end) frk5,
max(case when row=6 then data end) frk6,
max(case when row=7 then data end) frk7,
max(case when row=8 then data end) frk8,
max(case when row=9 then data end) frk9,
max(case when row=10 then data end) frk10,
max(case when row=11 then data end) frk11,
max(case when row=12 then data end) frk12
FROM atop INNER JOIN (aseco INNER JOIN axrt ON (aseco.abeiId = axrt.abeiId) AND (aseco.BCFId = axrt.BCFId) AND (aseco.topId = axrt.topId)) ON atop.topId = aseco.topId
WHERE (((axrt.ctp)<>8) AND ((aseco.abeiish)=5)) OR (((axrt.ctp)<>3) AND ((aseco.abeiish)=2) AND ((aseco.hpnmd)=2));
Thanks for your help.
You need a group by and you don't need the extra columns in the select. Try this:
SELECT atop.name AS top, aseco.name AS abei, aseco.abeiId AS CI, aseco.location AS pac, axrt.frk AS FRK,
max(case when row=1 then data end) frk1,
max(case when row=2 then data end) frk2,
max(case when row=3 then data end) frk3,
max(case when row=4 then data end) frk4,
max(case when row=5 then data end) frk5,
max(case when row=6 then data end) frk6,
max(case when row=7 then data end) frk7,
max(case when row=8 then data end) frk8,
max(case when row=9 then data end) frk9,
max(case when row=10 then data end) frk10,
max(case when row=11 then data end) frk11,
max(case when row=12 then data end) frk12
FROM atop INNER JOIN
(aseco INNER JOIN axrt ON (aseco.abeiId = axrt.abeiId) AND (aseco.BCFId = axrt.BCFId) AND (aseco.topId = axrt.topId)) ON atop.topId = aseco.topId
WHERE (((axrt.ctp)<>8) AND ((aseco.abeiish)=5)) OR (((axrt.ctp)<>3) AND ((aseco.abeiish)=2) AND ((aseco.hpnmd)=2))
GROUP BY atop.name, aseco.name, aseco.abeiId, aseco.location ;

JOIN TWO TABLES [duplicate]

This question already has answers here:
How can I return pivot table output in MySQL?
(10 answers)
Closed 8 years ago.
I have two tables: 'Project' and 'Project Monthly'.
| PROJECT_ID | TITLE | | ID | PROJECT_ID | MONTH | EXPENSE |
----------------------- --------------------------------------
| 1 | title1 | | 1 | 1 | 1 | 100 |
| 2 | title2 | | 2 | 1 | 2 | 2000 |
| 3 | title3 | | 3 | 1 | 3 | 900 |
| 4 | 1 | 4 | 900 |
| 5 | 2 | 1 | 200 |
| 6 | 2 | 2 | 200 |
| 7 | 3 | 1 | 500 |
I would like to have a table like this:
| PROJECT_ID | TITLE | MONTH_1 | MONTH_2 | MONTH_3 | MONTH_4 |
---------------------------------------------------------------
| 1 | title1 | 100 | 2000 | 900 | 900 |
| 2 | title2 | 200 | 200 | NULL| NULL|
| 3 | title3 | 500 | NULL | NULL| NULL|
Can I realize it only with JOIN and without subqueries?
Thanks!
Turning rows into columns is called pivoting. One approach is to group by each project. The group will become a single row. Inside the group, you define one column per month. The column sums up the expenses for one particular month:
select p.PROJECT_ID
, p.TITLE
, sum(case when m.month = 1 then m.expense end) as MONTH_1
, sum(case when m.month = 2 then m.expense end) as MONTH_2
...
from project p
join monthly m
on p.PROJECT_ID = m.PROJECT_ID
group by
p.PROJECT_ID
, p.TITLE
try like this
select p.PROJECT_ID,p.TITLE,
case when month=1 then EXPENSE end as Month_1,
case when month=2 then EXPENSE end as Month_2,
case when month=3 then EXPENSE end as Month_3,
case when month=4 then EXPENSE end as Month_4
from Project p inner join ProjectMonthly' pm on p.PROJECT_ID=pm.PROJECT_ID
You can do so by using case with max to get the desired result set,below query will only give the results for 4 months if you have more than 4 months then you have to write cases for all months with a max to pick greater value among the values for a month,i have used inner join so the projects that exists in ProjectMonthly only these projects will be returned if any project has no monthly data and you still want it to return the results the change inner join to left join
SELECT p.PROJECT_ID,p.TITLE,
MAX(CASE WHEN MONTH=1 THEN EXPENSE END) AS Month_1,
MAX(CASE WHEN MONTH=2 THEN EXPENSE END) AS Month_2,
MAX(CASE WHEN MONTH=3 THEN EXPENSE END) AS Month_3,
MAX(CASE WHEN MONTH=4 THEN EXPENSE END) AS Month_4
FROM Project p
INNER JOIN ProjectMonthly pm
ON p.PROJECT_ID=pm.PROJECT_ID
GROUP BY p.PROJECT_ID
Fiddle Demo