SQL AVG applied to more than one by group? - mysql

I am trying to calculate a column average by group for several groups in the same query.
Assume there are 2 columns (a,b), for each I want the average of another column (c) per group of the first column. For example, this would return the a groups and average c value per a group.
SELECT a, AVG(c)
FROM mytable
GROUP BY a
This gives me two columns.
a
AVG(c)
1
0.5
2
0.75
I want a table like with two columns per grouping column like this.
a
AVG(c)
b
AVG(c)
1
0.5
8
1.5
2
0.75
9
0.25
Thanks.

This is more simply done in separate rows with union all:
SELECT 'a' as which, a, AVG(c)
FROM mytable
GROUP BY a
UNION ALL
SELECT 'b' as which, b, AVG(c)
FROM mytable
GROUP BY b;
If you really want them side-by-side, the query is quite a bit more complicated:
SELECT MAX(a) as a, MAX(a_avg_c) as a_avg_c,
MAX(b) as b, MAX(b_avg_c) as b_avg_c
FROM ((SELECT a, AVG(c) as a_avg_c, null as b, null as b_avg_c,
ROW_NUMBER() OVER (ORDER BY a) as seqnum
FROM mytable
GROUP BY a
) UNION ALL
(SELECT null, null, b, AVG(c) as b_avc_c,
ROW_NUMBER() OVER (ORDER BY b) as seqnum
FROM mytable
GROUP BY b
)
) ab
GROUP BY seqnum;
This is more complicated because SQL treats a row as a single entity. You actually want columns on each row that are entirely unrelated to each other. So, this version creates a "relation" by assigning a sequential value and then aggregating by that value to get what you want.

You can use analytical function and distinct as follows:
Select distinct a,
Avg(c) over (partition by a) as a_avg,
B,
Avg(c) over (partition by b) as b_avg
From your_table t

Related

How do I find duplicate values across multiple columns in Mysql?

I have a table like this
I want to check the all rows in Column A with column B and get the count of duplicates.
For example, I want to get the
count of 12 as 3(2 times in A+1 time in B)
count of 11 as 2(2 times in A+0 time in B)
count of 13 as 2(1 time in A+0 time in B)
How can I acheive it?
You can calculate the total occurrences from a union all. A where clause can show only the values that occur in the A column:
select nr
, count(*)
from (
select A as nr
from YourTable
union all
select B
from YourTable
) sub
where nr in -- only values that occur at least once in the A column
(
select A
from YourTable
)
group by
nr
having count(*) > 1 -- show only duplicates
You can combine all values in A and B then do the group by.
Then only select those values found in column A.
Select A, count(A) as cnt
From (
Select A
from yourTable
Union All
Select B
from yourTable) t
Where t.A in
(select distinct A from yourTable)
Group by t.A
Order by t.A;
Result:
A cnt
11 2
12 3
13 1
See demo: http://sqlfiddle.com/#!9/9fcfe9/3

Mysql Count multiple columns based on the same values

I have the following like table
A B
Yes OOS
No No
OOS Yes
OOS No
Yes No
I want to do the following
Criteria A B
Yes 2 1
No 1 3
OOS 2 1
I can get this right with one column like
Criteria A
Yes 2
No 1
OOS 2
Here is what I have to achieve the above:
SELECT A, count(A) FROM temp_db GROUP BY A;
For this sample data, you could do this with a join of derived tables:
SELECT qa.Criteria, qa.A, qb.B FROM
(SELECT A AS Criteria, count(A) AS A FROM temp_db GROUP BY A) qa
FULL OUTER JOIN
(SELECT B AS Criteria, count(B) AS B FROM temp_db GROUP BY B) qb
ON qa.Criteria=qb.Criteria
But if there are missing criteria in the A column, they will not appear in the results of this query, and you would need the UNION ALL approach others have suggested.
You need to get the values into a single column, so you can use group by. Here is one method:
select criteria, sum(A) as A, sum(B) as B
from ((select A as criteria, 1 as A, 0 as B
from liketable
) union all
(select B, 0, 1
from liketable
)
) t
group by criteria;
Using the union all approach is the safest way in MySQL, in case not all criteria are in both columns. The following is a slight tweak that might be a bit better performance-wise:
select criteria, sum(A) as A, sum(B) as B
from ((select A as criteria, count(*) as A, 0 as B
from liketable
group by A
) union all
(select B, 0, count(*)
from liketable
group by B
)
) t
group by criteria;
Often doing two aggregations on half the data is more efficient than a bigger aggregation on all the data.
select val, sum(cntA), sum(cntB)
from (SELECT A as val, count(A) as cntA, 0 as cntB FROM temp_db GROUP BY A;
union all
SELECT B as val, 0 as cntA, count(B) as cntB FROM temp_db GROUP BY B)
group by val;

UNION ignores column names?

Why does
select 1 as a, 2 as b
union all
select 20 as b, 10 as a
returns
a b
1 2
20 10
instead of
a b
1 2
10 20
?
Is there a way to make union match column names?
Nope, selecting them in order is required with UNION.
Column names are only pertinent for the first part of the union to deifne the union columns. Other unions will join in the same order the columns are given from the first select and often have differn names. If you want want to relate the first column to the second column, You can't. However you can adjust your second select statment to put the columns in the correct order.
Union only looks at the number of columns, and their relative positions in the query string. it does NOT mix-match based on aliases or the source column names. e.g. You could have two completely different tables:
SELECT x,y FROM foo
UNION
SELECT p,q FROM bar
What should MySQL do in this case? return a single row
x,y,p,q
because none of the column names match? Nope. That'd be incorrect.
I'm not sure if this solves your problem, but you can use subqueries within the union to put the columns in the "right" order:
(select a, b from (select 1 as a, 2 as b) t)
union all
(select a, b from (select 20 as b, 10 as a) t)
I realize the question is tagged MySQL, which doesn't support full outer join. If it did, you could do do the union all as:
select coalesce(t1.a, t2.a) as a, coalesce(t1.b, t2.b) as b
from (select 1 as a, 2 as b) t1 full outer join
(select 20 as b, 10 as a) t2
on 0 = 1;
You can do this in MySQL. This assumes that none of your values are never NULL:
select coalesce(t1.a, t2.a) as a, coalesce(t1.b, t2.b) as b
from (select 1 as a, 2 as b union all select NULL, NULL) t1 join
(select 20 as b, 10 as a union all select NULL, NULL) t2
on (t1.a is null or t2.a is null) and coalesce(t1.a, t2.a) is not null

How to address multiple columns as one in MySQL?

Columns a, b and c contain some values of the same nature. I need to select all the unique values. If I had just one column I'd use something like
SELECT DISTINCT a FROM mytable ORDER BY a;
but I need to treat a, b and c columns as one and gett all the unique values ever occurring among them.
As an example, let this be a CSV representation of mytable, the first row naming the columns:
a, b, c
1, 2, 3
1, 3, 4
5, 7, 1
The result of the query is to be:
1
2
3
4
5
7
UPDATE: I don't understand why do all of you suggest wrapping it in an extra SELECT? It seems to me that the answer is
(SELECT `a` AS `result` FROM `mytable`)
UNION (SELECT `b` FROM `mytable`)
UNION (SELECT `c` FROM `mytable`)
ORDER BY `result`;
isn't it?
So you want one column all with unique values from a, b and c? Try this:
(select a as yourField from d1)
union
(select b from d2)
union
(select c from d3)
order by yourField desc
limit 5
Working example
Edited after requirements changed... There you have the order by and limit you requested. Of course, you'll get only 5 records in this example
sorry i miss understood your question. here is updated query.
select a from my table
UNION
select b from my table
UNION
select c from my table
SELECT tmp.a
FROM
(SELECT column_1 AS a
FROM table
UNION
SELECT column_2 AS a
FROM table
UNION
SELECT column_3 AS a
FROM table) AS tmp
ORDER BY `tmp`.`a` ASC
try this:
SELECT b.iResult
FROM
(SELECT a as iResult FROM tableName
UNION
SELECT b as iResult FROM tableName
UNION
SELECT c as iResult FROM tableName) b
ORDER BY b.iResult
LIMIT BY 10 -- or put any number you want to limit.

finding the lowest group of numbers in a row, not just the lowest

i know how to find the least or greatest numbers in a table row given 10 columns
greatest(a, b, c, d, etc).........least(a, b, c, d, etc).....
But what i want to do is find the 3 least or greatest columns in a row, not just the single least or greatest. You see, each column represents the ranking of a user in a certain catergory. The overall ranking is then determined by taking their three best ranked catergories from all ten.
Can this be done without having to turn the columns into rows, as despite the fact that it would require a fair bit of extra code, it comes with its own complications.
SELECT `src`, `value`
FROM
(
SELECT 'a' AS `src`, a AS `value` FROM yourtable WHERE id = 42
UNION ALL
SELECT 'b' AS `src`, b AS `value` FROM yourtable WHERE id = 42
UNION ALL
-- etc...
UNION ALL
SELECT 'j' AS `src`, j AS `value` FROM yourtable WHERE id = 42
) T1
ORDER BY `value`
LIMIT 3