I'm working on the following user table, where role = 2 means the user is an instructor, and role = 3 means that the user is a student.
+--------+------+---------------+
| name | role | creation_date |
+--------+------+---------------+
| Tom | 2 | 2020-07-01 |
| Diana | 3 | 2020-07-01 |
| Rachel | 3 | 2020-07-01 |
| Michel | 3 | 2020-08-01 |
+--------+------+---------------+
My goal is to select the sum value of all instructors and students, grouped by date. The result should look like this:
+------------------+---------------+---------------+
| totalInstructors | totalStudents | creation_date |
+------------------+---------------+---------------+
| 1 | 2 | 2020-07-01 |
| 0 | 1 | 2020-08-01 |
+------------------+---------------+---------------+
In this case, on 2020-07-01, I had 1 instructor and 2 students registered and on 2020-08-01, I had no instructors and I had 1 student registered.
My problem is that I am having difficulties in setting up this query, if someone can help me thank you very much!
You would need count with a case statement as follows
select count(case when role=2 then 1 end) as totalInstructors
,count(case when role=3 then 1 end) as totalStudents
,creation_date
from tbl
group by creation_date
Use conditional aggregation:
SELECT
creation_date,
COUNT(CASE WHEN role = 2 THEN 1 END) AS totalInstructors,
COUNT(CASE WHEN role = 3 THEN 1 END) AS totalStudents
FROM yourTable
GROUP BY
creation_date;
Demo
A simple GRoupBY and SUM helps
This works. because the compasison role = 2 gives 1 back it it tue and 0 if it is false
CREATE TABLE table1 (
`name` VARCHAR(6),
`role` INTEGER,
`creation_date` VARCHAR(10)
);
INSERT INTO table1
(`name`, `role`, `creation_date`)
VALUES
('Tom', '2', '2020-07-01'),
('Diana', '3', '2020-07-01'),
('Rachel', '3', '2020-07-01'),
('Michel', '3', '2020-08-01');
SELECT
SUM(`role` = 2) totalInstructors ,
SUM(`role` = 3) totalStudents,
`creation_date`
FROM table1
GROUP BY `creation_date`
ORDER BY `creation_date`
totalInstructors | totalStudents | creation_date
---------------: | ------------: | :------------
1 | 2 | 2020-07-01
0 | 1 | 2020-08-01
db<>fiddle here
Please use below query,
select
case when role = 2 then count(1) end as totalInstructors,
case when role = 3 then count(1) end as totalStudents,
creation_date
from table_name
group by creation_date;
You can use COALESCE() to replace null with 0
select COALESCE(totalInstructors, 0) as totalInstructors, COALESCE(totalStudents, 0) as totalStudents,creation_date
from
(select
case when role = 2 then count(1) end as totalInstructors,
case when role = 3 then count(1) end as totalStudents,
creation_date
from table_name
group by creation_date) qry;
Related
Hy Guys,
I have a table like that:
+----+------+
| id | grade|
+----+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | NULL |
| 5 | NULL |
| 6 | NULL |
+----+------+
Where
1 Bad
2 Good
3 Very Good
Im trying to get a result something like that:
+--------------+------+
| grade | count|
+--------------+------+
| "Bad" | 2 |
| "Good" | 1 |
| "Very Good" | 0 |
| "Not Ranked" | 3 |
+--------------+------+
Im tryng with count but no success
You can use CASE to change the value of grade ibno your desired string value,
SELECT CASE WHEN grade = 1 THEN 'Bad'
WHEN grade = 2 THEN 'Good'
WHEN grade = 3 THEN 'Very Good'
ELSE 'Not Ranked'
END as grade,
COUNT(IFNULL(grade, 0)) as `count`
FROM TableName
GROUP BY CASE WHEN grade = 1 THEN 'Bad'
WHEN grade = 2 THEN 'Good'
WHEN grade = 3 THEN 'Very Good'
ELSE 'Not Ranked'
END
Since you wanted to display all values, you need to create a subquery that returns all values and join to your table using LEFT JOIN.
SELECT a.grade,
COUNT(b.id)
FROM
(
SELECT 1 id, 'Bad' grade UNION ALL
SELECT 2 id, 'Good' grade UNION ALL
SELECT 3 id, 'Very Good' grade UNION ALL
SELECT 999 id, 'Not Ranked' grade
) a
LEFT JOIN TableName b ON a.id = IFNULL(b.grade, 999)
GROUP BY a.grade
Here's a Demo.
If you can use the numbers, and match them with the string later, then this could be a solution:
SELECT grade, count(*) as id FROM tbl GROUP BY grade
source
I have the table structure as shown below. The database is MariaDB.
+-----------+----------+--------------+-----------------+
| id_object | name | value_double | value_timestamp |
+-----------+----------+--------------+-----------------+
| 1 | price | 1589 | null |
| 1 | payment | 1590 | null |
| 1 | date | null | 2012-04-17 |
| 2 | price | 1589 | null |
| 2 | payment | 1590 | null |
| 2 | date | null | 2012-04-17 |
| 3 | price | 1589 | null |
| 3 | payment | 1590 | null |
| 3 | date | null | 2012-09-25 |
| ... | ... | ... | .. |
+-----------+----------+--------------+-----------------+
1) I need to get the duplicates by three entries: price & payment & date;
For example: the record with id_object=2 is duplicate because price, payment and date are the same as values of the record with id_object=1. Record with id_object = 3 is not the duplicate because the date is different (2012-09-25 != 2012-04-17)
2) I should remove the duplicates except one copy of them.
I thought to do three select operations and join each select on id_object. I can get the duplicates by one entry (price | payment | date). I faced the problem doing the joins
SELECT `id_object`,`name`,{P.`value_double` | P.`value_timestamp`}
FROM record P
INNER JOIN(
SELECT {value_double | value_timestamp}
FROM record
WHERE name = {required_entry}
GROUP BY {value_double | value_timestamp}
HAVING COUNT(id_object) > 1
)temp ON {P.value_double = temp.value_double | P.value_timestamp = temp.value_timestamp}
WHERE name = {required_entry}
Can someone help and show the pure (better) solution?
Though less efficient than certain alternatives, I find an approach along these lines easier to read...
SELECT MIN(id_object) id_object
, price
, payment
, date
FROM
( SELECT id_object
, MAX(CASE WHEN name = 'price' THEN value_double END) price
, MAX(CASE WHEN name = 'payment' THEN value_double END) payment
, MAX(CASE WHEN name = 'date' THEN value_timestamp END) date
FROM eav
GROUP
BY id_object
) x
GROUP
BY price
, payment
, date;
I would just group_concat() the values together and do the test that way:
select t.*
from t join
(select min(id_object) id_object
from (select id_object,
group_concat(name, ':', coalesce(value_double, ''), ':', coalesce(value_timestamp, '') order by name) pairs
from t
where name in ('price', 'payment', 'date')
group by id_object
) tt
group by pairs
) tt
on t.id_object = tt.id_object;
To actually delete the ones that are not the minimum id for each group of related values:
delete t
from t left join
(select min(id) as id
from (select id, group_concat(name, ':', coalesce(value_double, ''), ':', coalesce(value_timestamp, '' order by name) as pairs,
from t
where name in ('price', 'payment', 'date')
group by id
) tt
group by pairs
) tt
on t.id = tt.id
where tt.id is null;
I have following tables products and tests.
select id,pname from products;
+----+---------+
| id | pname |
+----+---------+
| 1 | prd1 |
| 2 | prd2 |
| 3 | prd3 |
| 4 | prd4 |
+----+---------+
select pname,testrunid,testresult,time from tests;
+--------+-----------+------------+-------------+
| pname | testrunid | testresult | time |
+--------+-----------+------------+-------------+
| prd1 | 800 | PASS | 2017-10-02 |
| prd1 | 801 | FAIL | 2017-10-16 |
| prd1 | 802 | PASS | 2017-10-02 |
| prd1 | 803 | NULL | 2017-10-16 |
| prd1 | 804 | PASS | 2017-10-16 |
| prd1 | 805 | PASS | 2017-10-16 |
| prd1 | 806 | PASS | 2017-10-16 |
+--------+-----------+------------+-------------+
I like to count test results for products and if there is no result available,for a product just show a zero for it. something like following table:
+--------+------------+-----------+----------------+---------------+
| pname | total_pass | total_fail| pass_lastweek | fail_lastweek |
+--------+------------+-----------+----------------+---------------+
| prd1 | 5 | 1 | 3 | 1 |
| prd2 | 0 | 0 | 0 | 0 |
| prd3 | 0 | 0 | 0 | 0 |
| prd4 | 0 | 0 | 0 | 0 |
+--------+------------+-----------+----------------++--------------+
I have tried different queries like following, which is just working for one product and is incomplete:
SELECT pname, count(*) as pass_lastweek FROM tests where testresult = 'PASS' AND time
>= '2017-10-11' and pname in (select pname from products) group by pname;
+-------------+---------------+
| pname | pass_lastweek |
+-------------+---------------+
| prd1 | 3 |
+-------------+---------------+
it looks so basic but still I am unable to write it, any idea?
Use conditional aggregation. The COUNT function count NULL values as zeros automatically, therefore, there is no need to take care of that.
select p.pname,
count(case when testresult = 'PASS' then 1 end) as total_pass,
count(case when testresult = 'FAIL' then 1 end) as total_fail,
count(case when testresult = 'PASS' and time >= curdate() - INTERVAL 6 DAY then 1 end) as pass_lastweek ,
count(case when testresult = 'FAIL' and time >= curdate() - INTERVAL 6 DAY then 1 end) as fail_lastweek ,
from products p
left join tests t on t.pname = p.pname
group p.id, p.pname
Generally, you need to LEFT JOIN the first table with the second one before you group. The join will give you a row for each product (even if there are no test results to join it to; INNER JOIN would exclude products with no associated tests) + an additional row for each test result (beyond the first). Then you can group them.
SELECT products.*, tests.* FROM products
LEFT JOIN tests ON products.pname = tests.pname
GROUP BY products.id
Also, I would strongly recommend using a product_id column in the tests table, rather than using pname (if a products.pname changes, your whole DB breaks unless you also update the pname field in kind for every test result). The general query would then look like this:
SELECT products.*, tests.* FROM products
LEFT JOIN tests ON products.id = tests.product_id
GROUP BY products.id
I used 2 queries , the first with conditional count and the second one is to change all null values into 0 :
select pname,
case when total_pass is null then 0 else total_pass end as total_pass,
case when total_fail is null then 0 else total_fail end as total_fail,
case when pass_lastweek is null then 0 else pass_lastweek end as pass_lastweek,
case when fail_lastweek is null then 0 else fail_lastweek end asfail_lastweek from (
select products.pname,
count(case when testresult = 'PASS' then 1 end) as total_pass,
count(case when testresult = 'FAIL' then 1 end) as total_fail,
count(case when testresult = 'PASS' and time >= current_date -7 DAY then 1 end) as pass_lastweek ,
count(case when testresult = 'FAIL' and time >= current_date -7 DAY then 1 end) as fail_lastweek ,
from products
left join tests on tests.pname = products.pname
group 1 ) t1
I am working on ice hockey software: trying to find out who in your team has collected the most points with a specific player (in this example user_id = 1).
Data structure:
goal_user_id | assist_user_id | second_assist_user_id
-----------------------------------
1 | 13856 | null
1 | 15157 | null
1 | 15157 | null
1 | 15157 | 18733
345 | 1 | 28703
18733 | 1 | null
36014 | 34867 | 1
Desired output:
user_id | partner_id | total_points
-----------------------------------
1 | 15157 | 3
1 | 18733 | 2
1 | 13856 | 1
1 | 345 | 1
1 | 28703 | 1
1 | 34867 | 1
1 | 36014 | 1
SQL Fiddle:
http://sqlfiddle.com/#!9/b1587/4/0 (Note: SQLFiddle has been acting weird today).
I managed to get it done on the across two columns but couldn't figure out for three:
SELECT
COUNT(goal_user_id) as total_points,
CASE WHEN goal_user_id <> 1
THEN goal_user_id
ELSE assist_one_user_id
END as partner_id,
CASE WHEN goal_user_id < assist_one_user_id
THEN CONCAT(goal_user_id,'-',assist_one_user_id)
ELSE CONCAT(assist_one_user_id,'-',goal_user_id)
END as player_pair
FROM goals
WHERE
assist_one_user_id IS NOT NULL AND (goals.goal_user_id = 1 OR goals.assist_one_user_id = 1)
GROUP BY player_pair
ORDER BY total_points DESC
My inclination is to break all the rows out into pairs of users. Then do the aggregation based on that:
select u2, count(*) as total_points
from ((select goal_user_id as u1, assist_one_user_id as u2 from goals) union all
(select assist_one_user_id, goal_user_id from goals) union all
(select goal_user_id, assist_two_user_id from goals) union all
(select assist_two_user_id, goal_user_id from goals) union all
(select assist_one_user_id, assist_two_user_id from goals) union all
(select assist_two_user_id, assist_one_user_id from goals)
) uu
where uu.u1 = 1 and uu.u2 is not null
group by u2
order by total_points desc;
Here is a SQL Fiddle.
I have this output from this query:
select Date,Status, count(distinct persons)from TableA where Date='2014-11-04' group by Status;
+------------+------------------------+-------------------------------+
| Date | Status | count(distinct persons) |
+------------+------------------------+-------------------------------+
| 2014-11-04 | 0 | 45 |
| 2014-11-04 | 1 | 93 |
+------------+------------------------+-------------------------------+
What I wanted to get is that:
+------------+------------------------+-------------------------------+
| Date | 0 | 1 |
+------------+------------------------+-------------------------------+
| 2014-11-04 | 45 | 93 |
+------------+------------------------+-------------------------------+
You can put a condition inside your COUNT function using CASE:
SELECT Date,
COUNT(DISTINCT CASE WHEN status = 0 THEN persons END) AS `0`,
COUNT(DISTINCT CASE WHEN status = 1 THEN persons END) AS `1`
FROM TableA
WHERE Date = '2014-11-04'
GROUP BY Date;
You can use following code -
select Date, [0], [1]
from
(select Date,Status,persons
from TableA
where Date='2014-11-04') AS SourceTable
PIVOT
(
COUNT(persons)
FOR Status IN ([0],[1])
) AS PivotTable;