Mysql left join 4 possible values - mysql

we're running this query in moodle database:
SELECT l.*,
MAX(l.time) as time,cat.id as catid, cat.name as catname,c.id as courseid,
c.fullname as coursename,
m.name as module,
r.revision as revision,
r.name as resourcename,
sco.name as scormname,
glo.name as glossaryname,
qui.name as quizname
FROM {log} l
INNER JOIN {user} u ON l.userid = u.id
INNER JOIN {course} c ON l.course = c.id
INNER JOIN {course_categories} cat ON c.category = cat.id
INNER JOIN {course_modules} cm ON l.cmid = cm.id
INNER JOIN {modules} m ON cm.module = m.id
LEFT JOIN {resource} r ON cm.instance = r.id and l.module = "resource"
LEFT JOIN {scorm} sco ON cm.instance = sco.id and l.module = "scorm"
LEFT JOIN {glossary} glo ON cm.instance = glo.id and l.module = "glossary"
LEFT JOIN {quiz} qui ON cm.instance = qui.id and l.module = "quiz"
WHERE LOWER(l.action) LIKE LOWER(:modaction) COLLATE utf8_bin AND l.userid = :userid
GROUP BY l.cmid
ORDER BY MAX(l.time) DESC
and i would like something like this:
SELECT l.*,
MAX(l.time) as time,cat.id as catid, cat.name as catname,c.id as courseid,
c.fullname as coursename,
m.name as module,
r.revision as revision,
r.name as resourcename
FROM {log} l
INNER JOIN {user} u ON l.userid = u.id
INNER JOIN {course} c ON l.course = c.id
INNER JOIN {course_categories} cat ON c.category = cat.id
INNER JOIN {course_modules} cm ON l.cmid = cm.id
INNER JOIN {modules} m ON cm.module = m.id
INNER JOIN (
SELECT * {resource} re WHERE cm.instance = r.id OR
SELECT * {scorm} sco WHERE cm.instance = sco.id OR
SELECT * {glossary} WHERE ON cm.instance = glo.id OR
SELECT * {quiz} qui WHERE cm.instance = qui.id OR
) as r
WHERE LOWER(l.action) LIKE LOWER(:modaction) COLLATE utf8_bin AND l.userid = :userid
GROUP BY l.cmid
ORDER BY MAX(l.time) DESC
Results return NULL values in "resource" names (resourcename, scormname, glossaryname or quizname) and only one valid "resource" name. I render results depending on if result is not null.
But i would like to avoid "filter" by not null result in resourcename, scormname, glossaryname and quizname using PHP.
Thanks!

Something like this maybe - use a union
SELECT l.cmid,
MAX(l.time) as logtime,
cat.id as catid,
cat.name as catname,
c.id as courseid,
c.fullname as coursename,
mods.module,
mods.revision,
mods.name as modname
FROM mdl_log l
JOIN mdl_user u ON l.userid = u.id
JOIN mdl_course c ON l.course = c.id
JOIN mdl_course_categories cat ON c.category = cat.id
JOIN (
SELECT cm.id AS cmid, m.name AS module, r.name, r.revision
FROM mdl_course_modules cm
JOIN mdl_modules m ON cm.module = m.id AND m.name = 'resource'
JOIN mdl_resource r ON r.id = cm.instance
UNION
SELECT cm.id AS cmid, m.name as modulentype, s.name, NULL AS revision
FROM mdl_course_modules cm
JOIN mdl_modules m ON cm.module = m.id AND m.name = 'scorm'
JOIN mdl_scorm s ON s.id = cm.instance
UNION
SELECT cm.id AS cmid, m.name as modulentype, g.name, NULL AS revision
FROM mdl_course_modules cm
JOIN mdl_modules m ON cm.module = m.id AND m.name = 'glossary'
JOIN mdl_glossary g ON g.id = cm.instance
UNION
SELECT cm.id AS cmid, m.name as modulentype, q.name, NULL AS revision
FROM mdl_course_modules cm
JOIN mdl_modules m ON cm.module = m.id AND m.name = 'quiz'
JOIN mdl_quiz q ON q.id = cm.instance
) mods ON mods.cmid = l.cmid AND mods.module = l.module
WHERE l.module IN ('resource', 'scorm', 'glossary', 'quiz')
GROUP BY l.cmid
ORDER BY 1 DESC

Related

Moodle: Combining the Two SQL for custom user profile fields

Please help me to combine this two SQL statement so that it will in the $sql and will be interconnected. The final output is in the $sql and I can use member_status as field. Thanks
SELECT UID.data FROM prefix_user_info_field UIF,
prefix_user_info_data UID WHERE UID.fieldid = UIF.id AND
UIF.shortname = 'member_status' AND UID.userid = U.id) as 'member_status'";
$sql = "SELECT ci.id, u.id as userid, $userfields, co.id as courseid,
co.fullname as coursefullname, c.id as certificateid,
c.name as certificatename, c.verifyany
FROM {customcert} c
JOIN {customcert_issues} ci
ON c.id = ci.customcertid
JOIN {course} co
ON c.course = co.id
JOIN {user} u
ON ci.userid = u.id
WHERE ci.code = :code";
$sql = "SELECT ci.id, u.id as userid, $userfields, co.id as courseid,
co.fullname as coursefullname, c.id as certificateid,
c.name as certificatename, c.verifyany,
d.data AS memberstatus
FROM {customcert} c
JOIN {customcert_issues} ci ON c.id = ci.customcertid
JOIN {course} co ON c.course = co.id
JOIN {user} u ON ci.userid = u.id
LEFT JOIN (
SELECT d.userid, d.data
FROM {user_info_data} d
JOIN {user_info_field} f ON f.id = d.fieldid AND f.shortname = 'member_status'
) d ON d.userid = u.id
WHERE ci.code = :code";

sql error using subquery in select and from clause

I get error When I use subquery in select and from clause
Error: https://www.awesomescreenshot.com/image/3069216/0505cad495528e9f9af2281ea281415c.
Here what I have done so far:
SELECT m.id, m.title_name, m.release_date, m.plot, m.poster,
(SELECT count(*) FROM users_ratings WHERE type_id = m.id AND type_name = 'movies' LIMIT 1) count_user_rating
FROM (SELECT u.* FROM users u
LEFT JOIN users_ratings ur ON ur.user_id = u.id
WHERE ur.type_id = 2
AND ur.type_name = 'movies') u
LEFT JOIN users_ratings ur ON ur.user_id = u.id
LEFT JOIN movies m ON m.id = ur.type_id
WHERE ur.type_name = 'movies'
UNION
SELECT s.id, s.title_name, s.first_air_date release_date, s.plot, s.poster,
(SELECT count(*) FROM users_ratings WHERE type_id = s.id AND type_name = 'series' LIMIT 1) count_user_rating
FROM (SELECT u.* FROM users u
LEFT JOIN users_ratings ur ON ur.user_id = u.id
WHERE ur.type_id = 2
AND ur.type_name = 'movies') u
LEFT JOIN users_ratings ur ON ur.user_id = u.id
LEFT JOIN series s ON s.id = ur.type_id
WHERE ur.type_name = 'series'
Not sure whats wrong in your query, seems fine to me. But here is another way to achieve it. Try this
SELECT m.id,
m.title_name,
m.release_date,
m.plot,
m.poster,
t.count_user_rating
FROM (SELECT u.*
FROM users u
LEFT JOIN users_ratings ur
ON ur.user_id = u.id
WHERE ur.type_id = 2
AND ur.type_name = 'movies') u
LEFT JOIN users_ratings ur
ON ur.user_id = u.id
LEFT JOIN movies m
ON m.id = ur.type_id
LEFT JOIN (SELECT type_id,
Count(*) AS count_user_rating
FROM users_ratings
WHERE NAME = 'movies'
GROUP BY type_id) t
ON t.type_id = m.id
WHERE ur.type_name = 'movies'
UNION
SELECT s.id,
s.title_name,
s.first_air_date release_date,
s.plot,
s.poster,
t.count_user_rating
FROM (SELECT u.*
FROM users u
LEFT JOIN users_ratings ur
ON ur.user_id = u.id
WHERE ur.type_id = 2
AND ur.type_name = 'movies') u
LEFT JOIN users_ratings ur
ON ur.user_id = u.id
LEFT JOIN series s
ON s.id = ur.type_id
LEFT JOIN (SELECT type_id,
Count(*) AS count_user_rating
FROM users_ratings
WHERE NAME = 'series'
GROUP BY type_id) t
ON t.type_id = s.id
WHERE ur.type_name = 'series'

MYSQL combining two counts that use same columns

I have two queries that i need to combine into one query. The problem is they are using the same columns in where clause depending on what i need to fetch.
Query 1
SELECT c.fullname, COUNT( DISTINCT sst.id ) AS 'Liczba rozpoczetych szkolen'
FROM mdl_course c
INNER JOIN mdl_scorm s ON s.course = c.id
INNER JOIN mdl_scorm_scoes_track sst ON s.id = sst.scormid
INNER JOIN mdl_user u ON u.id = sst.userid
WHERE sst.element = 'x.start.time' AND u.deleted =0
GROUP BY c.fullname ORDER BY `Liczba rozpoczetych szkolen` ASC
Query 2
SELECT c.fullname, COUNT(DISTINCT sst.userid ) AS 'Liczba_ukonczonych_szkolen'
FROM mdl_course c
INNER JOIN mdl_scorm s ON s.course=c.id
INNER JOIN mdl_scorm_scoes_track sst ON s.id = sst.scormid
INNER JOIN mdl_user u ON sst.userid=u.id
where `element`='cmi.core.score.raw' and `value` = 100 and u.deleted = 0
GROUP BY c.fullname ORDER BY `Liczba_ukonczonych_szkolen` DESC
They are depending on the same column named 'element'.
How i can display the result as
fullname Liczba rozpoczetych szkolen Liczba_ukonczonych_szkolen
A1 34 4
A2 5 3
A3 34 33
I've came up with this one, whitch works. Thanks for advice #HoneyBadger
SELECT t1.fullname, t1.Liczba_rozpoczetych_szkolen, t2.Liczba_ukonczonych_szkolen,
round(((t2.Liczba_ukonczonych_szkolen /t1.Liczba_rozpoczetych_szkolen)*100),2) as procentowo
FROM
(SELECT c.fullname, COUNT( DISTINCT sst.id ) AS Liczba_rozpoczetych_szkolen
FROM mdl_course c
INNER JOIN mdl_scorm s ON s.course = c.id
INNER JOIN mdl_scorm_scoes_track sst ON s.id = sst.scormid
INNER JOIN mdl_user u ON u.id = sst.userid
WHERE sst.element = 'x.start.time' AND u.deleted =0
GROUP BY c.fullname ) AS t1,
(SELECT c.fullname, COUNT(DISTINCT sst.userid ) AS Liczba_ukonczonych_szkolen
FROM mdl_course c
INNER JOIN mdl_scorm s ON s.course=c.id
INNER JOIN mdl_scorm_scoes_track sst ON s.id = sst.scormid
left join mdl_user u ON sst.userid=u.id
where `element`='cmi.core.score.raw' and `value` = 100 and u.deleted = 0
GROUP BY c.fullname) as t2
WHERE t1.fullname = t2.fullname
You can join them together like this:
SELECT COALESCE(R.fullname, U.fullname) AS fullname
, COALESCE(R.Liczba_rozpoczetych_szkolen, 0) AS Liczba_rozpoczetych_szkolen
, COALESCE(R.Liczba_ukonczonych_szkolen, 0) AS Liczba_ukonczonych_szkolen
FROM (
SELECT c.fullname
, COUNT( DISTINCT sst.id ) AS Liczba_rozpoczetych_szkolen
FROM mdl_course c
INNER JOIN mdl_scorm s
ON s.course = c.id
INNER JOIN mdl_scorm_scoes_track sst
ON s.id = sst.scormid
INNER JOIN mdl_user u
ON u.id = sst.userid
WHERE sst.element = 'x.start.time'
AND u.deleted =0
GROUP BY c.fullname
) AS R
FULL JOIN (
SELECT c.fullname
, COUNT(DISTINCT sst.userid ) AS Liczba_ukonczonych_szkolen
FROM mdl_course c
INNER JOIN mdl_scorm s
ON s.course=c.id
INNER JOIN mdl_scorm_scoes_track sst
ON s.id = sst.scormid
INNER JOIN mdl_user u
ON sst.userid=u.id
where `element`='cmi.core.score.raw'
AND `value` = 100
AND u.deleted = 0
GROUP BY c.fullname
) AS U
ON R.fullname = U.fullname
Try the query with the ORed predicates and extra filtering with CASE in the SELECT list. Kind of
SELECT c.fullname, COUNT(DISTINCT CASE WHEN sst.element = 'x.start.time' THEN sst.id END) AS 'Liczba rozpoczetych szkolen'
, COUNT(DISTINCT CASE WHEN sst.element = 'cmi.core.score.raw' and `value` = 100 THEN sst.userid END) AS 'Liczba_ukonczonych_szkolen'
FROM mdl_course c
INNER JOIN mdl_scorm s ON s.course = c.id
INNER JOIN mdl_scorm_scoes_track sst ON s.id = sst.scormid
INNER JOIN mdl_user u ON u.id = sst.userid
WHERE sst.element = IN( 'x.start.time', 'cmi.core.score.raw') AND u.deleted =0
GROUP BY c.fullname ORDER BY `Liczba rozpoczetych szkolen` ASC

Convert SQL DATEFORMAT to Postgres

Could someone please change the following query to work on postgres?
SELECT
user2.id AS ID,
ul.timeaccess,
user2.firstname AS Firstname,
user2.lastname AS Lastname,
user2.email AS Email,
user2.username AS IDNumber,
user2.institution AS Institution,
IF (user2.lastaccess = 0,'never',
DATE_FORMAT(FROM_UNIXTIME(user2.lastaccess),'%Y-%m-%d')) AS dLastAccess
,(SELECT DATE_FORMAT(FROM_UNIXTIME(timeaccess),'%Y-%m-%d') FROM mdl_user_lastaccess WHERE userid=user2.id AND courseid=c.id) AS CourseLastAccess
,(SELECT r.name
FROM mdl_user_enrolments AS uenrol
JOIN mdl_enrol AS e ON e.id = uenrol.enrolid
JOIN mdl_role AS r ON e.id = r.id
WHERE uenrol.userid=user2.id AND e.courseid = c.id) AS RoleName
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 user2 ON user2 .id = ue.userid
LEFT JOIN mdl_user_lastaccess AS ul ON ul.userid = user2.id
WHERE c.id=14 AND ue.userid NOT IN (SELECT qa.userid FROM mdl_quiz_attempts AS qa
JOIN mdl_quiz AS q ON qa.quiz = q.id
JOIN mdl_course AS c ON q.course = c.id
WHERE c.id = 14 AND q.name LIKE '%quiz name goes here%')
It is for Moodle
i understand that it has to go to_char etc but don't know how to change it
Thanks in advance
SELECT
user2.id AS "ID",
ul.timeaccess,
user2.firstname AS "Firstname",
user2.lastname AS "Lastname",
user2.email AS "Email",
user2.username AS "IDNumber",
user2.institution AS "Institution",
(CASE WHEN user2.lastaccess = 0 THEN'never' END ||
to_char(to_timestamp(user2.lastaccess)::date,'YYYY-MM-DD')) AS "dLastAccess"
,(SELECT to_char(to_timestamp(timeaccess)::date,'YYYY-MM-DD') FROM mdl_user_lastaccess WHERE userid=user2.id AND courseid=c.id) AS "CourseLastAccess"
,(SELECT r.name
FROM mdl_user_enrolments AS uenrol
JOIN mdl_enrol AS e ON e.id = uenrol.enrolid
JOIN mdl_role AS r ON e.id = r.id
WHERE uenrol.userid=user2.id AND e.courseid = c.id) AS "RoleName"
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 user2 ON user2 .id = ue.userid
LEFT JOIN mdl_user_lastaccess AS ul ON ul.userid = user2.id
WHERE c.id=14 AND ue.userid NOT IN (SELECT qa.userid FROM mdl_quiz_attempts AS qa
JOIN mdl_quiz AS q ON qa.quiz = q.id
JOIN mdl_course AS c ON q.course = c.id
WHERE c.id = 14 AND q.name LIKE '%quiz name goes here%')
Hey I tried.
FROM_UNIXTIME -is equivalent to to_timestamp on postgres.
IF ELSE are CASE WHEN in postgres.
to_char - if you want to format the date use this.
Have a look at: Data Type Formatting Functions on Postgres docs.
Use to_char(date, 'YYYY-MM-DD')
For example:
select to_char('20170501'::date, 'YYYY-MM-DD')
Returns:
2017-05-01
Check it here: http://rextester.com/JDZA16474

MYSQL union with many JOIN

I'm trying to write a query to fetch a listing from 2 (list1,list2)tables with the same columns.
Are there any other way to rewrite this code?
(SELECT r.id as rid, s.title, u.username
FROM list1 r
JOIN drama s ON r.parent_id = s.id
LEFT JOIN image i ON s.image_id = i.id
LEFT JOIN user u ON r.user_id = u.user_id)
UNION ALL
(SELECT r.id as rid, s.title, u.username
FROM list2 r
JOIN movie s ON r.parent_id = s.id
LEFT JOIN image i ON s.image_id = i.id
LEFT JOIN user u ON r.user_id = u.user_id)
ORDER BY rid LIMIT 10
whats wrong if you do this
SELECT r.id as rid, m.title AS movie Title,
d.title as DramaTitle, u.username
FROM list1 r
INNER JOIN movie m ON r.parent_id = m.id
INNER JOIN drama d ON r.parent_id = d.id
LEFT JOIN image i ON s.image_id = i.id
LEFT JOIN user u ON r.user_id = u.user_id
ORDER BY r.id LIMIT 10
SELECT r.id as rid, s.title, u.username
FROM (SELECT l1.id, l1.user_id, l1.parent_id FROM list1 l1
UNION ALL
SELECT l2.id, l2.user_id, l2.parent_id FROM list2 l2) r
INNER JOIN drama s ON r.parent_id = s.id
LEFT JOIN image i ON s.image_id = i.id
LEFT JOIN user u ON r.user_id = u.user_id
ORDER BY rid
LIMIT 10