Using SELECT clause inside AVG function - mysql

I have the following table:
table: people
id | name | income
==========================
1 Bob 10
2 John 5
3 Amy 15
4 Alyson 5
5 Henry 20
I want to take the average of only a select number of rows, like this:
SELECT
id,
name,
(AVG(
SELECT income FROM people WHERE FIND_IN_SET(id, '1,2,3')
) - income) AS averageDiff
FROM people;
I expect to get a result like this:
id | name | averageDiff
==========================
1 Bob 0
2 John 5
3 Amy -5
4 Alyson -5
5 Henry 10
However, I get an error (#1064) when trying to use the SELECT clause inside of the AVG function. How can I do this?

Use this syntax:
SELECT avg(income) FROM people WHERE FIND_IN_SET(id, '1,2,3')
You need to enclose the above query in brackeds in this way:
SELECT
......
(ABS(IFNULL(`age`, 0)
- IFNULL((SELECT AVG(age) FROM people WHERE FIND_IN_SET(id, '1,2,3')), 0)))
+ (ABS(IFNULL(`income`, 0)
- IFNULL((SELECT AVG(income) FROM people WHERE FIND_IN_SET(id, '1,2,3')), 0))) AS sumAvg
FROM `people`
....

If you want the average for everybody as your starting point, calculate that with a query and cross join it to the people you want to include:
SELECT
people.id,
people.name,
people.income - av.avgincome AS averageDiff
FROM people
CROSS JOIN (SELECT AVG(income) AS avgincome FROM people) av
WHERE people.ID IN (1, 2, 3)
If you want the average for the subset of people with ID 1, 2 or 3 as your starting point you can do it like this:
SELECT
people.id,
people.name,
people.income - av.avgincome AS averageDiff
FROM people
CROSS JOIN (
SELECT AVG(income) AS avgincome
FROM people
WHERE ID IN (1, 2, 3)) av
WHERE people.ID IN (1, 2, 3)
Both approaches avoid the correlated subquery (meaning a SELECT for a column name based on the top-level table), which is slow with large recordsets.
The FIND_IN_SET(people.id, '1,2,3') will work, but if you have an index ontable.idtheIN (1, 2, 3)` will be much faster. It will probably be faster even if you don't have the index.

Related

SQL nested query under WHERE

One of the test questions came by with following schemas, to look for the best doctor in terms of:
Best scored;
The most times/attempts;
For each medical procedures (in terms of name)
[doctor] table
id
first_name
last_name
age
1
Phillip
Singleton
50
2
Heidi
Elliott
34
3
Beulah
Townsend
35
4
Gary
Pena
36
5
Doug
Lowe
45
[medical_procedure] table
id
doctor_id
name
score
1
3
colonoscopy
44
2
1
colonoscopy
37
3
4
ulcer surgery
98
4
2
angiography
79
5
3
angiography
84
6
3
embolization
87
and list goes on...
Given solution as follow:
WITH cte AS(
SELECT
name,
first_name,
last_name,
COUNT(*) AS procedure_count,
RANK() OVER(
PARTITION BY name
ORDER BY COUNT(*) DESC) AS place
FROM
medical_procedure p JOIN doctor d
ON p.doctor_id = d.id
WHERE
score >= (
SELECT AVG(score)
FROM medical_procedure pp
WHERE pp.name = p.name)
GROUP BY
name,
first_name,
last_name
)
SELECT
name,
first_name,
last_name
FROM cte
WHERE place = 1;
It'll mean a lot to be clarified on/explain on how the WHERE clause worked out under the subquery:
How it worked out in general
Why must we match the two pp.name and p.name for it to reflect the correct rows...
...
WHERE
score >= (
SELECT AVG(score)
FROM medical_procedure pp
WHERE pp.name = p.name)
...
Thanks a heap!
Above is join with doctor and medical procedure and group by procedure name and you need doctor names with most attempt and best scored.
Subquery will join by procedure avg score and those who have better score than avg will be filtered.
Now there can be multiple doctor better than avg so taken rank by procedure count so most attempted will come first and then you taken first to pick top one

compare mysql numeric values group_concat of two columns with join

I have 3 tables
1.users
user_id nationality
1 Egyptian
2 Palestinian
3 French
centers
id center_name
1 q
12 y
5 x
23 z
centers_users
student_id center_id
1 12
2 5
3 5
1 23
2 12
what I expect
Nationality center_name count_of_users_from this country
Egyptian y,z 10
Palestinian x,y 33
French x,q 7
I have tried many mysql queries but I cannot get the result I want
Final query I execute:
SELECT * from (SELECT (LENGTH(GROUP_CONCAT(DISTINCT user_id))-ENGTH(REPLACE(GROUP_CONCAT(DISTINCT user_id), ',', ''))) as ss,GROUP_CONCAT( DISTINCT user_id) ,nationality from user where user_id in(SELECT student_id FROM `centers_users`) GROUP by nationality)a
But only get the count with nationality.
When I Join with centers gives me redundancy because I cannot put "ON" condition with
group_concat
How can I implement it?
Thanks..
I think you want to join the tables and aggregate:
select u.nationality,
group_concat(distinct c.center_name) as center_names,
count(distinct user_id) as users_from_this_country
from users u join
user_centers uc
on u.user_id = uc.student_id join
centers c
on c.center_id = uc.center_id
group by u.nationality;
You may be able to use count(*) for users_from_this_country. It depends on how you want to count a user who is in multiple centers in the same country.

Better approach to solving this Mysql query

I have two tables similar to the below examples. I wrote a query to combine the two tables and get the total score of the students. The total score consists of (caone+catwo+examscore). I am searching to see if there are other better approaches to solving this in terms of performance and also syntax wise. Thanks
ca table
name id course ca_cat score
one 1 maths 1 10
one 1 maths 2 6
two 2 maths 1 9
two 2 maths 2 7
exam table
name id course score
one 1 maths 50
two 2 maths 49
My query is shown below
WITH
firstca AS (
SELECT
id,
name,
score,
subject,
FROM
ca
WHERE
cacount =1 ),
secondca AS (
SELECT
id,
name,
score,
subject,
FROM
ca
WHERE
cacount=2),
exam AS (
SELECT
id,
name,
score,
subject,
FROM
exam),
totalscore AS (
SELECT
fca.studentid,
fca.name,
fca.subject,
fca.score AS firstcascore,
sca.score AS secondcascore,
ex.score AS examscore,
(fca.score +sca.score) AS totalca,
(fca.score+sca.score+ex.score) AS totalscores,
FROM
firstca AS fca
JOIN
secondca AS sca
ON
fca.studentid=sca.studentid
AND fca.subject=sca.subject
JOIN
exam AS ex
ON
fca.studentid=ex.studentid
AND fca.subject=ex.subject
The final result table can be similar to this
name id course caone catwo exam totalscore
one 1 maths 10 6 50 66
two 2 maths 9 7 49 65
Is there a better way to write this query, maybe without the with statement or using subqueries and unions?
I wish to learn from every answer here.
Below is for BigQuery Standard SQL
#standardSQL
SELECT name, id, course, caone, catwo, exam,
caone + catwo + exam AS totalscore
FROM (
SELECT name, id, course,
MAX(IF(ca_cat = 1, t2.score, NULL)) AS caone,
MAX(IF(ca_cat = 2, t2.score, NULL)) AS catwo,
ANY_VALUE(t1.score) AS exam
FROM `project.dataset.exam` t1
JOIN `project.dataset.ca` t2
USING (name, id, course)
GROUP BY name, id, course
)
If to apply to sample data from your question - output is
Row name id course caone catwo exam totalscore
1 one 1 maths 10 6 50 66
2 two 2 maths 9 7 49 65

Assign rows based on number of "chances" a user has

I need to assign jobs to users based on a score (number of "chances") calculated from previous jobs they have done. Here's my table of users:
user chances
Anna 6
Barry 4
Steve 3
Jackson 3
Helga 3
Maureen 3
Paul 3
Karen 2
Anita 2
Samson 2
Frank 2
Jean 1
Lilly 1
Boris 1
In another table, I have 100 rows of unassigned jobs (with currently NULL user), e.g.
id title user
1 Sort filing NULL
2 Clean office NULL
3 Order stationery NULL
I want to assign these jobs to the users above using a weighting based on the number of "chances" they have. For example, Anna will have 6 chances to be assigned one of these jobs, while Boris will have 1.
I've been playing around with a CASE which will assign a user to jobs, but nothing is satisfactory.
What's the best way for me to achieve this? Thanks
Presumably, you're after something like this...
SELECT user
FROM my_table
ORDER BY RAND() * chances * (SELECT SUM(chances) FROM my_table) DESC ;
If the changes are a small number and integers, then the easiest way might be:
update anothertable at
set user = (select user
from chances c cross join
(select 1 as n union all select 2 union all select 3 union all
select 4 union all select 5 union all select 6
) n
on c.chances <= n.n
where at.user is null
order by rand()
limit 1
);
The where clause is just so MySQL doesn't get the (brilliant) idea of optimizing away the subquery and only calling it once.

SQL Table Solution With Calculating Different Columns in Same Table

I have a sql table like :
id buy_product buy_product_total sell_product sell_product_total
1 apple 5
2 banana 8
3 cake 20
4 apple 1
5 cake 2
6 apple 2
My problem is, I want to show product name and how many product left. Like :
product_name left
apple 6
cake 18
How can I show like that solution with sql query ?
I create table as answerers as :
Buy Table
id product_name total
1 apple 5
2 banana 8
3 cake 20
4 apple 2
Sell Table
id product_name total
1 apple 1
2 cake 2
I want to table like this
product_name left
apple 6
banana 8
cake 18
Is not a good table, could be better that buy and sell to be the same collumn buy with positive values and sell with negative.
But answer your question, suppose that your table name is myTable,
obs: you can execute every select separeted to understand better
select buy_product as product_name, (buy_total - sell_total) as left
from (
(select buy_product, sum(buy_product_total) as buy_total
from myTable where buy_product_total is not null group by buy_product) as buy_list
inner join
(select sell_product, sum(sell_product_total) as sell_total
from myTable where sell_product_total is not null group by sell_product) as sell_list
on buy_list.buy_product = sell_list.sell_product
)
As others have noted, your table structure is less than optimal.
However, given what you have, this will give you the results you're after.
select product, sum(total) from
(
select buy_product as product, buy_product_total as total
from yourtable
where buy_product is not null
union
select sell_product, -sell_product_total
from yourtable
where sell_product is not null
) v
group by product
Or, with your two tables
select product_name, sum(total) from
(
select product_name, total
from buy_table
union
select product_name, -total
from sell_table
) v
group by product_name
You should consider a different database design that is more appropriate (You may want to read up on normalization), but query follows:
SELECT t1.buy_product_total - t2.sell_product_total
FROM ProductTable t1, ProductTable t2
WHERE t1.buy_product = t2.sell_product
i.e. You're joining the table to itself using a 'self join'...