SQL-Statement: Show all users who are enrolled in a moodle course - moodle-api

i search an sql-statement which gives all users, who are enrolled in a Moodle_course,
example course_id = 50?
Who can help?
Thanks a lot?
PS: Is there an API from webservice? If Yes, how can i use the API?
My goal is to be able to process the XML result, which i get via rest-API from "core_enrol_get_enrolled_users" further with PHP.
Thanks a lot
D-T

Here are to SQL-Statements which works...
First, shows the users for every course
SELECT distinct
u.lastname AS Nachname,
u.firstname AS Vorname,
c.fullname AS Kursname
FROM
(((mdl_user AS u INNER JOIN mdl_user_enrolments AS user_enr ON u.id = user_enr.userid)
INNER JOIN mdl_enrol AS enr ON user_enr.enrolid = enr.id)
INNER JOIN mdl_course AS c ON enr.courseid = 638)
INNER JOIN mdl_course_completions AS course_compl ON (user_enr.userid = course_compl.userid)
WHERE u.deleted=0
Last: Show the user vor a special Course (here 111)
SELECT username, idnumber,
firstname, lastname, email
FROM mdl_user_enrolments ue
JOIN mdl_enrol en ON ue.enrolid = en.id
JOIN mdl_user uu ON uu.id = ue.userid
WHERE en.courseid = 111

Related

Inner Join SQL table multiple times for the same table

I have two tables (msg and users). I am trying to join these two tables together and print out the first and last name of the sender and the recipient as well as all of the message content if the message is marked as flag. I keep getting errors though, and have not found a good way to do this yet.
Pictures of tables for reference: https://imgur.com/a/wYuptfR
SQL query I'm using right now:
SELECT msg.*, users.uuid AS users.ruuid, users.uuid AS users.suuid, users.firstName, users.lastName
FROM msg
INNER JOIN users ON users.ruuid = msg.recipient
AND INNER JOIN users ON users.suuid = msg.sender
WHERE msg.flag = 0
You can query the same table several times if you use different aliases. Also, note you shouldn't have an and between the two join clauses:
SELECT msg.*,
r.uuid AS ruuid, r.firstName AS rfirstname, r.lastName AS rlastname,
s.uuid AS suuid, s.firstName AS sfirstname, s.lastName AS slastname
FROM msg
INNER JOIN users r ON r.uuid = msg.recipient
INNER JOIN users s ON s.uuid = msg.sender
WHERE msg.flag = 0
Try This
SELECT msg.*, u.uuid , u.firstName, u.lastName, s.uuid , s.firstName, s.lastName
FROM msg
INNER JOIN users u ON u.uuid = msg.recipient
INNER JOIN users s ON s.uuid = msg.sender
WHERE msg.flag = 0

How to JOIN three tables with IN array filter, but still get all the rows from third table

I have Users and Skills table connected through UserSkills table for many-to-many relation. Here is what's in the tables:
Users (id, username)
Skills (id, name)
UserSkills (user_id, skill_id)
I have an array of skills that I am looking for ['C#', 'SQL']. So I am doing join with IN filtering operation to get only those users that I need.
SELECT (username, GROUP_CONCAT(Skills.name)) FROM Users
JOIN UserSkills ON Users.id = UserSkills.user_id
JOIN Skills ON Skills.id = UserSkills.skill_id AND Skills.name IN ('C#', 'SQL')
GROUP BY Users.id
I want to get same users list but with all their skills, not only those that I am looking for. For instance:
In my case I get: username: 'joe' | skills: 'C#', 'SQL'
I want to get: username: 'joe' | skills : 'C#', 'SQL', 'JavaScript', 'NodeJS'
Please help.
The most straight forward way would be to just join a second time:
SELECT u.username, GROUP_CONCAT(DISTINCT userS.name)
FROM Users AS u
INNER JOIN UserSkills AS us ON u.id = us.user_id
INNER JOIN Skills AS filterS ON filterS.id = us.skill_id AND filterS.name IN ('C#', 'SQL')
INNER JOIN Skills AS userS ON userS.id = us.skill_id
GROUP BY u.id
But you will need the DISTINCT in GROUP_CONCAT as the filter skills will basically cross join with the user skills. Also worth noting is this query gets users with any of the skills specified not all. This is a better solution:
SELECT u.username, GROUP_CONCAT(s.name) AS skils
, COUNT(CASE WHEN s.name IN ('C#', 'SQL') THEN 1 ELSE NULL END) AS matchedSkillCount
FROM Users AS u
INNER JOIN UserSkills AS us ON u.id = us.user_id
INNER JOIN Skills AS s ON s.id = us.skill_id
GROUP BY u.id
HAVING matchedSkillCount > 0 -- You can replace 0 with the number of matches you want
;
...but on very large databases, the above might become costly; a couple other alternatives:
SELECT u.username, GROUP_CONCAT(DISTINCT userS.name)
FROM Skills AS filterS
INNER JOIN UserSkills AS ufs ON filterS.id = ufs.skill_id
INNER JOIN Users AS u ON ufs.user_id = u.id
INNER JOIN UserSkills AS us ON u.id = us.user_id
INNER JOIN Skills AS userS ON us.skill_id = userS.id
WHERE filterS.name IN ('C#', 'SQL')
GROUP BY u.id;
SELECT u.username, GROUP_CONCAT(s.name) AS skils
FROM Users AS u
INNER JOIN UserSkills AS us ON u.id = us.user_id
INNER JOIN Skills AS sON s.id = us.skill_id
WHERE u.id IN (
SELECT DISTINCT ufs.user_id
FROM Skills AS filterS
INNER JOIN UserSkills AS ufs ON filterS.id = ufs.skill_id
WHERE filterS.name IN ('C#', 'SQL')
)
GROUP BY u.id
;
Logically, the WHERE is applied after the joins; but most RDBMS (like MySQL) optimize execution by using the WHERE conditions to minimize the rows joined together.
select USERS.name,GROUP_CONCAT(Skills.name) from USERS join USERSKILLS on users.id =
USERSKILLS.user_id join skills on USERSKILLS.SKILL_ID = skills.ID and users.id in (
select distinct users.id from
users join USERSKILLS on users.id = USERSKILLS.user_id join skills on skills.id = userSkills.skill_id and
Skills.name IN ('C#', 'SQL'))
group by USERS.name;

Moodle Dates When User Completed Course

I am trying to get the dates for when a user has last completed the course, I have the following working SQL but its giving the wrong dates.
I have dates for all modules, but know they are wrong as there a couple of months old, and I know 1 course was completed yesterday.
SELECT
u.username,
c.shortname,
DATE_FORMAT(FROM_UNIXTIME(gi.timemodified), '%d/%m/%Y') AS 'date'
FROM moodle.user u
JOIN moodle.grade_grades g ON g.userid = u.id
JOIN moodle.grade_items gi ON g.itemid = gi.id
JOIN moodle.course c ON c.id = gi.courseid
WHERE u.email = ?
GROUP BY c.shortname
The course completions should be in mdl_course_completions
SELECT u.username,
c.shortname,
cc.timecompleted
FROM mdl_course_completions cc
JOIN mdl_course c ON c.id = cc.course
JOIN mdl_user u ON u.id = cc.userid
WHERE u.email = :email

Moodle, mysql query users enrolment status by course

this is a query in moodle to get alumn status in courses.
A 'X' indicates user is enrolled, empty not enrolled.
We have to add a subquery for every course we have...
Could anyone help to do a better query?
SELECT
user1.firstname AS Firstname,
user1.lastname AS Lastname,
user1.email AS Email,
if (exists( SELECT
ue.id
FROM mdl_user_enrolments AS ue
JOIN mdl_enrol AS en ON en.id = ue.enrolid
JOIN mdl_course AS course ON course.id = en.courseid
WHERE user1.id = ue.userid AND course.id = '1'), 'X', '') as "Enrolled Course One"
FROM mdl_user AS user1
LEFT JOIN mdl_user_enrolments AS ue ON ue.userid = user1.id
LEFT JOIN mdl_enrol AS en ON en.id = ue.enrolid
LEFT JOIN mdl_course AS course ON course.id = en.courseid
[...]
WHERE user1.deleted='0'
You could try something like this but it will take a long time to process if you have a lot of users and courses
SELECT CONCAT(u.id, '_', c.id) AS uniqueid,
u.id AS userid,
u.firstname,
u.lastname,
u.email,
MAX(CASE WHEN ue.id IS NULL THEN '' ELSE 'X' END) AS enrolled,
c.id AS courseid,
c.fullname AS coursename
FROM mdl_user u
CROSS JOIN mdl_course c
LEFT JOIN mdl_enrol e ON e.courseid = c.id
LEFT JOIN mdl_user_enrolments ue ON u.id = ue.userid AND ue.enrolid = e.id
WHERE u.deleted = 0
GROUP BY u.id, c.id

Combined queries returns different results

The results of a query I created from two different queries is not returning the same results.
Query A
SELECT
c.fullname Course,
u.firstname First,
u.lastname Last,
u.id ID,
u.institution Company
FROM (mdl_scorm_scoes_track AS st)
JOIN mdl_user AS u ON st.userid=u.id
JOIN mdl_scorm AS sc ON sc.id=st.scormid
JOIN mdl_course AS c ON c.id=sc.course
Join mdl_user_enrolments AS uenr ON uenr.userid=u.id
Join mdl_enrol AS enr ON enr.id=uenr.enrolid
WHERE (
(st.value='incomplete' OR st.value='not attempted')
AND DATEDIFF(NOW(), FROM_UNIXTIME(uenr.timecreated)>60)
ORDER BY c.fullname, u.lastname,u.firstname, u.id
Query B
SELECT
c.fullname AS Course,
u.firstname AS Firstname,
u.lastname AS Lastname,
u.id AS ID,
u.institution AS Company,
IF (u.lastaccess = 0,'never',
DATE_FORMAT(FROM_UNIXTIME(u.lastaccess),'%Y-%m-%d')) AS dLastAccess
,(SELECT DATE_FORMAT(FROM_UNIXTIME(timeaccess),'%Y-%m-%d') FROM mdl_user_lastaccess WHERE userid=u.id AND courseid=c.id) AS CourseLastAccess
FROM mdl_user_enrolments AS ue
JOIN mdl_enrol AS e ON e.id = ue.enrolid
JOIN mdl_course AS c ON c.id = e.courseid
JOIN mdl_user AS u ON u.id = ue.userid
LEFT JOIN mdl_user_lastaccess AS ul ON ul.userid = u.id
WHERE ul.timeaccess IS NULL AND (DATEDIFF(NOW(), FROM_UNIXTIME(ue.timecreated))>60)
ORDER BY u.id, c.fullname
I have combined them into Query C
SELECT
c.fullname AS Course,
u.firstname AS Firstname,
u.lastname AS Lastname,
u.id AS IDNumber,
u.institution AS Institution,
IF (u.lastaccess = 0,'never',
DATE_FORMAT(FROM_UNIXTIME(u.lastaccess),'%Y-%m-%d')) AS dLastAccess
,(SELECT DATE_FORMAT(FROM_UNIXTIME(timeaccess),'%Y-%m-%d') FROM mdl_user_lastaccess WHERE userid=u.id AND courseid=c.id) AS CourseLastAccess
FROM mdl_user_enrolments AS ue
JOIN mdl_enrol AS e ON e.id = ue.enrolid
JOIN mdl_course AS c ON c.id = e.courseid
JOIN mdl_user AS u ON u.id = ue.userid
LEFT JOIN mdl_user_lastaccess AS ul ON ul.userid = u.id
WHERE (ul.timeaccess IS NULL OR ue.userid IN
(SELECT u.id
FROM (mdl_scorm_scoes_track AS st)
JOIN mdl_scorm AS sc ON sc.id=st.scormid
WHERE c.id=sc.course AND st.userid=u.id AND (st.value='incomplete' OR st.value='not attempted')
)
)AND (DATEDIFF(NOW(), FROM_UNIXTIME(ue.timecreated))>60)
ORDER BY c.fullname, u.lastname,u.firstname
I have not found where my logic is incorrect in Query C. Query C is adding an incorrect record not found by either A or B and duplicating entries in a couple of cases.
I would like some pointers on where my logic on combining the 2 went astray.
I fixed the commas in this post, the actual queries did have them.
My intent is to list all users that have been enrolled into a course but within a given timeframe, have not logged into the system, accessed the course and finally have not completed the activity in the course.
So the logic I am looking for is:
If the user has not logged in within 60 days - display name
If logged in but has not accessed the course within 60 days - display name
If logged in and has accessed the course but has not completed the course activity- display name
Query A does list all users that started the activity but have not completed within 60 days
Query B does list all users that have not logged in or accessed the course within 60 days
In trying to combine the 2 queries to satisfy all 3 conditions is where I am having problems. I first tried a UNION but could not get it to work.
You are not selecting from the same worlds in both queries. Query A has 6 tables it is selecting from and Query B has 5 (its missing the mdl_scorm table) and your Query C has the 5 tables that Query B has. So now your scope for Query A has changed. That extra table could have been eliminating rows in the join that are now appearing in Query C for the selects of A.
I would check Query A with the mdl_scorm table missing from the query and you probably have a different result size.
Think of it as
select COLUMNS from TABLES where FILTERS; TABLES (universe of data) needs to be the same for both queries for you to just join the COLUMNS and FILTERS (expand selection and limit results)