I am doing a SELECT from a MYSQL table
Table looks like this
year tour starts scoring_avg
1990 EUR 5 71.56
1990 USA 6 0.0
1991 EUR 12 71.21
1991 USA 8 69.23
I am doing a SELECT like so
SELECT
year,
SUM(starts),
SUM(starts*scoring_avg) as scoring.avg
FROM scores
GROUP BY year
Goal is to get combined scoring average for the year, combining the EUR and USA rows for each year.
After the SELECT, I divide scoring_avg by starts. Since I added a 6 to starts from the second line, with no scoring_avg for that line, the result is not correct.
Works fine for year 1991.
Doesn't work for year 1990, since the scoring_avg for the USA row is 0.
Is there a way the SELECT can be modified to only use the ps.starts*ps.scoring_avg in the SUM where ps.scoring_avg in that row is greater than 0?
Thank you.
-- Ed
If you just want to change scoringavg, use conditional aggregation:
SELECT year, SUM(starts),
SUM(case when scoring_avg > 0 then starts*scoring_avg end) as scoring_avg
FROM scores
GROUP BY year;
However, I would suggest doing all the work in a single query and not doing any division afterwards. The following calculates the average that you want:
SELECT year, SUM(starts),
(SUM(case when scoring_avg > 0 then starts*scoring_avg end) /
SUM(scoring_avg > 0)
) as scoring_avg
FROM scores
GROUP BY year;
You may try this:
SELECT
year,
SUM(starts),
SUM(starts*scoring_avg) as scoring.avg
FROM scores
WHERE scoring_avg > 0
GROUP BY year
You could use an expression that conditionally returns either the value from the starts column, or 0 (or NULL), based on the value in the scoring_avg column. As an example:
IF(scoring_avg=0,0,starts)
That says, if scoring_avg has a value of zero (for a particular row), then return zero; otherwise, returns the value from the starts column. This is MySQL specific syntax, equivalent to the an ANSI-standard CASE expression:
CASE scoring_avg WHEN 0 THEN 0 ELSE starts END
In the context of your query:
SELECT s.year
, SUM( IF(s.scoring_avg=0,0,s.starts) ) AS `starts`
, SUM( s.starts*s.scoring_avg ) AS `scoring.avg`
, SUM( s.starts*s.scoring_avg ) /
SUM( IF(s.scoring_avg=0,0,s.starts) ) AS `scoring.avg_div_starts`
FROM scores s
GROUP BY s.year
Related
This question already has answers here:
How can I get multiple counts with one SQL query?
(12 answers)
Closed 2 months ago.
I am trying to see 2 conditional columns using the WHERE clause from 1 column.
For example:
I have a dataset where I have a long list of calls. I would like to see two different columns where the first column shows a count of all those calls who are equal or longer that 0 seconds and a second column that shows a count of all those calls that are equal or longer to 120 seconds.
That is my first query:
SELECT distinct year(date) Year, monthname(date) as Month, count(calls) Leads
FROM database.calls
where duration >= 120
and year(date) = 2022
group by month(date)
order by max(date);
second query:
(The only difference it's on the 3rd line where duration is equal to 0)
SELECT distinct year(date) Year, monthname(date) as Month, count(calls) Leads
FROM database.calls
where duration >= 0
and year(date) = 2022
group by month(date)
order by max(date);
Expected Result:
Year
Month
Calls(0sec)
Calls(120sec)
2022
January
654
521
2022
February
895
465
2022
March
562
321
For obtaining conditional sums you can use conditional aggregation: specify a condition inside your aggregate function, so that it aggregates only the eligible values.
SELECT YEAR(date) AS Year_,
MONTHNAME(date) AS Month_,
SUM(IF(duration > 0, 1, 0)) AS Calls0Sec,
SUM(IF(duration > 120, 1, 0)) AS Calls120Sec
FROM database.calls
WHERE YEAR(date) = 2022
GROUP BY YEAR(date), MONTHNAME(date)
ORDER BY MAX(date);
Note: you need to add every selected and non-aggregated field inside the GROUP BY clause, otherwise you can expect either DBMS errors or wrong data.
I am trying to find a way of building a result set which is comprised a comparison of data from two specified years.
So it the result set would look something akin to this
PRODUCT - 2019 QTY - 2020 QTY
Car 10 10
Boat 10 0
Plane 0 10
You can use conditional aggregation. I am guessing something like this:
select product,
sum( year = 2019 ) as qty_2019,
sum( year = 2020 ) as qty_2020
from t
group by product;
This assumes that your raw data has one row per item you want to count.
If you need to sum quantity from a column then use:
select product,
sum( case when year = 2019 then quantity else 0 end ) as qty_2019,
sum( case when year = 2020 then quantity else 0 end ) as qty_2020
from t
group by product;
I'm trying to arrange data in a table. The table has the following columns:
Customer Name, Amount, Day. The customer names are not distinct, the amount is an amount represented by dollars and the Day is over the course of 365 days.
I'm trying to arrange the amount paid per quarter, regardless of the customer name.
This is a homework assignment and I've tried this code
SELECT day as 'Quarter', SUM(amount) as 'Total Earnings'
FROM invoices
WHERE day BETWEEN 0 and 90
GROUP BY day
I'm running into 3 problems. I did the above code just to test that it would work for one quarter before i tried to tackle the whole year.
The first problem is that I need the day 'value' to be 'First' and I'm not sure how to do that at all.
Secondly, it is totaling the amounts, but not 0-90, it's totaling 1, 2, 3... 89, 90. Rather than a single row with the total 'amounts' for days 0-90.
Lastly, I'm not sure how to do another sum for the other quarters (91-180, 181-270, 271-365). I'm assuming possibly subqueries, but I'm not sure how to do that while using WHERE/BETWEEN.
My output should be something like:
Quarter | Total Earnings
-------------------------
First | 111111111
Second | 111111111
Third | 111111111
Fourth | 111111111
SELECT 'first' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day between 0 AND 90
UNION ALL
SELECT 'second' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day between 91 AND 180
UNION ALL
SELECT 'third' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day between 181 AND 270
UNION ALL
SELECT 'fourth' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day >= 271
This will get you the expected results. The group by you were using will try to group based on day unlike on quater
You could use a CASE to find what quarter a day is in and then group by that. Something like this:
SELECT `quarter` AS 'Quarter',
SUM(amount) AS 'Total Earnings'
FROM (
SELECT CASE WHEN DAY < (365/4)
THEN 'First'
WHEN t.`day` < (365/4)*2
THEN 'Second'
WHEN t.`day` < (365/4)*3
THEN 'Third'
ELSE 'Fourth'
END AS `quarter`,
t.*
FROM `table` t
) t2
GROUP BY `quarter`;
You could of course replace the 365/ whatever with just a number of days or set a variable for the number of days in a year like SET #days_in_year = 365;. I'm just manually calculating to give a quick explanation of what the number is.
With a CASE statement you can evaluate the Quarter and then you can group by Quarter:
SELECT
case
when day BETWEEN 0 and 90 then 'First'
when day BETWEEN 91 and 180 then 'Second'
when day BETWEEN 181 and 270 then 'Third'
else 'Fourth'
end Quarter,
SUM(amount) as `Total Earnings`
FROM invoices
GROUP BY Quarter
Change the day ranges as you like.
I have a simple piece of SQL code where I am trying to get the monthly averages of numbers. But the problem I am running into is if any number within any given month is 0 then the average returned is 0 or if there are any rows that don't exist with any given month then there are no values returned at all for that month. Hopefully, someone can give me some insight as to what I am doing wrong.
GROUP BY 1 = a.metric and GROUP BY 2 = a.report_dt within the subquery
I have tried inserting the missing rows with a value of 0, but as I said it will return the averaged value as 0 as well.
SELECT a.report_dt - INTERVAL 1 DAY AS 'Date',
a.metric,
a.num
FROM (SELECT *
FROM reporting.tbl_bo_daily_levels b
WHERE b.report_dt = reporting.func_first_day(b.report_dt)
AND b.report_dt > DATE_SUB(NOW(), INTERVAL 12 MONTH)
GROUP BY 1,2
)a;
My expected results are to get the average numbers of each month even if there are non-existent rows within the specified date range or even if there zeroes as values.
You need a relation of all the months you want to span. This can be made ad hoc with UNION ALL. Then left join the data on the months GROUP BY the month and the metric and get avg(num).
SELECT m.year,
m.month,
l.metric,
coalesce(avg(l.num), 0)
FROM (SELECT 2017 year,
12 month
UNION ALL
SELECT 2018 year,
1 month
UNION ALL
SELECT 2018 year,
2 month
...
SELECT 2018 year,
11 month
UNION ALL
SELECT 2018 year,
12 month) months m
LEFT JOIN reporting.tbl_bo_daily_levels l
ON year(l.report_dt) = m.year
AND month(l.report_dt) = m.month;
GROUP BY m.year,
m.month,
l.metric;
(Change the second parameter to coalesce if you want any other number than 0 if there are no numbers for a month. Or don't use coalesce() at all if you want NULL in such cases.)
I have this table below. I want to count and select the values within a month range.
Here's my query
select count(*) as c, monthname(file_date) as mn
from baguio_patrolcase
where monthname(file_date) between 'January' and 'July'
group by monthname(file_date)
order by file_date
What I want to achive is that it will also count and select the values from February an June. How will I do this?
When you convert the date to a month name, you are comparing strings. Here are two options to do what you want:
where month(file_date) between 2 and 6
where file_date >= '2015-02-01' and file_date < '2015-07-01'
The second is better, because it allows the engine to use an index on file_date (if available).
Also, between keeps the end values (it is inclusive). So, if you want February through June, then use those months, rather than 1 and 7.
What I have understood, you can use in clause:
select count(*) as c, monthname(file_date) as mn
from baguio_patrolcase
where monthname(file_date) in('January', 'July')
group by monthname(file_date)
order by file_date
Add the name of all the required months in the in clause.
You can see the SQLFiddle Demo here.