I need to write a SQL query in MariaDB to print a report with in between summary lines with aggregated values.
e.g. the data in the EMP table is:
EmpName ROLE SALARY
A Manager 10000
B operator 8000
C operator 8500
D GM 20000
E Manager 9000
I need an output like:
ROLE EmpName SALARY
Manager A 10000
E 9000
TOTAL 19000
----------------------------
GM D 20000
TOTAL 20000
----------------------------
operator B 8000
C 8500
TOTAL 16500
Many thanks in advance.
The following query can produce an output similar to the one you want:
SELECT IF(type = 1, ROLE, 'TOTAL'), SALARY
FROM (
SELECT ROLE, SALARY, 1 AS type
FROM mytable
UNION ALL
SELECT ROLE, SUM(SALARY) AS TotalSalary, 2 AS type
FROM mytable
GROUP BY ROLE) AS t
ORDER BY ROLE, type
Demo here
Try to use WITH ROLLUP modifier, for example -
SELECT
role, empname, SUM(salary)
FROM
table1
GROUP BY
role, empname WITH ROLLUP;
Output:
GM D 20000
GM (null) 20000
Manager A 10000
Manager E 9000
Manager (null) 19000
operator B 8000
operator C 8500
operator (null) 16500
(null) (null) 55500
All NULL values for grouped columns are total values. The last row is grand total value for all salaries.
GROUP BY WITH ROLLUP modifiers documentation.
Related
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
Hi I’ve a table database1
3 columns : customer_id , income , country
Customer_id
1001
1002
...
Income
5000
6000
7000
Country
SG
HK
VN
...
How do I write a query that returns the lowest 100 earning customers per country?
Is it possible to return:
Customer ID | country code
1003 SG
1004 SG
...
1007 VN
...
So on
Thanks!
On mySQL 8 you can leverage a window function for this:
SELECT * FROM
(
SELECT
country,
customer_id,
row_number() over(partition by country order by income asc) earn_rank
FROM table
)x
WHERE x.earn_rank <= 100
You can conceive that this window function will sort the rows by country then by income, then start counting up from 1. Each time the country changes the row numbering starts over from 1 again. This means that for every country there will be a row numbered 1 (with the lowest income), and a 2, 3 etc. If we then wrap it up in another outer query that selects only rows where the number is less than 101 we get 100 rows per country
This is my sql query to get the following table below :
select c.name, s.company, p.qty, p.qty * p.price as Total
from client c, purchase p, stock s
where c.clno = p.clno AND s.company = p.company
group by c.name, s.company, p.qty, p.qty * p.price
order by sum(p.qty) desc
The output of the above query looks like this :
Name | Company | Qty | Total
John ABC 12 100
Bob XYZ 10 150
John ABC 5 50
Bob XYZ 20 250
Bob XYZ 2 20
Nav QRS 10 150
John ABC 10 150
I want to have the query to get the output as the following :
Name | Company | Qty | Total
John ABC 27 300
Bob XYZ 32 420
Nav QRS 10 150
As of now your query uses GROUP BY but does not actually aggregates data. You want to GROUP BY name and company, and SUM the quantities and amounts, like :
select c.name, s.company, SUM(p.qty), SUM(p.qty * p.price) as Total
from client c
inner join purchase p on c.clno = p.clno
inner join stock s on s.company = p.company
group by c.name, s.company
order by Total desc
Other remarks regarding your query :
always use explicit joins instead of implicit ones
you can use column aliases in the ORDER BY clause (here, Total ; this can make the query easier to read
So, for example i've got the following table;
ID COUNTRY VALUE
--------------------- -------------------- --------------------
1 India 12000
2 India 11000
3 UK 11000
4 India 15000
5 Canada 11000
And I would like to group by Country but only have the country with the highest value show up, if I would just use a group by query like:
SELECT * FROM countries GROUP BY country
I would get;
ID COUNTRY VALUE
--------------------- -------------------- --------------------
1 India 12000
3 UK 11000
5 Canada 11000
Where the value for india would be 12000. I would like the query to group on the highest value for the group by on country like:
ID COUNTRY VALUE
--------------------- -------------------- --------------------
3 UK 11000
4 India 15000
5 Canada 11000
So it's grouped on the highest value which is 15000.
DEMO
SELECT s1.ID, s1.COUNTRY, s1.VALUE
FROM countries s1
LEFT JOIN countries s2
ON s1.VALUE < s2.VALUE
AND s1.COUNTRY = s2.COUNTRY
WHERE s2.COUNTRY IS NULL;
OUTPUT
NOTE: But be carefull of ties. In that case you can get one random from those ties.
You can use the MAX aggregate function.
select
country,
max(value) value
from countries
group by
country
See the live example.
Edit: The original solution was only correct due to the nature of the data. I've removed the ID from the first query, to correct the mistake. Here is another solution (based on #Juan Carlos Oropeza's work - thank you) that will return the ID and eliminate the ties.
select
min(x.id) id,
x.country,
x.value
from (
select
c.*
from countries c
left join countries c1 on c.value < c1.value and c.country = c1.country
where c1.country is null
) x
group by
x.country,
x.value
;
See the live example - I've modified the data to cover edge cases mentioned above.
This is my employee table
empid name Date_of_joining
1 dilip 2010-01-30
2 suresh 2001-03-01
3 ramesh 2003-01-01
I want to get the number of employees with total employees group by employee date of joining
expected output
year new joining total employees
2001 10 10
2002 12 22
2003 15 27
query
select YEAR(`DATE_OF_JOINING`) as 'year', COUNT(*) as 'count1',sum(count(*)) from employee
GROUP BY YEAR(`DATE_OF_JOINING`)
You need a running total using a user defined variable.
You need a derived table cause running totals don't work with group by statement
SET #SUM = 0;
SELECT
YEAR,
NoOfEmployee AS newJoining,
(#SUM := #SUM + NoOfEmployee) AS totalJoining
FROM (
SELECT
YEAR(Date_of_joining) AS YEAR,
COUNT(*) AS NoOfEmployee
FROM
employees
GROUP BY
YEAR(Date_of_joining)
) O
here a sample