Conflict between group by, case when, and where while using CTE - mysql

I have a long query, with joins from multiple tables. But for the sake of asking question here, I have simplified the query.
WITH cte1 AS
(
SELECT
id,
( (price * quantity) + ((gst * quantity) + (gst2 * quantity))
) gross_revenue
FROM orders
GROUP BY id
),
cte2 AS (
SELECT
id,
ROUND(gross_revenue, 2) gross_revenue,
ROUND((gross_revenue), 2) gross_profit
FROM cte1
),
cte3 AS (
SELECT id, gross_revenue,
CASE
WHEN gross_profit < 0
THEN 0
ELSE
gross_profit
END gross_profit
FROM cte2
)
SELECT
id, gross_revenue, gross_profit
FROM cte3
WHERE gross_profit >= 22.27
LIMIT 25
The above query gives me the following error:
Error Code: 1054
Unknown column 'cte1.gross_revenue' in 'where clause'
When I remove either GROUP BY, CASE WHEN
or replace WHERE to HAVING then the the query works correctly
or I remove the condition of gross_profit from WHERE clause,
Or if extract CASE WHEN from cte3 to the final SELECT statement, then the query works fine.
I am confused as what is the technical reason of this behavior?
Why is it working with HAVING but not with WHERE?
Why does it work fine when I extract CASE WHEN to the final SELECT statement?
Why does GROUP BY of cte1 is making problem at the very final SELECT statement?
Why does it work if I remove gross_profit condition from WHERE clause?
Sample Data for the given columns
id price, quantity gst gst2
1 90.0000 1.00 0.0000 0.0000
2 10.0000 1.00 0.0000 0.0000
3 30.0000 1.00 0.0000 0.0000
4 70.0000 100.00 0.0000 0.0000
5 500.0000 1.00 0.0000 0.0000
6 20.9100 20.00 2.0900 0.0000
7 100.0000 1.00 0.0000 0.0000
8 100.0000 -1.00 0.0000 0.0000
9 20.9100 -20.00 2.0000 0.0000
10 9.7100 1.00 0.0000 0.0000
11 80.0000 1.00 0.0000 0.0000
12 1100.0000 1.00 0.0000 0.0000
13 870.0000 1.00 0.0000 0.0000
14 947.0000 1.00 0.0000 0.0000
15 10.0000 1.00 1.0000 0.0000
16 200.0000 1.00 0.0000 0.0000
17 300.0000 1.00 0.0000 0.0000
18 600.0000 1.00 0.0000 0.0000
19 350.0000 1.00 0.0000 0.0000
20 10.0000 1.00 0.8000 0.0000
21 50.0000 1.00 0.0000 0.0000
22 60.0000 1.00 0.0000 0.0000
23 150.0000 1.00 0.0000 0.0000
24 20.0000 1.00 2.0000 0.0000
25 29.9500 1.00 3.0000 0.0000
Edit
Interestingly for me this query works on mysql-8.0.31 but not on mysql-8.0.27.

Related

MySQL: Get last value of a non-grouped column in GROUP BY query

I'm running below query to join two tables and select a few columns. There are multiple values of points_balance but as I'm doing GROUP BY, I'm getting very first value of points_balance (which seems default). The use case is to fetch the last value of points_balance which will be the latest one in my case.
What updates below query requires for that? TIA
SELECT DATE(main_table.created_at) AS period, main_reward.customer_id,
main_reward.website_id,
SUM(IF(points_delta > 0, points_delta, 0 )) AS points_added,
SUM(IF(points_delta < 0 && is_expired = 0, ABS(points_delta), 0 )) AS points_used,
SUM(IF(points_delta < 0 && is_expired = 1, ABS(points_delta), 0 )) AS points_expired,
main_table.points_balance
FROM magento_reward_history AS main_table
INNER JOIN magento_reward AS main_reward ON main_table.reward_id = main_reward.reward_id
GROUP BY period, customer_id, website_id
Table schemas with some test data are:
magento_reward
reward_id
customer_id
website_id
points_balance
website_currency_code
75505
218501
1
71
magento_reward_history
history_id
reward_id
website_id
store_id
action
entity
points_balance
points_delta
points_used
points_voided
currency_amount
currency_delta
base_currency_code
additional_data
comment
created_at
expired_at_static
expired_at_dynamic
is_expired
is_duplicate_of
notification_sent
is_processed
313769
75505
1
1
8
949831
64
64
64
0
3.0000
3.0000
USD
2021-05-18 00:47:38
2022-05-18 00:47:38
2022-05-18 00:47:38
0
0
313770
75505
1
1
8
949832
109
45
45
0
5.0000
2.0000
USD
2021-05-18 00:50:18
2022-05-18 00:50:18
2022-05-18 00:50:18
0
0
313775
75505
1
1
8
949835
138
29
11
0
6.0000
1.0000
USD
2021-05-19 16:23:56
2022-05-19 16:23:56
2022-05-19 16:23:56
0
0
313783
75505
1
1
1
18
-120
0
0
0.0000
-6.0000
USD
2021-05-19 23:08:43
2022-05-19 23:08:43
2022-05-19 23:08:43
0
0
313784
75505
1
1
8
949840
71
53
0
0
3.0000
2.0000
USD
2021-05-19 23:08:46
2022-05-19 23:08:46
2022-05-19 23:08:46
0
0
For this data, I need to get 109 as points_balance for 2021-05-18, and 71 for 2021-05-19. Currently, I'm getting 64 and 138 which are the very first values for these dates.

How to combine 2 tables with same date

I have 2 Tables with following data:
TABLE 1 (dummy_daily)
Entry storenum busidate daily_budget
1 1 2017-07-01 4000
2 1 2017-07-02 3500
3 1 2017-07-03 2000
4 1 2017-07-04 6000
5 1 2017-07-05 1500
TABLE 2 (site_labour)
Lab_id storenum busidate lab_hour
1123 1 2017-07-01 128
1124 1 2017-07-02 103
1125 1 2017-07-03 114
1126 1 2017-07-04 108
1127 1 2017-07-05 118
This is my current query to combine the 2 tables that have the same date to give result of daily_budget and lab_hour
QUERY:
SELECT
a.daily_budget as Ideal, c.lab_hour as Actual,
b.store_name, b.storenum,a.busidate
FROM dummy_daily a JOIN site_store b ON b.storenum=a.storenum JOIN
site_labour c ON b.storenum=c.storenum
WHERE b.storenum='1' AND
(CASE WHEN c.busidate BETWEEN '2017-07-01' AND '2017-07-05' THEN c.lab_hour ELSE 0 END)
AND (CASE WHEN a.busidate
BETWEEN '2017-07-01' AND '2017-07-05' THEN a.daily_budget ELSE 0 END)
But my current query give me a wrong result :
Wrong Result of Current Query
Ideal Actual storenum busidate
4000 128 1 2017-07-01
3500 128 1 2017-07-02
2000 128 1 2017-07-03
6000 128 1 2017-07-04
1500 103 1 2017-07-05
4000 103 1 2017-07-01
3500 103 1 2017-07-02
2000 103 1 2017-07-03
6000 103 1 2017-07-04
1500 103 1 2017-07-05
This data will continue until end of Actual 118
Expected Result
Ideal Actual storenum busidate
4000 128 1 2017-07-01
3500 103 1 2017-07-02
2000 114 1 2017-07-03
6000 108 1 2017-07-04
1500 118 1 2017-07-05
You have missed one more table so its make confusion to create logic,
As per my understanding, I have created a SELECT statement. Please try this:-
SELECT
dummy_daily.daily_budget as Ideal, site_labour.lab_hour as Actual,
site_store.store_name, site_store.storenum, dummy_daily.busidate
FROM dummy_daily
JOIN site_store
ON dummy_daily.storenum = site_store.storenum
JOIN site_labour
ON dummy_daily.storenum = site_labour.storenum
WHERE (dummy_daily.storenum = 1)
AND (dummy_daily.busidate BETWEEN '2017-07-01' AND '2017-07-05')
AND (site_labour.busidate BETWEEN '2017-07-01' AND '2017-07-05')
AND (dummy_daily.busidate = site_labour.busidate);
Let's try the SQL below:
SELECT
a.daily_budget as Ideal, c.lab_hour as Actual,
b.store_name, b.storenum,a.busidate
FROM dummy_daily a JOIN site_store b ON b.storenum=a.storenum AND b.busidate=a.busidate JOIN
site_labour c ON b.storenum=c.storenum AND b.busidate=c.busidate
WHERE b.storenum='1' AND
(CASE WHEN c.busidate BETWEEN '2017-07-01' AND '2017-07-05' THEN c.lab_hour ELSE 0 END)
AND (CASE WHEN a.busidate
BETWEEN '2017-07-01' AND '2017-07-05' THEN a.daily_budget ELSE 0 END)

MYSQL - Multiple table join and SUM

I'm facing problem with joining three tables and getting sum of its value I have three types of table Tax Types, Purchase Order and Purchase Items
currently I'm facing to get result of tax report kindly check below.
Tax Types
id name
1 No Tax
2 VAT #10%
3 GST #6%
4 VAT #20%
Purchase Order
id total order_tax_id order_tax total_tax grand_total
1 10.0000 1 0.0000 0.0000 10.0000
2 10.0000 2 1.0000 1.0000 11.0000
3 10.0000 3 0.6000 0.6000 10.6000
4 10.0000 4 2.0000 2.0000 12.0000
5 10.0000 1 0.0000 0.9100 10.0000
6 10.0000 1 0.0000 0.5700 10.0000
7 10.0000 1 0.0000 1.6700 10.0000
9 10.0000 2 1.0000 1.5700 11.0000
Purchase Items
id purchase_id tax_rate_id item_tax tax subtotal
1 1 1 0.0000 0.0000 10.0000
2 2 1 0.0000 0.0000 10.0000
3 3 1 0.0000 0.0000 10.0000
4 4 1 0.0000 0.0000 10.0000
5 5 2 0.9100 10.0000% 10.0000
6 6 3 0.5700 6.0000% 10.0000
7 7 4 1.6700 20.0000% 10.0000
9 9 3 0.5700 6.0000% 10.0000
And the output should be like this
tax_name sum_of_subtotal sum_of_item_tax sum_of_grand_total sum_of_total_tax
VAT #20% 10.00 1.67 10.00 2.00 3.67
VAT #10% 10.00 1.67 10.00 2.00 3.67
No Tax 40.00 0.00 0 0 0.00
GST #6% 10.00 1.67 10.00 2.00 3.67
Kindly help me
Query I used
SELECT
tax_rates.name,
(SUM(purchase_items.subtotal)) AS item_total_amount,
(SUM(purchase_items.item_tax)) AS item_tax_amount,
(SUM(purchases.grand_total) - SUM(purchases.order_tax)) AS total_amount,
(SUM(purchases.order_tax)) AS tax_amount
FROM tax_rates
LEFT JOIN purchases ON purchases.order_tax_id = tax_rates.id
LEFT JOIN purchase_items ON purchase_items.tax_rate_id = tax_rates.id
GROUP BY tax_rates.id
ORDER BY tax_rates.name desc
LIMIT 0, 10
I fixed the issue
SELECT
tax_rates.name,
purchase_item.subtotal AS item_total_amount,
purchase_item.item_tax AS item_tax_amount,
(purchases.grand_total - purchases.order_tax) AS total_amount,
purchases.order_tax AS tax_amount
FROM tax_rates
LEFT JOIN (SELECT a.order_tax_id, SUM(a.order_tax) as order_tax, SUM(a.grand_total) as grand_total FROM purchases AS a GROUP BY a.order_tax_id) AS purchases ON purchases.order_tax_id = tax_rates.id
LEFT JOIN (SELECT a.tax_rate_id, SUM(a.item_tax) as item_tax, SUM(a.subtotal) as subtotal FROM purchase_items AS a GROUP BY a.tax_rate_id) AS purchase_item ON purchase_item.tax_rate_id = tax_rates.id
GROUP BY tax_rates.id
ORDER BY tax_rates.name desc
LIMIT 0, 10

Count number of clients in a routine in Mysql

Hi I have a database in mysql and php use to read the data, I have a table called payments where insert payments routines that make customers then are three tables "payments, routines and customers," Now the issue is that I want to load the number of customers using routine id "3", I got it resolved but a detail escapes me, I need the client ID is not the same on the counter and thus can not be counted twice for the same customer in a rut when you have two payments that routine.
The statement that I have is:
select r.name_routine, count(p.id_routine)
from payments p,routines r,customers c
where p.id_client=c.id_client
and p.id_routine=r.id_routine
and p.id_routine='3'
Data set, in spanish:
id_pago monto activo fecha_pago fecha_vencimiento observacion id_cliente id_rutina id_oferta
17 300 1 2016-02-24 2016-03-24 Ninguna 11 3 NULL
13 450 1 2016-02-05 2016-07-05 Ninguna 8 NULL 8
16 270 1 2016-02-05 2016-03-05 Ninguna 7 5 NULL
11 490 1 2016-02-04 2016-04-04 Ninguna 8 NULL 5
15 300 0 2016-02-04 2015-03-04 Ninguna 11 3 NULL
9 330 1 2016-02-03 2016-03-03 Ninguna 11 10 NULL
12 400 1 2016-02-03 2016-05-03 Ninguna 10 NULL 7
10 500 1 2016-02-02 2016-06-02 Ninguna 10 NULL 4
14 420 1 2016-02-02 2016-05-02 Ninguna 8 NULL 9
5 250 0 2016-01-18 2016-02-18 Ninguna 5 7 NULL
8 320 0 2016-01-05 2016-02-05 Ninguna 9 9 NULL
7 250 0 2016-01-03 2016-02-03 Ninguna 7 7 NULL
6 300 0 2016-01-02 2016-02-02 Ninguna 6 6 NULL
2 520 1 2015-10-18 2016-03-18 Ninguna 2 NULL 2
3 290 0 2015-10-18 2015-11-18 Ninguna 3 4 NULL
4 550 0 2015-10-18 2016-01-18 Ninguna 4 NULL 3
In this table you can see that the routine "3" is used 2 times by the same customer (id_client 11), then my query should return the routine is used 1 time and not two, that's what I want to tell all clients that are in routine "3"
Try using distinct but returns the same result, how could you do this?
This should work
SELECT groupedTable.name_routine, COUNT(groupedTable.numbers) FROM
(
SELECT r.name_routine, count(p.id_routine) as numbers
FROM payments p,routines r,customers c
where p.id_client=c.id_client
and p.id_routine=r.id_routine
and p.id_routine='3'
GROUP BY p.id_client) as groupedTable
This should work:
select r.name_routine, count(distinct p.id_cliente)
from payments p,routines r,customers c
where p.id_client=c.id_client
and p.id_routine=r.id_routine
and p.id_routine='3'

How to first order by column A, return a top-3, then order the result by column B in one query?

(Please refer to SQLFiddle for a working example of this post)
I have a table with stock information, as follows:
sp100_id _date bullishness agreement
----------------------------------------------
1 2011-03-16 1.01 0.33
1 2011-03-17 0.85 1.28
1 2011-03-18 0.89 1.25
1 2011-03-21 1.46 1.21
1 2011-03-22 0.39 2.53
2 2011-03-16 3.07 1.27
2 2011-03-17 2.09 0.80
2 2011-03-18 0.91 0.12
2 2011-03-21 1.50 0.00
2 2011-03-22 2.62 1.10
3 2011-03-16 0.73 1.13
3 2011-03-17 1.13 1.21
3 2011-03-18 1.12 0.45
3 2011-03-21 1.00 1.01
3 2011-03-22 1.00 0.53
4 2011-03-16 0.40 1.10
4 2011-03-17 2.40 0.03
4 2011-03-18 3.16 0.10
4 2011-03-21 0.86 0.50
4 2011-03-22 1.00 0.10
I need to order the companies (sp100_id) by their averge bullishness over time into a top-3:
SELECT
sp100_id,
AVG(bullishness) as bullishness,
AVG(agreement) AS agreement
FROM stocks
WHERE _date BETWEEN '2011-03-16' AND '2011-03-22'
GROUP BY sp100_id LIMIT 3
This works fine, as the result is
SP100_ID BULLISHNESS AGREEMENT
2 2.038 0.658
4 1.564 0.366
3 0.996 0.866
Now that I have the top-3, I need the top-3 to be re-ordered by AGREEMENT, ascending:
SP100_ID BULLISHNESS AGREEMENT
4 1.564 0.366
2 2.038 0.658
3 0.996 0.866
Is this possible to do with one query? I tried the following but it didn't work. It still only orders by bullishness
SELECT
sp100_id,
AVG(bullishness) as bullishness,
AVG(agreement) AS agreement
FROM stocks
WHERE _date BETWEEN '2011-03-16' AND '2011-03-22'
GROUP BY sp100_id
ORDER BY bullishness DESC, agreement ASC LIMIT 3
So to be clear: (1) I need to find the top-3 companies with highest average bullsihness (2) this top-3 then needs to be ordered from lowest to highest agreement. Preferably with one query. Do you know how?
It's called structured query language because you can build structures in which queries (aka virtual tables) are nested inside other queries.
Take your first query, which is correct except it needs its own ORDER BY clause, and nest it in another, like so.
SELECT *
FROM (
SELECT sp100_id,
AVG(bullishness) as bullishness,
AVG(agreement) AS agreement
FROM stocks
WHERE _date BETWEEN '2011-03-16' AND '2011-03-22'
GROUP BY sp100_id
ORDER BY bullishness DESC
LIMIT 3
) subquery
ORDER BY agreement ASC
Go fiddle: http://sqlfiddle.com/#!2/c9ff0/7/0