SQL: SELECT AS multiple value with the same FROM with different WHERE - mysql

So i have this code:
SELECT a.total_sales AS July, b.total_sales AS August, c.total_sales AS September
FROM
(SELECT EXTRACT(month FROM delivered_at) AS month, ROUND(SUM (sale_price),2) AS total_sales
FROM `bigquery-public-data.thelook_ecommerce.order_items`
WHERE status = 'Complete' AND delivered_at BETWEEN "2022-01-01" AND "2022-10-01"
GROUP BY month
ORDER BY month) a,
(SELECT EXTRACT(month FROM delivered_at) AS month, ROUND(SUM (sale_price),2) AS total_sales
FROM `bigquery-public-data.thelook_ecommerce.order_items`
WHERE status = 'Complete' AND delivered_at BETWEEN "2022-01-01" AND "2022-10-01"
GROUP BY month
ORDER BY month) b,
(SELECT EXTRACT(month FROM delivered_at) AS month, ROUND(SUM (sale_price),2) AS total_sales
FROM `bigquery-public-data.thelook_ecommerce.order_items`
WHERE status = 'Complete' AND delivered_at BETWEEN "2022-01-01" AND "2022-10-01"
GROUP BY month
ORDER BY month) c
WHERE a.month = 7 AND b.month = 8 AND c.month = 9
I got the result that i wanted, which is this:
Row July August September
1 148622.29 169310.62 209339.57
Is there any simpler ways to do this?

We can reduce 3 subquerys into 1 subquery
SELECT
SUM(IF(t.month=7,t.total_sales,0)) AS July,
SUM(IF(t.month=8,t.total_sales,0)) AS August,
SUM(IF(t.month=9,t.total_sales,0)) AS September
FROM
(
SELECT EXTRACT(month FROM delivered_at) AS month, ROUND(SUM (sale_price),2) AS total_sales
FROM `bigquery-public-data.thelook_ecommerce.order_items`
WHERE status = 'Complete' AND delivered_at BETWEEN "2022-01-01" AND "2022-10-01"
AND month in(7,8,9)
GROUP BY month
) t

Related

Including and excluding specific records

I want to find some of buyer who had special condition (in this case, transaction >= 600000 called star member)
In this case, I want to find out star member (transaction >= 600000) who exists in January 2020 and March 2020, but it does not include star member who is doing transaction in February 2020.
here's my syntax
SELECT users_id
FROM order_star_member
GROUP BY users_id
HAVING SUM(CASE WHEN MONTHNAME(createdAt) = 'January'
THEN total_price_star_member END) >= 600000
AND SUM(CASE WHEN MONTHNAME(createdAt) = 'March'
THEN total_price_star_member END) >= 600000
AND NOT EXISTS (SELECT 1 FROM order_star_member
GROUP BY users_id
having sum(case when monthname(createdAt) = 'February'
THEN total_price_star_member END) >= 600000);
and here's my fiddle
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=2c85037215fe71f700b51c8fd3a5ae76
on my fiddle, the expected result are the users_Id 15 because that id order at january and march but not in february
First in the inner t we group by month to determine all the star members.
The outer grouping groups by user_id. Their score is the sum of their star_member.
For February (m=2 (February being the second month) on the first line of the query below) if they are a star_member, they get an penalty (-100) as an arbitrary value that the SUM cannot overcome.
The only way a month_score=2 can exist if if a user has a star_member being true (1) for both January and March but not February.
SELECT users_id, SUM(IF(m=2 and star_member, -100, star_member)) as month_score
FROM
(SELECT users_id,
MONTH(createdAt) as m,
SUM(total_price_star_member) >= 600000 as star_member
FROM order_star_member
WHERE createdAt BETWEEN '20190101' AND '20190331'
GROUP BY users_id, m
) t
GROUP BY users_id
HAVING month_score=2
fiddle

Optimize subquery in SELECT

My table schema is as follow:
Indexes:
products.id PRIMARY KEY
products.description UNIQUE
expenses.id PRIMARY KEY
expenses.product_id FOREIGN KEY to product.id
My goal is to load
Cost of each product of current month (AS costs_november)
Cost of each product of last month (AS costs_october)
Change in costs of current month compared to last (current month costs - last month costs) (AS costs)
Percentage change of current month costs compared to last (last month costs * 100 / current month costs) (AS percent_diff)
I've managed to code SQL that does exactly that:
SELECT description, (SUM(cost) - IFNULL(
(
SELECT SUM(cost)
FROM expenses
WHERE month = 9 AND year = 2019 AND product_id = e.product_id
GROUP BY product_id
), 0)) AS costs,
SUM(cost) * 100 /
(
SELECT SUM(cost)
FROM expenses
WHERE month = 9 AND year = 2019 AND product_id = e.product_id
GROUP BY product_id
) AS percent_diff,
SUM(cost) AS costs_october,
IFNULL(
(
SELECT SUM(cost)
FROM expenses
WHERE month = 9 AND year = 2019 AND product_id = e.product_id
GROUP BY product_id
), 0) AS costs_september
FROM expenses e
JOIN products p ON (e.product_id = p.id)
WHERE month = 10 AND year = 2019
GROUP BY product_id
ORDER BY product_id;
But is copy-pasting the same subquery three times really the solution? In theory it requires to run four queries per product. Is there a more elegant way?
Appreciate for any help!
I would address this with conditional aggregation:
select
p.description,
sum(case when e.month = 11 then e.cost else 0 end) costs_november,
sum(case when e.month = 10 then e.cost else 0 end) costs_october,
sum(case when e.month = 11 then e.cost else -1 * e.cost end) costs,
sum(case when e.month = 10 then e.cost else 0 end)
* 100
/ nullif(
sum(case when e.month = 11 then e.cost else 0 end),
0
) percent_diff
from expenses e
inner join products p on p.id = e.product_id
where e.year = 2019 and e.month in (10, 11)
goup by e.product_id
You can avoid repeating the same conditional sums by using a subquery (your RDBMS would probably optimize it anyway, but this tends to make the query more readable):
select
description,
costs_november,
costs_october,
costs_november - costs_october costs,
costs_october * 100 / nullif(costs_november, 0) percent_diff
from (
select
p.description,
sum(case when e.month = 11 then e.cost else 0 end) costs_november,
sum(case when e.month = 10 then e.cost else 0 end) costs_october
from expenses e
inner join products p on p.id = e.product_id
where e.year = 2019 and e.month in (10, 11)
goup by e.product_id
) t
You can calculate for all months and all products at one time:
SELECT year, month,
SUM(costs) as curr_month_costs,
LAG(SUM(costs)) OVER (ORDER BY year, month) as prev_month_costs,
(SUM(costs) -
LAG(SUM(costs)) OVER (ORDER BY year, month)
) as diff,
LAG(SUM(costs)) OVER (ORDER BY year, month) * 100 / SUM(costs)
FROM expenses e JOIN
products p
ON e.product_id = p.id
GROUP BY product_id, year, month
ORDER BY year, month, product_id;
You can use a subquery if you want to select only the current month.

How to write a SQL query for finding more than percent average?

I have a table containing customer_id, customer_name, month_day, month, total_sales. The data looks like the following way
customer_id customer_name month_day month total_sales
1 ravi 2 Feb 479892.901
2 arun 7 Aug 889390.709
3 goutham 6 Sep 791831.561
4 naveen 8 Sep 797413.558
Here the average of total_sales is 739632.18 and its 5% is 36981.6 and the total is 776613.78. I want to get the total_sales in between this 776613.78 and 739632.18 on Sep.
It's not super clear to me, but maybe this?
SELECT *
FROM sales,
(SELECT avg(total_sales) AS avgval
FROM sales
WHERE month = 'Sep') tabavg
WHERE month = 'Sep'
AND day = '1'
AND total_sales > tabavg.avgval
AND total_sales < tabavg.avgval * 1.05;
Did you need something like this:
SELECT customer_id FROM a
WHERE month_day = 6 and month = 'Apr' --Date and Month filter
and total_sales > (SELECT (AVG(total_sales)*1.05) FROM a WHERE month_day = 6 and month = 'Apr');
I'm a bit confuse that why you don't have same customer_id when this is sales table
This is a query in case this is order transactions that customer_id isn't a pk
SELECT customer_id FROM a
WHERE month_day = 6 and month = 'Apr'
GROUP BY customer_id
HAVING SUM(total_sales) > (SELECT (AVG(total_sales)*1.05) FROM a WHERE month_day = 6 and month = 'Apr');
Hope its help

Retrieve repeated month data in separate rows

MySql query is like this
SELECT MONTHNAME(access_date) as date,
DATE_FORMAT( access_date, '%d/%m/%Y' ) as month_date,
COUNT( log_id ) as total_count
FROM user_activity_log
WHERE dam_id = (
SELECT dam_id
FROM dam_content_details
WHERE content_type= 'userLogin'
)
AND CAST(access_date as DATE) BETWEEN '2012-09-01'
AND '2014-01-01'
GROUP BY MONTH( access_date )
ORDER BY access_date ASC
The problem i faced is the data of November & December in 2012 year is adding with November & December of 2013 year & showing in a one row. But i want to be separate rows for this.
The second one is its only showing the first 12 months not up to 2014 January.
My sample output is like this
date month_date total_count
--------- ------------ -----------
September 15/09/2012 7
October 05/10/2012 34
November 05/11/2012 21
December 07/12/2012 49
January 01/01/2013 45
February 02/02/2013 107
March 01/03/2013 158
April 01/04/2013 100
May 01/05/2013 393
June 01/06/2013 272
Try this:
SELECT Monthname(access_date) AS DATE,
Date_format(access_date, '%d/%m/%Y') AS month_date,
Count(log_id) AS total_count
FROM user_activity_log
WHERE dam_id = (SELECT dam_id
FROM dam_content_details
WHERE content_type = 'userLogin')
AND Cast(access_date AS DATE) BETWEEN '2012-09-01' AND '2014-01-01'
GROUP BY Year(access_date),
Month(access_date)
ORDER BY access_date ASC
OR
Query WITH JOIN
SELECT Monthname(ual.access_date) AS DATE,
Date_format(ual.access_date, '%d/%m/%Y') AS month_date,
Count(DISTINCT ual.log_id) AS total_count
FROM user_activity_log ual
INNER JOIN dam_content_details dcd
ON ual.dam_id = dcd.dam_id
AND dcd.content_type = 'userLogin'
WHERE ual.access_date BETWEEN '2012-09-01' AND '2014-01-01'
GROUP BY Year(ual.access_date),
Month(ual.access_date)
ORDER BY ual.access_date ASC

getting multi row data into columns in mysql

SELECT
Day,
month,
year,
GROUP_CONCAT(total),
GROUP_CONCAT(SP_ID)
FROM
(
SELECT
DATE_FORMAT(l.act_date, '%d') AS DAY,
DATE_FORMAT(l.act_date, '%M') AS MONTH,
EXTRACT(YEAR FROM l.act_date) AS YEAR,
COUNT(*) as total,l.sp_id
FROM lead_activity2 as l
right outer join salesperson as s on l.sp_id=s.sp_id
WHERE l.act_name='scb'
AND ((l.act_date>='2012-09-07 13:03:27' )
AND (l.act_date<= '2012-11-07 13:03:27'))
GROUP BY MONTH, YEAR, DAY, l.sp_id
ORDER BY YEAR DESC, MONTH DESC, DAY DESC, l.sp_id DESC
) t GROUP BY day, month, year
http://sqlfiddle.com/#!2/1514d/3 - you can view the scheme and the query,
what i would like to get is
18 | october | 2012 | 0,0,1,1 | 6,5,4,3
spid 6 and spid 5 have no data for 18 october but still should be shown tried doing right join and right outer join both dont seem to work...
Use GROUP_CONCAT like so:
SELECT
Day,
month,
year,
GROUP_CONCAT(total),
GROUP_CONCAT(SP_ID)
FROM
(
SELECT
DATE_FORMAT(l.act_date, '%d') AS DAY,
DATE_FORMAT(l.act_date, '%M') AS MONTH,
EXTRACT(YEAR FROM l.act_date) AS YEAR,
COUNT(*) as total,l.sp_id
FROM lead_activity2 as l
WHERE l.act_name='scb'
AND ((l.act_date>='2012-09-07 13:03:27' )
AND (l.act_date<= '2012-11-07 13:03:27'))
GROUP BY MONTH, YEAR, DAY, l.sp_id
ORDER BY YEAR DESC, MONTH DESC, DAY DESC, l.sp_id DESC
) t GROUP BY day, month, year
Updated SQL Fiddle
Update: Yes you can do this, but use LEFT JOIN to include non matching sp_id. These non matching ids will have a value of NULL use IFNULL to display it with zeros like so:
SELECT
Day,
month,
year,
GROUP_CONCAT(total) Total,
GROUP_CONCAT(SP_ID) 'List of sp_ids'
FROM
(
SELECT
DATE_FORMAT(l.act_date, '%d') AS DAY,
DATE_FORMAT(l.act_date, '%M') AS MONTH,
EXTRACT(YEAR FROM l.act_date) AS YEAR,
COUNT(*) as total,
IFNULL(s.sp_id , 0) sp_id
FROM lead_activity2 as l
LEFT JOIN salesperson s ON l.sp_id = s.sp_id
WHERE l.act_name='scb'
AND ((l.act_date>='2012-09-07 13:03:27' )
AND (l.act_date<= '2012-11-07 13:03:27'))
GROUP BY MONTH, YEAR,DAY,s.sp_id
) t
ORDER BY YEAR DESC,
MONTH DESC,
DAY DESC,
sp_id DESC
Updates SQL Fiddle Demo