SQL sub queries - one alias column - mysql

course_completions
+-----------------------------------------------+
| id coursemodid userid state timemodified |
+-----------------------------------------------+
| 370 23 2 1 1433582890 |
| 329 24 89 1 1427771915 |
| 333 30 39 1 1428309816 |
| 332 32 39 1 1428303307 |
| 327 33 40 1 1427689703 |
| 328 34 89 1 1427710711 |
| 303 35 41 1 1410258482 |
| 358 36 99 1 1432020067 |
| 365 25 2 1 1433142455 |
| 304 26 69 1 1410717866 |
| 353 37 95 1 1430387005 |
| 416 38 2 1 1438972465 |
| 300 27 70 1 1409824001 |
| 302 29 74 1 1412055704 |
| 297 30 2 1 1409582123 |
| 301 133 41 1 1410255923 |
| 336 133 91 1 1428398435 |
| 364 133 40 1 1433142348 |
| 312 133 85 1 1425863621 |
+-----------------------------------------------+
course_modules
+------------------+
| id course |
+------------------+
| 23 6 |
| 24 6 |
| 25 6 |
| 26 6 |
| 27 6 |
| 28 6 |
| 29 8 |
| 30 8 |
| 31 8 |
| 32 8 |
| 33 8 |
| 34 5 |
| 35 5 |
| 36 5 |
| 37 5 |
| 38 5 |
| 39 9 |
| 40 9 |
| 41 9 |
+------------------+
course_mod_settings
+--------------------------------------+
|id course modinstance |
+--------------------------------------+
| 27 8 30 |
| 28 8 31 |
| 29 8 32 |
| 30 8 33 |
| 31 6 23 |
| 32 6 24 |
| 33 6 25 |
| 34 6 26 |
| 35 6 27 |
| 36 6 28 |
| 37 9 39 |
| 38 9 40 |
| 39 9 41 |
+--------------------------------------+
I was trying about to frame two sub queries in one SQL statement like I want the count of 'criteria mod settings' table values in one column and count of 'course_completions' table values in one column for a particular user along with course.
There shouldn't be relation between count(cms.id) and count(cc.id) except course id, because count(cms.id) is the count of user modules and count(cc.id) is the settings count set by default.
OUTPUT:
COURSE USERID count(cms.id) count(cc.id)
6 89 3 6
6 39 7 6
6 40 5 6
8 69 3 4
8 2 0 4
8 95 4 4
COURSE : getting courseid
USERID : getting userid
count(cms.id) : getting the count of user completed modules.
count(cc.id) : getting the count of settings (ex: For course 6, settings count has 6 and for course 4, settings count has 3.
SELECT cm.course
,cc.userid
,count(cc.coursemodid) AS usermodules
,(
SELECT count(ccc.id)
FROM course_mod_settings cms
INNER JOIN course_modules cm ON cms.course = cm.course
) AS modsettings
FROM course_completions cc
INNER JOIN course_modules cm ON cc.coursemodid = cm.id
WHERE cc.STATE = 1
GROUP BY cm.cours
,cc.userid

I have read your comment above.
Have you tried something like this?
I didn't test the query, it's just a thought.
I might be wrong.
SELECT cms.course AS COURSE, cc.userid AS USERID, COUNT(cms.id), COUNT(cc.id) FROM
course_completions AS cc
INNER JOIN course_modules AS cm ON cc.coursemodid = cm.id
INNER JOIN course_mod_settings AS cms cm.course = cms.course
WHERE cc.state = 1
GROUP BY cm.course, cc.userid

Related

Get single unique row from duplicate data with multiple tables in mysql

I want to get the unique data from multiple tables with the highest id
Refer below data
Users Table
id firstname_english
------ --------------------
2 Lama
4 Akram
6 Ammar
8 Basil
10 Sami
12 Hasan
14 Adnan
16 Mamoon
18 Sulaiman
20 Wasfi
22 Mervat
Users form status with their id
id users_id status_id
------ ----------- -----------
3 2 10
368 4 10
5 4 10
402 6 9
7 6 10
9 8 10
11 10 10
223 10 10
13 12 10
3253 14 2
15 14 10
17 16 10
1488 16 9
231 16 10
19 18 10
22 20 10
750 20 9
232 22 10
24 22 10
2935 22 9
297 22 10
The Result I need
id firstname_english status_id form_id
------ -------------------- --------------------
2 Lama 10 3
4 Akram 10 368
6 Ammar 9 402
8 Basil 10 8
10 Sami 10 223
12 Hasan 10 12
........and so on
I need to display the highest data from the table 2 with the matching id of table 1
For MySql 8.0+ use row_number() window function:
select
u.id, u.firstname_english,
f.status_id, f.id form_id
from users u inner join (
select *,
row_number() over (partition by users_id order by id desc) rn
from users_form
) f on f.users_id = u.id
where f.rn = 1
See the demo.
For previous versions of MySql:
select
u.id, u.firstname_english,
f.status_id, f.id form_id
from users u inner join (
select uf.* from users_form uf
where not exists (
select 1 from users_form
where users_id = uf.users_id and id > uf.id
)
) f on f.users_id = u.id
See the demo.
Results:
| id | firstname_english | status_id | form_id |
| --- | ----------------- | --------- | ------- |
| 2 | Lama | 10 | 3 |
| 4 | Akram | 10 | 368 |
| 6 | Ammar | 9 | 402 |
| 8 | Basil | 10 | 9 |
| 10 | Sami | 10 | 223 |
| 12 | Hasan | 10 | 13 |
| 14 | Adnan | 2 | 3253 |
| 16 | Mamoon | 9 | 1488 |
| 18 | Sulaiman | 10 | 19 |
| 20 | Wasfi | 9 | 750 |
| 22 | Mervat | 9 | 2935 |

How to get the opposite of a join?

I am trying to get the rows that don't exist in one table where one table called schedules (match_week, player_home_id, player_away_id) and the other table called match (match_week, Winner_id, Defeated_id) are joined. The players look at their schedule and play a match. I am trying to get a list of the scheduled matches that do not exist in the match table. The IDs in the match table can be in either column Winner_id or Defeated_id.
I have reviewed a number of Stack Exchange examples, but most use "IS NULL" and I don't have null values. I have used a Join that does give the output of the matches played. I would like the matches that have not been played.
CSV - wp_schedule_test
+----+------------+--------------+--------------+-----------------+-----------------+
| ID | match_week | home_player1 | away_player1 | player1_home_id | player1_away_id |
+----+------------+--------------+--------------+-----------------+-----------------+
| 1 | WEEK 1 | James Rives | Dale Hemme | 164 | 169 |
| 2 | WEEK 1 | John Head | David Foster | 81 | 175 |
| 3 | WEEK 1 | John Dalton | Eric Simmons | 82 | 23 |
| 4 | WEEK 2 | John Head | James Rives | 81 | 164 |
| 5 | WEEK 2 | Dale Hemme | John Dalton | 169 | 82 |
| 6 | WEEK 2 | David Foster | Eric Simmons | 175 | 23 |
| 7 | WEEK 3 | John Dalton | James Rives | 82 | 164 |
| 8 | WEEK 3 | John Head | Eric Simmons | 81 | 23 |
| 9 | WEEK 3 | Dale Hemme | David Foster | 169 | 175 |
| 10 | WEEK 4 | Eric Simmons | James Rives | 23 | 164 |
| 11 | WEEK 4 | David Foster | John Dalton | 175 | 82 |
| 12 | WEEK 4 | Dale Hemme | John Head | 169 | 81 |
+----+------------+--------------+--------------+-----------------+-----------------+
CSV - wp_match_scores_test
+----+------------+------------+------------+
| ID | match_week | player1_id | player2_id |
+----+------------+------------+------------+
| 5 | WEEK 1 | 82 | 23 |
| 20 | WEEK 1 | 164 | 169 |
| 21 | WEEK 2 | 164 | 81 |
| 25 | WEEK 2 | 82 | 169 |
| 61 | WEEK 3 | 175 | 169 |
| 62 | WEEK 4 | 175 | 82 |
| 69 | WEEK 2 | 175 | 23 |
| 85 | WEEK 3 | 164 | 82 |
| 86 | WEEK 4 | 164 | 23 |
+----+------------+------------+------------+
The output from the mysql query are the matches that have been played. I am trying to figure out how to list the matches that have not been played from the table Schedule.
CSV - MySQL Output
+------------+------------+------------+
| match_week | player1_id | player2_id |
+------------+------------+------------+
| WEEK 1 | 164 | 169 |
| WEEK 1 | 82 | 23 |
| WEEK 2 | 164 | 81 |
| WEEK 2 | 82 | 169 |
| WEEK 2 | 175 | 23 |
| WEEK 3 | 175 | 169 |
| WEEK 3 | 164 | 82 |
| WEEK 4 | 175 | 82 |
| WEEK 4 | 164 | 23 |
+------------+------------+------------+
MYSQL
select DISTINCT ms.match_week, ms.player1_id , ms.player2_id FROM
wp_match_scores_test ms
JOIN wp_schedules_test s
ON (s.player1_home_id = ms.player1_id or s.player1_away_id =
ms.player2_id)
Order by ms.match_week
The expected output is:
CSV - Desired Output
+------------+----------------+----------------+
| match_week | player_home_id | player_away_id |
+------------+----------------+----------------+
| WEEK 1 | 81 | 175 |
| WEEK 3 | 81 | 23 |
| WEEK 4 | 169 | 81 |
+------------+----------------+----------------+
The added code I would like to use is
SELECT s.*
FROM wp_schedules_test s
WHERE NOT EXISTS
(select DISTINCT ms.match_week, ms.player1_id , ms.player2_id FROM
wp_match_scores_test ms
JOIN wp_schedules_test s
ON (s.player1_home_id = ms.player1_id or s.player1_away_id =
ms.player2_id)
Order by ms.match_week)
Unfortunately, the output yields "No Rows"
You can use a LEFT JOIN to achieve the desired results, joining the two tables on matching player ids (noting that player id values in wp_match_scores_test can correspond to either player1_home_id or player1_away_id in wp_schedules_test). If there is no match, the result table will have NULL values from the wp_match_scores_test table values, and you can use that to select the matches which have not been played:
SELECT sch.*
FROM wp_schedule_test sch
LEFT JOIN wp_match_scores_test ms
ON (ms.player1_id = sch.player1_home_id
OR ms.player2_id = sch.player1_home_id)
AND (ms.player1_id = sch.player1_away_id
OR ms.player2_id = sch.player1_away_id)
WHERE ms.ID IS NULL
Output:
ID match_week home_player1 away_player1 player1_home_id player1_away_id
2 Week 1 John Head David Foster 81 175
8 Week 3 John Head Eric Simmons 81 23
12 Week 4 Dale Hemme John Head 169 81
Note that you can also use a NOT EXISTS query, using the same condition as I used in the JOIN:
SELECT sch.*
FROM wp_schedule_test sch
WHERE NOT EXISTS (SELECT *
FROM wp_match_scores_test ms
WHERE (ms.player1_id = sch.player1_home_id
OR ms.player2_id = sch.player1_home_id)
AND (ms.player1_id = sch.player1_away_id
OR ms.player2_id = sch.player1_away_id))
The output of this query is the same. Note though that conditions in the WHERE clause have to be evaluated for every row in the result set and that will generally make this query less efficient than the LEFT JOIN equivalent.
Demo on dbfiddle

MYSQL/Query: How to make table rows into column

I have 3 tables tbl_contestant , tbl_criteria and tbl_judges. And then i have 1 more table combined this 3 table as my result, tbl_score.
tbl_criteria
------------------------
crit_id | criteria_name
16 | sports
tbl_judges
------------------------
judge_id | judge_name
61 | first
62 | second
63 | third
tbl_cotestant
--------------------------------------
con_id | contestant_number | contestant_name |
1 | 1 | john |
2 | 2 | sy |
3 | 3 | Nah |
tbl_score
--------------------------------------------------
score_id | crit_id | judge_id | contestant_number | score
1 | 16 | 61 | 1 | 25
2 | 16 | 61 | 2 | 25
3 | 16 | 61 | 3 | 25
4 | 16 | 62 | 1 | 25
5 | 16 | 62 | 2 | 73
6 | 16 | 62 | 3 | 59
7 | 16 | 63 | 1 | 70
8 | 16 | 63 | 2 | 80
9 | 16 | 63 | 3 | 70
How can i achieve this output, judge_id row turns into column based on crit_id
contestant_number | contestant_name | 16_judge_61 | 16_judge_62 | 16_judge_63 | total
1 | john | 25 | 25 | 70 |
2 | sy | 25 | 73 | 80 |
3 | Nah | 25 | 59 | 70 |
Please correct my query
SELECT DISTINCT(c.contestant_number) , contestant_name , j1.sports as
16_judge_61, j2.sports as 16_judge_62, j3.sports as 16_judge_63 from
tbl_criteria , tbl_score, tbl_contestant c
LEFT JOIN tbl_ // <-- i have no idea how start from here joining those 4 tables together
You could use CASE WHEN to solve this.
SELECT
s.contestant_number,
c.contestant_name,
SUM(CASE WHEN s.crit_id='16' AND s.judge_id='61' THEN s.score END) as 16_judge_61,
SUM(CASE WHEN s.crit_id='16' AND s.judge_id='62' THEN s.score END) as 16_judge_62,
SUM(CASE WHEN s.crit_id='16' AND s.judge_id='63' THEN s.score END) as 16_judge_63,
SUM(s.score) as Total
FROM tbl_score s
INNER JOIN tbl_contestant c ON s.contestant_number = c.contestant_number
GROUP BY s.contestant_number
see SQL Fiddle http://sqlfiddle.com/#!9/9efa5/1

mysql grouped ranking with ties

so i have data in a table like this:
id total group_id
1897 738 1
2489 716 2
2325 715 3
1788 702 2
1707 699 3
2400 688 3
2668 682 2
1373 666 1
1494 666 1
1564 660 1
2699 659 1
1307 648 4
1720 645 4
2176 644 1
1454 644 4
2385 639 3
1001 634 2
2099 634 4
1006 632 1
2587 630 3
1955 624 3
1827 624 4
2505 623 4
2062 621 3
1003 618 1
2286 615 4
2722 609 4
how can i rank the ids per group based on the total and giving the same rank when there is a tie?
i have tried this solution below but it doesnt take care of the ties.
SELECT g1.admission_no
, g1.total
, g1.stream_id
, COUNT(*) AS rn
FROM ranktest AS g1
JOIN ranktest AS g2
ON (g2.total, g2.admission_no) >= (g1.total, g1.admission_no)
AND g1.stream_id = g2.stream_id
GROUP BY g1.admission_no
, g1.stream_id
, g1.total
ORDER BY g1.stream_id
, total ;
expected
id total group_id rank
1897 738 1 1
2489 716 2 1
2325 715 3 1
1788 702 2 2
1707 699 3 2
2400 688 3 3
2668 682 2 3
1373 666 1 2
1494 666 1 2
1564 660 1 3
2699 659 1 4
1307 648 4 1
1720 645 4 2
2176 644 1 4
1454 644 4 3
2385 639 3 4
1001 634 2 4
2099 634 4 4
1006 632 1 5
2587 630 3 5
1955 624 3 6
1827 624 4 5
2505 623 4 6
2062 621 3 6
1003 618 1 6
2286 615 4 7
2722 609 4 8
If original order is not very important you can start from:
http://sqlfiddle.com/#!9/a15a2/10
SELECT
ranktest.*,
IF(#rank IS NULL,#rank:=1, IF(#prev!=group_id,#rank:=1,#rank:=#rank+1) ) rank,
#prev:=group_id
FROM ranktest
ORDER BY group_id, total
but keep in mind that this is not very efficient query from performance perspective.
From the MySQL Manual Page entitled User-Defined Variables:
In the following statement, you
might think that MySQL will evaluate #a first and then do an
assignment second:
SELECT #a, #a:=#a+1, ...;
However, the order of evaluation for expressions involving user
variables is undefined.
This is why so few people write these answers correctly and safely. The object is not to hit the desired results once, but to do so again and again in real-world environments using best practices and guaranteed results.
If you don't read the Obligatory Doc, and implement it, your results are not guaranteed. There is no lazy way to do this and one shouldn't even bother :p
select id,total,group_id,rank
from
( select id,total,group_id,
#rn:=if( group_id!=#curr_grp, greatest(#grp_rank:=1,0),
if(total=#prev_grp_total,#grp_rank,greatest(#grp_rank:=#grp_rank+1,0) ) ) as rank,
#curr_grp:=group_id,
#prev_grp_total:=total
from trans01
cross join (select #grp_rank:=0,#curr_grp:=0,#prev_grp_total:=-1) xParams
order by group_id,total desc
)xDerived;
+------+-------+----------+------+
| id | total | group_id | rank |
+------+-------+----------+------+
| 1897 | 738 | 1 | 1 |
| 1373 | 666 | 1 | 2 |
| 1494 | 666 | 1 | 2 |
| 1564 | 660 | 1 | 3 |
| 2699 | 659 | 1 | 4 |
| 2176 | 644 | 1 | 5 |
| 1006 | 632 | 1 | 6 |
| 1003 | 618 | 1 | 7 |
| 2489 | 716 | 2 | 1 |
| 1788 | 702 | 2 | 2 |
| 2668 | 682 | 2 | 3 |
| 1001 | 634 | 2 | 4 |
| 2325 | 715 | 3 | 1 |
| 1707 | 699 | 3 | 2 |
| 2400 | 688 | 3 | 3 |
| 2385 | 639 | 3 | 4 |
| 2587 | 630 | 3 | 5 |
| 1955 | 624 | 3 | 6 |
| 2062 | 621 | 3 | 7 |
| 1307 | 648 | 4 | 1 |
| 1720 | 645 | 4 | 2 |
| 1454 | 644 | 4 | 3 |
| 2099 | 634 | 4 | 4 |
| 1827 | 624 | 4 | 5 |
| 2505 | 623 | 4 | 6 |
| 2286 | 615 | 4 | 7 |
| 2722 | 609 | 4 | 8 |
+------+-------+----------+------+
came up with this answer after googling a bit..not sure is its the best but it works for my case:
SELECT id, group_id, total,
#std:=CASE WHEN #grp <> group_id THEN concat(left(#grp:=group_id, 0), 1) ELSE if(#prev=total,#std,#std+1) END AS rn,#prev:=total
FROM
(SELECT #std:= -1) s,
(SELECT #grp:= -1,#prev:=null) c,
(SELECT *
FROM table
ORDER BY group_id, total desc
) s

Dynamic Cross Tab in MySQL

I have the following table with the following data
Year | Age Group | Male | Female
2000 | 0 - 25 | 50 | 100
2000 | 26 above | 40 | 75
2001 | 0 - 25 | 150 | 86
2001 | 26 above | 65 | 83
I would like to create a cross tab in the following format
| Male | Female
2000 | 90 | 175
0 - 25 | 50 | 100
26 above | 40 | 75
2001 | 215 | 169
0 - 25 | 150 | 86
26 above | 65 | 83
I will be very grateful for your assistance.
SQLFiddle example:
select * from
(
select year,ageGroup,male,female from t
union all
select year,'' ageGroup,sum(male) male,sum(female) female
from t group by year
) st
order by year,agegroup