Number of courses completed by student..MOODLE - moodle

what would be query to find no. of students who have completed their courses in MOODLE?
i am using follwing query :
elect mu.id as student_id,count(mcc.course) as completed_course from mdl_user mu join mdl_course_completions mcc on mcc.userid=mu.id JOIN mdl_course mc on mc.id=mcc.course WHERE mcc.userid = $user_id group by mu.id

I notice that you seem to have limited the results to only a single userid (WHERE mcc.userid = $user_id), so you should probably remove that restriction if you want to get details of more than one student.
You don't really need to join with the mdl_course or mdl_user tables, as there is only one mdl_course_completions table for each student + course combination.
You should, however, add a restriction on the 'timecompleted' field, to make sure it is not null (mdl_course_completions records are created when a student starts on a course, to record the timeenrolled and timestarted; when the course is complete the timecompleted field is set as well).
This should give you:
SELECT userid AS student_id, COUNT(*) AS completed_courses
FROM mdl_course_completions
WHERE timecompleted IS NOT NULL
Which will list the number courses each student has completed.
If, instead (and as stated at the start of the question), you want the number of students who have completed at least one course, then the query would be:
SELECT DISTINCT(userid)
FROM mdl_course_completions
WHERE timecompleted IS NOT NULL

Related

Problem adding a number of students registered to a course (as a row) to a SELECT statement in POSTGRES

I have a database of courses. I need to get a name, a a topic, a teacher, a duration and a number of students registered. I get the first four successfully, but not the last one.
Here is what my tables look like:
courses table
journal table
all tables
That's the successful part for the first four:
SELECT c.name, t.topic_name AS topic, u.name || ' '|| u.surname AS TEACHER, ((c.end_date - c.start_date) / 7)::int AS duration
FROM public.course c
RIGHT JOIN public.topic t ON c.topic_id = t.topic_id
RIGHT JOIN public.teacher_course tc ON c.course_id = tc.course_id
RIGHT JOIN public.user u ON tc.teacher_id = u.user_id
WHERE u.role_id = 2;
Basically, to know the number of registered students per course, I only need to count records in the journal table for each course, but when I add
count(j.id_record) AS students_registered
it just breaks and asks me to group everything by and blah blah.
I'm confused about that. How to get this number correctly for each course?

Update count in row after Insert

I'm completely new to SQL and have a question. I am using is PostgreSQL.
I have two tables called "employees" and "offices"
The table "employees" have a list of unique employees with each having an OfficeID (The office where they work).
What I want to do is to "count" the number of appearances of the Office_ID and take that count into the table "offices" where the "office_ID" have a column called "number_of_employees".
Being completely new to SQL the only thing I have managed to even come close to this is fore example.
SELECT COUNT(*)
FROM employees
WHERE office_id = 203
But this only selects and gives the sum of rows with the id "203" that has to be manually entered into "number of empolyees"
What I want is a trigger function that updates the field "number_of_empolyees" when a new record is inserted into the table "empolyees"
A view is the way to go here.
I am assuming since you're completely new to SQL, you're unsure how to make it work (Edit: just seen your comment after posting :^D) .
The correct way to count employees for each office is:
SELECT office_id, COUNT(*) as employeeCount
FROM employees
GROUP BY office_id
Note how your WHERE office_id = XXX has been replaced by a GROUP BY office_id in order to count employees for all offices in a single query.
That being done, we can use it inside the view.
Be careful about the JOIN: I believe in your schema, an office may have no employee (for instance, right after you created it or right before you delete it). We will handle that part with a LEFT JOIN.
CREATE VIEW OfficeWithEmployeeCount AS
SELECT Offices.*, EmployeeCount
FROM Offices
LEFT OUTER JOIN (SELECT office_id, COUNT(*) as EmployeeCount FROM Employees GROUP BY office_id) T
ON Offices.office_id = T.office_id
Note: to avoid having NULL returned in EmployeeCount for empty offices, you may want to write:
CREATE VIEW OfficeWithEmployeeCount AS
SELECT Offices.*, COALESCE(EmployeeCount,0)
FROM ...

count max values in postgresql

I have a problem to formulate an sql question in postgresql, hoping to get some help here
I have three tables employee, visitor, and visit. I want to find out which employee (fk_employee_id) who have been responsible for most visit that haven't been checked out.
I want to make an sql question which are returning just the number one result, (by max function maybe?) instead of my current one, which are returning a ranked list (this ranked list doesn't work either if the number one position is shared by two persons)
This is my current sql question:
select visitor.fk_employee_id, count(visitor.fk_employee_id)
From Visit
Inner Join visitor on visit.fk_visitor_id = visitor.visitor_id
WHERE check_out_time IS NULL
group by visitor.fk_employee_id, visitor.fk_employee_id
Limit 1
Anyone now how to do this?
enter image description here
To avoid confusion, I will change the column names to:
visitor table, the FK to employee id : employee_in_charge_id
visit table, the FK to employee id : employee_to_meet_id
From your explanation in comments, you are looking for Employee, who has the most visits which are not check-out .
In the case where, more than 1 employees are having same max number of visits which are not check-out, this query lists all the multiple employees:
SELECT * FROM
(
SELECT
r.employee_in_charge_id,
count(*) cnt,
rank() over (ORDER BY count(*) DESC)
FROM visit v
JOIN visitor r ON v.visitor_id = r.id
WHERE v.check_out_time IS NULL
GROUP BY r.employee_in_charge_id
) a
WHERE rank = 1;
Refer SQLFidle link: http://sqlfiddle.com/#!17/423d9/2
Side Note:
To me, it sounds more correct if employee_in_charge_id is part of visit table, rather than visitor table. My assumption is for each visit, there is 1 employee (A) who is responsible to handle the visit, & the visitor is meeting 1 employee (B). So 1 visitor can make multiple visits, which handle by different employees.
Anyway, my answer above is based on your original schema design.
Assuming a standard n:m implementation like detailed here, this whould be one way to do it:
SELECT fk_employee_id
FROM visit
WHERE check_out_time IS NULL
GROUP BY fk_employee_id
ORDER BY count(*) DESC
LIMIT 1;
Assuming referential integrity, you do not need to include the table visitor in the query at all.
count(*) is a bit faster than count(fk_employee_id) doing the same in this case. (assuming fk_employee_id is NOT NULL). See:
PostgreSQL: running count of rows for a query 'by minute'

need help to write a trigger

I have three tables:
Orders:
orderid, valuedid, valuesdesc
Customers:
customerid, cutomdesc
Groups:
groupid, groupdesc
valueid - id of a customer or a group
valuesdesc - must filled with appropriate for inserted valueid description from Customers or Groups depend on what (customer or group) user selected in the client.
So, when client send an insert query for Orders it consists orderid for new order and valuedid. And on a client side I know what user selected: group or customer.
What I need: if a new row inserted in Orders, corresponding valuesdesc for valuedid from Customers or Groups inserted in valuesdesc column.
I have an idea to insert with new order record valuesdesc which would contain key - fake value to pick the right dictionary (customers or groups), but how to create that trigger I unfortunately don't know for now.
First, I must say this is a very uncommon design. When you are inserting a record into Orders, you use valuesdesc to hint at the table that valueid references. But as soon as valuesdesc gets filled by the trigger, valueid essentially loses any meaning. That is, you haven't reveal us you've got any other way to tell a reference to Customers from a reference to Groups. So, you could just as well drop valueid altogether and use valuesdesc to pass both the 'dictionary' hint and the reference within that table.
Other than that, because valueid can reference more than one table, you cannot use a foreign key constraint on it.
Anyway, in your present design you could try this:
CREATE TRIGGER Orders_UpdateValuesdesc
ON Orders
FOR INSERT
AS
UPDATE o
SET valuesdesc = COALESCE(c.customdesc, g.groupdesc)
FROM Orders o
INNER JOIN inserted ON o.orderid = i.orderid
LEFT JOIN Customers c ON i.valuesdesc = 'Customers'
AND i.valueid = c.customerid
LEFT JOIN Groups g ON i.valuesdesc = 'Groups'
AND i.valueid = g.customerid
The strings 'Customers' and 'Groups' are meant as the dictionary specifiers. You replace them with the actual values.
The trigger uses LEFT JOIN to join both Customers and Groups, then fills Orders.valuesdesc with either customdesc or groupdesc, depending on which one is not null.

T-SQL - How to write query to get records that match ALL records in a many to many join

(I don't think I have titled this question correctly - but I don't know how to describe it)
Here is what I am trying to do:
Let's say I have a Person table that has a PersonID field. And let's say that a Person can belong to many Groups. So there is a Group table with a GroupID field and a GroupMembership table that is a many-to-many join between the two tables and the GroupMembership table has a PersonID field and a GroupID field. So far, it is a simple many to many join.
Given a list of GroupIDs I would like to be able to write a query that returns all of the people that are in ALL of those groups (not any one of those groups). And the query should be able to handle any number of GroupIDs. I would like to avoid dynamic SQL.
Is there some simple way of doing this that I am missing?
Thanks,
Corey
select person_id, count(*) from groupmembership
where group_id in ([your list of group ids])
group by person_id
having count(*) = [size of your list of group ids]
Edited: thank you dotjoe!
Basically you are looking for Persons for whom there is no group he is not a member of, so
select *
from Person p
where not exists (
select 1
from Group g
where not exists (
select 1
from GroupMembership gm
where gm.PersonID = p.ID
and gm.GroupID = g.ID
)
)
You're basically not going to avoid "dynamic" SQL in the sense of dynamically generating the query at query time. There's no way to hand a list around in SQL (well, there is, table variables, but getting them into the system from C# is either impossible (2005 & below) or else annoying (2008)).
One way that you could do it with multiple queries is to insert your list into a work table (probably a process-keyed table) and join against that table. The only other option would be to use a dynamic query such as the ones specified by Jonathan and hongliang.