I have a MySQL (MariaDB 10.3) query, which takes almost 60 seconds to run. I need to optimise this significantly, as it's frustrating users of my web app.
The query returns the name of a user then 12 columns showing how many customers they signed up, by month, who are eligible to earn commission. It then returns a further 12 columns showing how many commission entries were recorded for the user within each month. (The query needs to return in this 24-column format for compatibility reasons.)
Here's the query:
SELECT
people.full_name AS "Name",
/* Count how many unique customers are eligible for commission in each month, for a rolling 12-month window */
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2020-08-01" AND "2020-08-31" THEN customers.id END)) AS "eligible_customers_month_1",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2020-09-01" AND "2020-09-30" THEN customers.id END)) AS "eligible_customers_month_2",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2020-10-01" AND "2020-10-31" THEN customers.id END)) AS "eligible_customers_month_3",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2020-11-01" AND "2020-11-30" THEN customers.id END)) AS "eligible_customers_month_4",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2020-12-01" AND "2020-12-31" THEN customers.id END)) AS "eligible_customers_month_5",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2021-01-01" AND "2021-01-31" THEN customers.id END)) AS "eligible_customers_month_6",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2021-02-01" AND "2021-02-28" THEN customers.id END)) AS "eligible_customers_month_7",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2021-03-01" AND "2021-03-31" THEN customers.id END)) AS "eligible_customers_month_8",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2021-04-01" AND "2021-04-30" THEN customers.id END)) AS "eligible_customers_month_9",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2021-05-01" AND "2021-05-31" THEN customers.id END)) AS "eligible_customers_month_10",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2021-06-01" AND "2021-06-30" THEN customers.id END)) AS "eligible_customers_month_11",
COUNT(DISTINCT(CASE WHEN customers.commission_start_date BETWEEN "2021-07-01" AND "2021-07-31" THEN customers.id END)) AS "eligible_customers_month_12",
/* In each month of a rolling 12-month window, count how many unique commission entries were recorded. */
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2020-08-01" AND "2020-08-31" THEN user_commission.id END)) AS "total_sales_1",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2020-09-01" AND "2020-09-30" THEN user_commission.id END)) AS "total_sales_2",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2020-10-01" AND "2020-10-31" THEN user_commission.id END)) AS "total_sales_3",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2020-11-01" AND "2020-11-30" THEN user_commission.id END)) AS "total_sales_4",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2020-12-01" AND "2020-12-31" THEN user_commission.id END)) AS "total_sales_5",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2021-01-01" AND "2021-01-31" THEN user_commission.id END)) AS "total_sales_6",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2021-02-01" AND "2021-02-28" THEN user_commission.id END)) AS "total_sales_7",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2021-03-01" AND "2021-03-31" THEN user_commission.id END)) AS "total_sales_8",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2021-04-01" AND "2021-04-30" THEN user_commission.id END)) AS "total_sales_9",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2021-05-01" AND "2021-05-31" THEN user_commission.id END)) AS "total_sales_10",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2021-06-01" AND "2021-06-30" THEN user_commission.id END)) AS "total_sales_11",
COUNT(DISTINCT(CASE WHEN user_commission.commission_paid_at BETWEEN "2021-07-01" AND "2021-07-31" THEN user_commission.id END)) AS "total_sales_12"
FROM users
LEFT JOIN people ON people.id = users.person_id
LEFT JOIN customers ON customers.user_id = users.id
LEFT JOIN user_commission ON user_commission.user_id = users.id
WHERE users.id NOT IN (103, 2, 155, 24, 137, 141, 143, 149, 152, 3, 135)
GROUP BY users.id
And here's the output from EXPLAIN SELECT:
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
1
SIMPLE
users
index
PRIMARY
PRIMARY
4
16
Using where
1
SIMPLE
people
eq_ref
PRIMARY
PRIMARY
4
users.person_id
1
Using where
1
SIMPLE
customers
ref
user_id
user_id
5
users.id
284
Using where
1
SIMPLE
user_commission
ref
comm_index,user_id
comm_index
4
users.id
465
Using index
comm_index is a UNIQUE index on the user_commission table, covering user_id,order_id,commission_paid_at.
I'm a bit stumped as to what to do next — there are indexes in place, and not many rows for the engine to parse per table.
Any clues would be much appreciated — thanks!
Lets first start that this query going for EVERY user (with the few exceptions you want to EXCLUDE -- I did not include that exclusion list in my query ), I would ask why are you trying to show sales and commission counts for all users to see how all users are doing. I would think that if I was a rep for your company, I only care about how MY activities are going.
Next, this might be a good instance to suggest a pre-aggregation table of the counts per month per user so you dont have to keep re-trying to compute on the fly. If the data does not change such as when a new customer is signed-up, or a sales commission is entered, you may be best to keep those computed at the end of every day for the given user/month/year it represents. But that too is an alternative.
Now, the WHY you are probably getting hit with large delay times, and you are using COUNT( DISTINCT ) on the given customer and commission tables is you are getting a Cartesian result. So, lets go with a scenario you have 100 users. Of those users, in a given month, one user has 3 new customers, 2 commissions because they are new. Yet a long-term rep has 37 new customers and 45 commissions. THESE are the ones killing you. Because your left-join is on user ID, it is taking 1 record from the customers table for a given user and joining that to the commission table for the same user id the sale recorded against.. So the first rep it creates 6 entries to count against (3 * 2). But the second user goes through 1,665 iterations. So, this Cartesian (or cross-join) result is killing you.
So that is the WHY its failing. Now, on to the solution I have for you. You appear to have a bunch of hard-coded dates left-and-right through the code. What happens when next month comes. Do you have to hard-code fix the begin/end dates? If so, then the solution I have for you will simplify that all.
By using the "WITH" (Common-Table-Expression aka CTE), you can pre-write queries and use those "aliase" names AS-IF you wrote each of the queries within a multi-nested query. But the benefit is the query is written once, even if you keep re-using the alias name reference.
So here is the query and I'll describe / break it down next so you can view/follow along.
with Rolling12 as
(
select
#rptMonth := #rptMonth +1 as QryMonth,
#beginDate as AtLeastDate,
date_add( #beginDate, interval 1 month ) as AndLessThanDate,
#beginDate := date_add( #beginDate, interval 1 month )
from
user_commission
JOIN ( select #rptMonth := 0,
#beginDate := date_sub(
date_add(
date_sub( curdate(),
interval day( curdate()) -1 day ),
interval 1 month ),
interval 1 year )
) sqlvars
limit 12
),
MinMaxDates as
(
select
min( AtLeastDate ) MinDate,
max( AndLessThanDate ) MaxDate
from
Rolling12
),
SumCommission as
(
select
uc.user_id,
coalesce( sum( CASE WHEN R12.QryMonth = 1 then 1 else 0 end ), 0) commission01,
coalesce( sum( CASE WHEN R12.QryMonth = 2 then 1 else 0 end ), 0) commission02,
coalesce( sum( CASE WHEN R12.QryMonth = 3 then 1 else 0 end ), 0) commission03,
coalesce( sum( CASE WHEN R12.QryMonth = 4 then 1 else 0 end ), 0) commission04,
coalesce( sum( CASE WHEN R12.QryMonth = 5 then 1 else 0 end ), 0) commission05,
coalesce( sum( CASE WHEN R12.QryMonth = 6 then 1 else 0 end ), 0) commission06,
coalesce( sum( CASE WHEN R12.QryMonth = 7 then 1 else 0 end ), 0) commission07,
coalesce( sum( CASE WHEN R12.QryMonth = 8 then 1 else 0 end ), 0) commission08,
coalesce( sum( CASE WHEN R12.QryMonth = 9 then 1 else 0 end ), 0) commission09,
coalesce( sum( CASE WHEN R12.QryMonth = 10 then 1 else 0 end ), 0) commission10,
coalesce( sum( CASE WHEN R12.QryMonth = 11 then 1 else 0 end ), 0) commission11,
coalesce( sum( CASE WHEN R12.QryMonth = 12 then 1 else 0 end ), 0) commission12
from
user_commission uc
JOIN Rolling12 R12
on uc.commission_paid_at >= R12.AtLeastDate
AND uc.commission_paid_at < R12.AndLessThanDate
-- only a single row returned for MinMaxDates source
JOIN MinMaxDates mm
where
uc.commission_paid_at >= mm.MinDate
AND uc.commission_paid_at < mm.MaxDate
group by
uc.user_id
),
SumCustomers as
(
select
c.user_id,
coalesce( sum( CASE WHEN R12.QryMonth = 1 then 1 else 0 end ), 0) customers01,
coalesce( sum( CASE WHEN R12.QryMonth = 2 then 1 else 0 end ), 0) customers02,
coalesce( sum( CASE WHEN R12.QryMonth = 3 then 1 else 0 end ), 0) customers03,
coalesce( sum( CASE WHEN R12.QryMonth = 4 then 1 else 0 end ), 0) customers04,
coalesce( sum( CASE WHEN R12.QryMonth = 5 then 1 else 0 end ), 0) customers05,
coalesce( sum( CASE WHEN R12.QryMonth = 6 then 1 else 0 end ), 0) customers06,
coalesce( sum( CASE WHEN R12.QryMonth = 7 then 1 else 0 end ), 0) customers07,
coalesce( sum( CASE WHEN R12.QryMonth = 8 then 1 else 0 end ), 0) customers08,
coalesce( sum( CASE WHEN R12.QryMonth = 9 then 1 else 0 end ), 0) customers09,
coalesce( sum( CASE WHEN R12.QryMonth = 10 then 1 else 0 end ), 0) customers10,
coalesce( sum( CASE WHEN R12.QryMonth = 11 then 1 else 0 end ), 0) customers11,
coalesce( sum( CASE WHEN R12.QryMonth = 12 then 1 else 0 end ), 0) customers12
from
customers c
JOIN Rolling12 R12
on c.commission_start_date >= R12.AtLeastDate
AND c.commission_start_date < R12.AndLessThanDate
-- only a single row returned for MinMaxDates source
JOIN MinMaxDates mm
where
c.commission_start_date >= mm.MinDate
AND c.commission_start_date < mm.MaxDate
group by
c.user_id
)
select
u.id,
p.full_name AS "Name",
com.Commission01,
com.Commission02,
com.Commission03,
com.Commission04,
com.Commission05,
com.Commission06,
com.Commission07,
com.Commission08,
com.Commission09,
com.Commission10,
com.Commission11,
com.Commission12,
cst.Customers01,
cst.Customers02,
cst.Customers03,
cst.Customers04,
cst.Customers05,
cst.Customers06,
cst.Customers07,
cst.Customers08,
cst.Customers09,
cst.Customers10,
cst.Customers11,
cst.Customers12
from
users u
JOIN People p
ON u.person_id = p.id
LEFT JOIN SumCommission com
on u.id = com.user_id
LEFT JOIN SumCustomers cst
on u.id = cst.user_id;
You state that you are running on a rolling 12-month period. For this, I have my first CTE alias "Rolling12". This query is a setup for the rest of the query. It creates MySQL variables and keeps computing an updated begin/end date for each month represented. It starts by taking the current date ex: July 6 and rolls it back to July 1. Then adds 1 month to get August 1, then subtracts 1 year from that Aug 1, 2020 for the beginning period of your 12-month rolling computation. I then simple join to the commission table and limit to 12 records, each time going forward and making a column for the beginning and ending dates of the pay periods and just assigning a month ID sequence to it.
If you highlight and just run the query inside the With Rolling12 as ( the query ), you will see what it builds out. This prevents all the hard-coding dates associated with your current 24 case/count distinct when conditions.
Then a comma and the next CTE for MinMaxDates. Here, I am querying from this 12-month roll to get the minimum begin and end date for the entire period you are reporting, so when querying the sales customers and commissions, I can join to this as a single row result for the begin/end dates of details.
Next are the SumCommission and SumCustomers. These are joining against the CTE "Rolling12" records with the JOIN so we can associate the specific commission or customer to that one date range entry. So from that, I get the query month of the rolling 12 and sum() it. But since sum() of a null results in null, I wrap it with coalesce( calculation, 0 ) to show 0 as a worst-case.
The reason for each of these being run individually and grouped by user is to prevent the Cartesian result previously mentioned.
Once those individual parts are all done, I now start with the user, join to people to get the name, then LEFT-JOIN to the respective other SUM() queries. So, if a user had only a new customer for a month, but no commission, you would only have a record in that set and not the other, thus preventing the duplication of query results requiring your DISTINCT to begin with.
So, even though it looks long and may be confusing, especially the WITH CTE context, look at it to its individual parts. The SUMs() are pre-grouped by user ID, so each sum() result will only have one possible record per user for that given period.
As for indexes to help optimize the query, I would ensure the commission and customer table have an index on ( dateField, useridField ) respectively.
I would be interested in knowing how well this performs when you give it a shot.
First of all, you select about all rows instead of only the months you are interested in.
Solution: A WHERE clause to restrict the rows taken into consideration.
Then you cross join a user's customers with the user's commissions, thus building a huge intermediate result you don't need and want.
Solution: Aggregate before joining.
In order to
This can look thus for instance:
SELECT
people.full_name AS "Name",
cu.eligible_customers_month_1,
cu.eligible_customers_month_2,
...
co.total_sales_1,
co.total_sales_2,
...
FROM users
LEFT JOIN people ON people.id = users.person_id
LEFT JOIN
(
select
user_id,
max(case when month_index = 1 then cnt else 0 end) as eligible_customers_month_1,
max(case when month_index = 2 then cnt else 0 end) as eligible_customers_month_2,
...
from
(
select
user_id,
(year(current_date) * 12 + month(current_date))
- (year(commission_start_date) * 12 + month(commission_start_date))
+ 1 as month_index,
count(*) as cnt
from customers
where commission_start_date >=
last_day(current_date) + interval 1 day - interval 1 year
group by user_id, month_num
) months
group by user_id
) cu ON cu.user_id = users.id
LEFT JOIN
(
(
select
user_id,
max(case when month_index = 1 then cnt else 0 end) as total_sales_1,
max(case when month_index = 2 then cnt else 0 end) as total_sales_2,
...
from
select
user_id,
(year(current_date) * 12 + month(current_date))
- (year(commission_paid_at) * 12 + month(commission_paid_at))
+ 1 as month_index,
count(*) as cnt
from user_commission
where commission_paid_at >=
last_day(current_date) + interval 1 day - interval 1 year
group by user_id, month_num
) months
group by user_id
) co ON co.user_id = users.id
WHERE users.id NOT IN (103, 2, 155, 24, 137, 141, 143, 149, 152, 3, 135)
ORDER BY users.id;
Recommended indexes:
create index idx1 on customers (commission_start_date, user_id);
create index idx2 on user_commission (commission_paid_at, user_id);
Locked. There are disputes about this question’s content being resolved at this time. It is not currently accepting new answers or interactions.
I am new to SQL and would like to know how to approach writing a query for this question.
Lets say we have these fields:
date_created date_unsubscribed subscriberid
How to write a SQL query that lists, by month, how many people subscribed to the list, unsubscribed from the list, and how many net subscribers there were (new subscribers minus unsubscribers).
All in a single query...
Here's one option using conditional aggregation and union all:
select month(dt),
count(case when subscribe = 1 then 1 end) subscribecount,
count(case when subscribe = -1 then 1 end) unsubscribecountt,
sum(subscribe) overallcount
from (
select date_created as dt, 1 as subscribe
from yourtable
union all
select date_unsubscribed, -1
from yourtable
where date_unsubscribed is not null
) t
group by month(dt)
The subquery creates a list of dates with a flag for subscribe or unsubscribe. Then you can use count with case to determine the appropriate number of subscribers/unsubscribers.
SQL Fiddle Demo
You could write a sum(case) (a sum with conditions) to aggregate - assuming the date_created column is never null. For instance:
ORACLE:
SELECT
TO_CHAR(DATE_CREATED,'MM-YYYY') CREATE_MONTH
,SUM(CASE WHEN date_unsubscribed is not null then 1 else 0 end) unsubscribed
,SUM(CASE WHEN date_unsubscribed is null then 1 else 0 end) subscribed
,COUNT(SUBSCRIBER_ID)
FROM
--YOURTABLENAME
--WHERE
--WHATEVER OTHER CONDITIONS YOU HAVE APPLY
GROUP BY TO_CHAR(DATE_CREATED,'MM-YYYY')
MYSQL:
SELECT
DATE_FORMAT(DATE_CREATED,'%m-%Y') CREATE_MONTH
,SUM(CASE WHEN date_unsubscribed is not null then 1 else 0 end) unsubscribed
,SUM(CASE WHEN date_unsubscribed is null then 1 else 0 end) subscribed
,COUNT(SUBSCRIBER_ID)
FROM
--YOURTABLENAME
--WHERE
--WHATEVER OTHER CONDITIONS YOU HAVE APPLY
GROUP BY DATE_FORMAT(DATE_CREATED,'%m-%Y')
Oracle solution
Here is a query using the PIVOT operator, which was created exactly for this kind of work, and ROLLUP to get the net number. This is just for illustration; I assume the year is a user or application input (bind variable :year, set to 2015 for the output), and I show the summary for January through June.
with
test_data ( date_created, date_unsubscribed, subscriber_id ) as (
select date '2015-05-10', null , 330053448 from dual union all
select date '2015-04-28', null , 330053457 from dual union all
select date '2015-05-10', null , 330053466 from dual union all
select date '2015-04-28', null , 220053475 from dual union all
select date '2015-04-28', date '2015-05-10', 330053484 from dual
),
prep ( type, val, mth ) as (
select 'Subscribed' , 1, extract(month from date_created) from test_data
where extract(year from date_created) = :year
union all
select 'Unsubscribed', -1, extract(month from date_unsubscribed) from test_data
where extract(year from date_unsubscribed) = :year
)
select nvl(type, 'Net Subscr') as description,
nvl(sum(jan), 0) as jan, nvl(sum(feb), 0) as feb, nvl(sum(mar), 0) as mar,
nvl(sum(apr), 0) as apr, nvl(sum(may), 0) as may, nvl(sum(jun), 0) as jun
from prep
pivot (
sum(val)
for mth in (1 as jan, 2 as feb, 3 as mar, 4 as apr, 5 as may, 6 as jun)
)
group by rollup(type)
order by case type when 'Subscribed' then 1 when 'Unsubscribed' then 2 else 3 end
;
DESCRIPTION JAN FEB MAR APR MAY JUN
------------ ---------- ---------- ---------- ---------- ---------- ----------
Subscribed 0 0 0 3 2 0
Unsubscribed 0 0 0 0 -1 0
Net Subscr 0 0 0 3 1 0
3 rows selected.
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
I need to tabulate the data from three tables for a report. Please see this query
SELECT
DATE( sale.sale_time ) AS dat,
location.location_name as location,
sale.sale_id AS total_orders,
SUM( CASE sale.is_cancelled WHEN 0 THEN sale.sale_amount ELSE 0 END ) AS sales,
SUM( CASE sale.is_cancelled WHEN 0 THEN sale.sale_discount ELSE 0 END ) AS discounts,
SUM( CASE sale.is_cancelled WHEN 0 THEN sale.sale_tax ELSE 0 END ) AS taxes,
COUNT( DISTINCT sale.customer_id ) AS total_customers,
SUM( CASE WHEN DATE( person.added_on ) = DATE( sale.sale_time ) THEN 1 ELSE 0 END ) AS new_customers,
SUM( CASE sale.is_cancelled WHEN 1 THEN 1 ELSE 0 END ) AS cancelled_orders
FROM
sales AS sale
INNER JOIN locations AS location ON location.location_id = sale.location_id
INNER JOIN people AS person ON person.person_id = sale.customer_id
GROUP BY
dat,location
The results for new_customers is showing wrong, often more than total_customers. Where is it wrong, and how to correct this?
The results are like this:
dat location total_orders sales discounts taxes new_customers total_customers cancelled_orders
15-03-14 Location1 52 1355 0 129.04 4 2 0
16-03-14 Location1 56 280 0 30 2 1 0
16-03-14 Location2 59 2518 0 212.2 3 6 2
As you might have guessed from the query, sales table has the columns sale_id,sale_time,sale_cost,sale_discount,sale_tax,sale_amount, is_cancelled (tinyint with values 0 or 1), and customer_id
People table has the columns person_id, first_name, added_on
By comparing the date(salessale_time) to date(person.added_on), I want to list customers added on that date
I modified the query to the following to get new customers also in the result set.
SELECT DATE(sale.sale_time) AS dat, location.location_name as location, (sale.sale_id) AS total_orders,
SUM(CASE sale.is_cancelled WHEN 0 THEN sale.sale_amount ELSE 0 END) AS sales,
SUM(CASE sale.is_cancelled WHEN 0 THEN sale.sale_discount ELSE 0 END) AS discounts,
SUM(CASE sale.is_cancelled WHEN 0 THEN sale.sale_tax ELSE 0 END) AS taxes,
count(distinct(sale.customer_id)) AS total_customers,
(select count(person_id) from people where date(added_on) = date(sale.sale_time) and person_id = sale.customer_id) as new_customers,
SUM(CASE sale.is_cancelled WHEN 1 THEN 1 ELSE 0 END) AS cancelled_orders
FROM sales AS sale
INNER JOIN locations AS location ON location.location_id = sale.location_id
GROUP BY dat,location;
I have table with structure
id
YearBorn
sex
livelen
I need to receive output result table of average livelength(livelen) for both sexes
with struct
year, len1(female), len2(male)
My query
SELECT YearBorn ,
AVG(IF(sex='F', LiveLen, 0)) len1(female),
AVG(IF(sex='M', LiveLen, 0)) len2(male)
FROM persons p
GROUP BY YearBorn
but it is not working properly
Average value that is returned is the result of dividing sum livlen of fem(male) to the total number of records livlen , but not exactly to the number of f or m*.*
What can you say about it
Replace 0 with NULL. Otherwise, the value counts too:
70 + 0 + 90
------------ = 50
3
70 + 90
------- = 80
2
Use your own AVG
SELECT
YearBorn ,
SUM(IF(sex='F', LiveLen, 0))/COUNT(IF(sex='F', LiveLen, 0)) len1(female),
SUM(IF(sex='F', LiveLen, 0))/COUNT(IF(sex='F', LiveLen, 0)) len2(male)
FROM persons p
GROUP BY YearBorn
Or use NULL which is ignored in aggregates. 0 is a value
SELECT
YearBorn ,
AVG(CASE WHEN sex = 'F' THEN LiveLen ELSE NULL END) len1(female),
AVG(CASE WHEN sex = 'M' THEN LiveLen ELSE NULL END) len2(male)
FROM persons p
GROUP BY YearBorn
CASE is more portable then inline IF too