syntax error in calculation of attendance % in mysql - mysql

SELECT name, DISTINCT studentid, count(attendance)
from attendance a,students s
where attendance = 'p'and s.studentid=a.studentid
having count(attendance)<3/4*sum(attendance);
I have 2 tables attendance and students from which i wanna choose the name of the student(from student table) and attendance (from attendance table) where studentid is the foreign key of those students whose attendance<75%. i save attendance as p and a for present and absent respectively.

You could try something like this:
Data prep
create table attendance (studentid int, attendance char(1));
insert into attendance values (1,'p'),(1,'a'),(2,'p'),(2,'p'),(2,'a'),(3,'p');
Data
select * from students;
+-----------+------+
| studentid | name |
+-----------+------+
| 1 | John |
| 2 | Matt |
| 3 | Mary |
+-----------+------+
select * from attendance;
+-----------+------------+
| studentid | attendance |
+-----------+------------+
| 1 | p |
| 1 | a |
| 2 | p |
| 2 | p |
| 2 | a |
| 3 | p |
+-----------+------------+
Query
select s.*, a.total, a.p_present
from students s
inner join (
select studentid, count(*) as total, sum(case attendance when 'p' then 1 else 0 end) * 100/count(*) as p_present
from attendance
group by studentid
) a on s.studentid = a.studentid
where a.p_present < 75 ;
Result
+-----------+------+-------+-----------+
| studentid | name | total | p_present |
+-----------+------+-------+-----------+
| 1 | John | 2 | 50.0000 |
| 2 | Matt | 3 | 66.6667 |
+-----------+------+-------+-----------+
p_present is percent present. Note that John and Matt had 50% and 66.6% attendance, respectively.
Explanation
In order to get total records, we'd do something like this:
select studentid, count(*)
from attendance
group by studentid;
In order to get total times each student was present, we'd do:
select studentid, sum(case attendance when 'p' then 1 else 0 end)
from attendance
group by studentid;
% present is going to be the # of times the student was present divided by the total. So, that's what I did in the subquery.
Once the data about the student was available, join that result with student's information and extract the information desired from both tables.

Related

MySQL Group_Concat Row to colum and Count Value

I am working with 2 tables and need help to produce an output by using group concat, and i need to sum the value first be grouping
Here is the fiddle: https://www.db-fiddle.com/f/kSwpa6y4UByAMeQWix3m3x/1
Here is the table:
CREATE TABLE teacher (
TeacherId INT, BranchId VARCHAR(5));
INSERT INTO teacher VALUES
("1121","A"),
("1132","A"),
("1141","A"),
("2120","B"),
("2122","B"),
("2123","B");
CREATE TABLE activities (
ID INT, TeacherID INT, Hours INT);
INSERT INTO activities VALUES
(1,1121,2),
(2,1121,1),
(3,1132,1),
(4,1141,NULL),
(5,2120,NULL),
(6,2122,NULL),
(7,2123,2),
(7,2123,2);
My SQL:
select IFNULL(sumhours.hr,0) as totalhours, t.branchid, t.teacherid
from teacher t
left join
(select teacherid, sum(hours) as hr from activities
group by teacherid
order by hr asc) as sumhours
on
t.teacherid = sumhours.teacherid
order by branchid, hr
Output:
+---------------+-------------------+--------------------+
| totalhours | branchid | teacherid |
+---------------+-------------------+--------------------+
| 0 | A | 1141 |
| 1 | A | 1132 |
| 3 | A | 1121 |
| 0 | B | 2120 |
| 0 | B | 2122 |
| 4 | B | 2123 |
+---------------+-------------------+--------------------+
Explanation:
Table teacher consist teacher id and branch id, while table activities consist of id, foreign key teacher id, and hours. Hours indicate duration of each activities made by teacher. Teacher can do more than one activities or may not do any activities. Teachers who not doing any activity will be set to null.
The objective of queries is to produce a table that consist of summary of teachers activity by branch and group by hours.
In the expected output table, 'Hours' is a fixed value to indicate hours from 0 - 4. A and B columns are branch. The value indicates total number of teachers who are doing activities. So, for row 0, there are 1 teacher for branch A and 2 teachers for branch B who are not doing activities.
Expected output:
+-----------+------------+------------+
| Hours | A | B |
+-----------+------------+------------+
| 0 | 1 | 2 |
| 1 | 1 | 0 |
| 2 | 0 | 0 |
| 3 | 1 | 0 |
| 4 | 0 | 1 |
+-----------+------------+------------+
It seems like you've pretty much figured it out...
SELECT totalhours hours
, branchid
, COUNT(*) total
FROM
( SELECT COALESCE(y.hr,0) totalhours
, x.branchid
, x.teacherid
FROM teacher x
JOIN
( SELECT teacherid
, SUM(hours) hr
FROM activities
GROUP
BY teacherid
ORDER
BY hr ASC
) y
ON x.teacherid = y.teacherid
) a
GROUP
BY hours
, branchid
ORDER
BY hours
, branchid;
For the rest, I would handle the pivot (and any missing data) in application code

Mysql query to get max age by section and if two or more has same age return student with smallest id

I have a table of students with temporary test values like this:
Table students
+----+-------------+-------+-----------+
| id | section_id | age | name |
+----+-------------+-------+-----------+
| 1 | 1 | 18 | Justin |
+----+-------------+-------+-----------+
| 2 | 2 | 14 | Jillian |
+----+-------------+-------+-----------+
| 3 | 2 | 16 | Cherry |
+----+-------------+-------+-----------+
| 4 | 3 | 19 | Ronald |
+----+-------------+-------+-----------+
| 5 | 3 | 21 | Marie |
+----+-------------+-------+-----------+
| 6 | 3 | 21 | Arthur |
+----+-------------+-------+-----------+
I want to query the table such that I want to get all the maximum ages of each section. However, if two students have the same age, the table produced will return the student with smallest id.
Return:
+----+------------+-----+--------+
| id | section_id | age | name |
+----+------------+-----+--------+
| 1 | 1 | 18 | Justin |
+----+------------+-----+--------+
| 3 | 2 | 16 | Cherry |
+----+------------+-----+--------+
| 5 | 3 | 21 | Marie |
+----+------------+-----+--------+
I tried this query:
SELECT ANY_VALUE(id), ANY_VALUE(section_id), MAX(age), ANY_VALUE(name) FROM
(SELECT id, section_id, age, name FROM students ORDER BY id) as X
GROUP BY section_id
Unfortunately, there are instances that id does not match the age and name.
I have on my end:
sql_mode = only_full_group_by
and I don't have a privilege to edit that, hence the any_value function but I have no idea how to use it.
This will do what you want.
It starts by finding the maximum age per section (including duplicates).
Then it joins those results with the minimum id per section (to eliminate duplicates).
And finally, select all fields for the matching id and section combinations.
SELECT s3.*
FROM students s3
INNER JOIN (
SELECT MIN(s2.id) AS id, s2.section_id
FROM students s2
INNER JOIN (
SELECT s1.section_id, MAX(s1.age) AS age
FROM students s1
GROUP BY s1.section_id
) s1 USING (section_id, age)
GROUP BY s2.section_id
) s2 USING (id, section_id);
Working SQL fiddle: https://www.db-fiddle.com/f/aezgAYM6A5KnXykceB7At1/0
I would simply use a correlated subquery:
select s.*
from students s
where s.id = (select s2.id
from students s2
where s2.section_id = s.section_id
order by s2.age desc, s2.id asc
limit 1
);
This is pretty much the simplest way to express the logic. And with an index on students(section, age, id), it should be the most performant as well.

How to select the sum() of a group of rows and the sum() of another group

I have created a SQLfiddle demo with sample data and desired result here :(http://sqlfiddle.com/#!9/dfe73a/7)
sample data
-- table company
+--------+---------+
| id | name |
+--------+---------+
| 1 | foo |
| 2 | bar |
+--------+---------+
-- table sales
+--------+---------------+-----------------+
| id | company_id | total_amount |
+--------+---------------+-----------------+
| 1 | 1 | 300.0 |
| 2 | 1 | 300.0 |
| 2 | 1 | 100.0 |
+--------+---------------+-----------------+
-- table moves
+--------+---------------+-----------------+
| id | company_id | balance_move |
+--------+---------------+-----------------+
| 1 | 1 | 700.0 |
| 2 | 1 | -300.0 |
| 2 | 1 | -300.0 |
+--------+---------------+-----------------+
I need to select every company along with the sum of it's total amount of sales and the sum of it's total balance moves
desired result
+----+----------------------+---------------------+
| id | total_amount_sum | balance_move_sum |
+----+----------------------+---------------------+
| 1 | 700 | 100 |
+----+----------------------+---------------------+
| 2 | (null) | (null) |
+----+----------------------+---------------------+
I tried this SQL query
SELECT
company.id,
sum(total_amount) total_amount_sum,
sum(balance_move) balance_move_sum
FROM company
LEFT JOIN sales ON company.id = sales.company_id
LEFT JOIN moves ON company.id = moves.company_id
GROUP BY company.id
But the sum() functions add all the redundant values came from the joins which result in 2100 (700*3) for total amount and 300 (100*3) for net balance
bad SQL statement result
+----+----------------------+---------------------+
| id | total_amount_sum | balance_move_sum |
+----+----------------------+---------------------+
| 1 | 2100 | 300 |
+----+----------------------+---------------------+
| 2 | (null) | (null) |
+----+----------------------+---------------------+
Is it possible to achieve the result I want ?
You're repeating rows by doing your joins.
Company: 1 row per company
After Sales join: 3 rows per company (1x3)
After Moves join: 9 rows per company (3x3)
You end up triplicating your SUM because of this.
One way to fix is to use derived tables like this, which calculate the SUM first, then join the resulting rows 1-to-1.
SELECT
company.id,
total_amount_sum,
balance_move_sum
FROM company
LEFT JOIN (SELECT SUM(total_amount) total_amount_sum, company_id
FROM sales
GROUP BY company_id
) sales ON company.id = sales.company_id
LEFT JOIN (SELECT SUM(balance_move) balance_move_sum, company_id
FROM moves
GROUP BY company_id
) moves ON company.id = moves.company_id
Using sub-queries to calculate the two sums separately will work.
SELECT
company.id,
(Select sum(total_amount) from sales where sales.company_id = company.id) total_amount_sum,
(Select sum(balance_move) from moves where moves.company_id = company.id) balance_move_sum
FROM company

Percentage Totals for Column Groups Using MySQL

If I have a table with the following:
+---------+-------------+
| Country | campaign_id |
+---------+-------------+
| US | 1 |
| FR | 1 |
| FR | 1 |
| UK | 1 |
| NL | 2 |
| DE | 2 |
+---------+-------------+
How do I return the percentages for each country like this:
+---------+------------+
| Country | Percentage |
+---------+------------+
| US | 25 |
| FR | 50 |
| UK | 25 |
+---------+------------+
This is where I'm up to so far:
SELECT country FROM campaigns WHERE campaign_id = 1
Just to note, it's ok to return 0.25 if that is easier.
Write a subquery to get the total number of rows in the campaign, and then divide that into the number of rows grouped by country.
SELECT a.country, ROUND(100 * COUNT(*)/b.total, 2) AS percentage
FROM yourTable AS a
CROSS JOIN (
SELECT COUNT(*) AS total
FROM yourTable
WHERE campaign_id = 1) AS b
WHERE a.campaign_id = 1
GROUP BY a.country
DEMO
for what you've given (looks like counting number of countries with a specific id):
SELECT country, ((COUNT(campaign_id)*100)/(SELECT SUM(campaign_id) FROM campaigns WHERE campaign_id = 1)) AS percent
FROM campaigns
WHERE campaign_id = 1
GROUP BY country
This is my solution for your case:
SELECT Country,
(campaign_id/((select SUM(campaign_id) FROM table1 where campaign_id = 1))*100) as percentage
FROM tablename
WHERE campaign_id = 1
GROUP BY Country, campaign_id

Get the balance of my users in the same table

Help please, I have a table like this:
| ID | userId | amount | type |
-------------------------------------
| 1 | 10 | 10 | expense |
| 2 | 10 | 22 | income |
| 3 | 3 | 25 | expense |
| 4 | 3 | 40 | expense |
| 5 | 3 | 63 | income |
I'm looking for a way to use one query and retrive the balance of each user.
The hard part comes when the amounts has to be added on expenses and substracted on incomes.
This would be the result table:
| userId | balance |
--------------------
| 10 | 12 |
| 3 | -2 |
You need to get each totals of income and expense using subquery then later on join them so you can subtract expense from income
SELECT a.UserID,
(b.totalIncome - a.totalExpense) `balance`
FROM
(
SELECT userID, SUM(amount) totalExpense
FROM myTable
WHERE type = 'expense'
GROUP BY userID
) a INNER JOIN
(
SELECT userID, SUM(amount) totalIncome
FROM myTable
WHERE type = 'income'
GROUP BY userID
) b on a.userID = b.userid
SQLFiddle Demo
This is easiest to do with a single group by:
select user_id,
sum(case when type = 'income' then amount else - amount end) as balance
from t
group by user_id
You could have 2 sub-queries, each grouped by id: one sums the incomes, the other the expenses. Then you could join these together, so that each row had an id, the sum of the expenses and the sum of the income(s), from which you can easily compute the balance.