Select threads from forum based on comments - mysql

I've got the following sql that will return a list of forums. Under each forum it will select the thread with the latest comment. This works fine but when a new thread hasn't got any comments, nothing is returned.
How to tackle this problem?
SELECT f.Id AS forum_id,
f.name AS forum_name,
f.slug AS forum_slug,
f.image AS forum_image,
t.Id AS thread_id,
t.title AS thread_topic,
t.unixtime AS thread_timestamp,
p.Id AS post_id,
p.content AS post_content,
p.unixtime AS post_timestamp,
(SELECT COUNT(*) FROM a_comments o WHERE o.forumID=f.Id AND o.teamId = {$teamId}) comments_count,
(SELECT COUNT(*) FROM a_threads w WHERE w.forumID=f.Id AND w.teamId = {$teamId}) threads_count
FROM a_forums f
LEFT JOIN (SELECT t2.forumID, max(COALESCE(p2.unixtime, t2.unixtime)) as ts, COUNT(p2.unixtime) as post_count
FROM a_threads t2
LEFT JOIN a_comments p2 ON p2.threadId = t2.id
GROUP BY t2.forumId) max_p ON f.id = max_p.forumId
LEFT JOIN a_comments p ON max_p.ts = p.unixtime AND p.teamId = {$teamId} AND p.deleted = 0
LEFT JOIN a_threads t ON f.Id = t.forumID AND (max_p.post_count = 0 OR p.threadId = t.ID) AND t.teamId = {$teamId} AND t.deleted = 0
ORDER BY f.id

I think you just have to change the LEFT JOIN in the first subquery to a JOIN. With the LEFT JOIN, you'll get NULL or a non-valid time for the comment. This then throws off the rest of the logic -- I think.
SELECT f.Id AS forum_id, f.name AS forum_name, f.slug AS forum_slug, f.image AS forum_image,
t.Id AS thread_id, t.title AS thread_topic, t.unixtime AS thread_timestamp,
p.Id AS post_id, p.content AS post_content, p.unixtime AS post_timestamp,
(SELECT COUNT(*) FROM a_comments o WHERE o.forumID=f.Id AND o.teamId = {$teamId}) as comments_count,
(SELECT COUNT(*) FROM a_threads w WHERE w.forumID=f.Id AND w.teamId = {$teamId}) as threads_count
FROM a_forums f LEFT JOIN
(SELECT t2.forumID, max(p2.unixtime) as ts,
COUNT(p2.unixtime) as post_count
FROM a_threads t2 JOIN
a_comments p2
ON p2.threadId = t2.id
GROUP BY t2.forumId
) max_p
ON f.id = max_p.forumId LEFT JOIN
a_comments p
ON max_p.ts = p.unixtime AND p.teamId = {$teamId} AND
p.deleted = 0 LEFT JOIN
a_threads t
ON f.Id = t.forumID AND (max_p.post_count = 0 OR p.threadId = t.ID) AND t.teamId = {$teamId} AND t.deleted = 0
ORDER BY f.id

Related

Transpile mysql query to laravel style

How would I transpile this query to Laravel style
SELECT
gr.name,
p.ma_status AS status,
COUNT(p.ma_status) AS total
FROM accounts u
LEFT JOIN accounts_prop p ON p.account_id = u.account_id
AND (
SELECT j.iid
FROM accounts_prop AS j
WHERE u.account_id = j.account_id
AND j.ma_status IS NOT NULL
ORDER BY j.von DESC LIMIT 1
) = p.iid
LEFT JOIN `deprecators` gr ON gr.id = p.group_id
LEFT JOIN `deprecators` unit ON unit.id = p.unit_id
LEFT JOIN `deprecators` team ON team.id = p.team_id
WHERE p.group_id IS NOT NULL
AND u.account_status = 'A'
GROUP BY p.ma_status, gr.id
I do not have any idea how would I go with the following statement
LEFT JOIN accounts_prop p ON p.account_id = u.account_id
AND (
SELECT j.iid
FROM accounts_prop AS j
WHERE u.account_id = j.account_id
AND j.ma_status IS NOT NULL
ORDER BY j.von DESC LIMIT 1
) = p.iid

Is a way to optimize big mysql query?

This is my query and it's to slow... I'm looking for the way to optimize it.
SELECT p.id,
Group_concat(pc.cat_id) AS groups,
p.code,
p.NAME,
p.price,
p.thumbnail,
p.image,
mc.queries AS merch_queries,
mc.position AS merch_position,
Group_concat(op.image) AS option_images,
cf_RETAIL.value AS custom_RETAIL,
cf_rating.value AS custom_rating,
cf_reviews.value AS custom_reviews,
cf_sku.value AS custom_sku,
cf_brand.value AS custom_brand,
cf_custom_thumbnail.value AS custom_custom_thumbnail
FROM s01_products AS p
LEFT JOIN s01_categoryxproduct AS pc
ON p.id = pc.product_id
LEFT JOIN (SELECT pv.product_id,
pv.value
FROM s01_cfm_prodfields AS pf
INNER JOIN s01_cfm_prodvalues AS pv
ON pf.id = pv.field_id
WHERE pf.code = 'RETAIL') AS cf_RETAIL
ON p.id = cf_RETAIL.product_id
LEFT JOIN (SELECT pv.product_id,
pv.value
FROM s01_cfm_prodfields AS pf
INNER JOIN s01_cfm_prodvalues AS pv
ON pf.id = pv.field_id
WHERE pf.code = 'rating') AS cf_rating
ON p.id = cf_rating.product_id
LEFT JOIN (SELECT pv.product_id,
pv.value
FROM s01_cfm_prodfields AS pf
INNER JOIN s01_cfm_prodvalues AS pv
ON pf.id = pv.field_id
WHERE pf.code = 'reviews') AS cf_reviews
ON p.id = cf_reviews.product_id
LEFT JOIN (SELECT pv.product_id,
pv.value
FROM s01_cfm_prodfields AS pf
INNER JOIN s01_cfm_prodvalues AS pv
ON pf.id = pv.field_id
WHERE pf.code = 'sku') AS cf_sku
ON p.id = cf_sku.product_id
LEFT JOIN (SELECT pv.product_id,
pv.value
FROM s01_cfm_prodfields AS pf
INNER JOIN s01_cfm_prodvalues AS pv
ON pf.id = pv.field_id
WHERE pf.code = 'brand') AS cf_brand
ON p.id = cf_brand.product_id
LEFT JOIN (SELECT pv.product_id,
pv.value
FROM s01_cfm_prodfields AS pf
INNER JOIN s01_cfm_prodvalues AS pv
ON pf.id = pv.field_id
WHERE pf.code = 'custom_thumbnail') AS cf_custom_thumbnail
ON p.id = cf_custom_thumbnail.product_id
LEFT JOIN (SELECT p.product_id AS product_id,
Group_concat(q.query) AS queries,
Min(p.position) AS position
FROM s01_srch_merchandisingproduct AS p
LEFT JOIN s01_srch_merchandisingquery AS q
ON q.id = p.query_id
GROUP BY p.product_id) AS mc
ON p.id = mc.product_id
LEFT JOIN s01_options AS op
ON p.id = op.product_id
AND op.image <> ''
WHERE p.active = 1
GROUP BY p.id
Thanks for any help!
Updated Tables Schema:
**s01_categoryxproduct**
cat_id,
product_id,
disp_order
**s01_products**
id
catcount
agrpcount
pgrpcount
disp_order
code
name
thumbnail
image
price
cost
descrip
weight
taxable
active
sku
cancat_id
page_id
page_title
dt_created
dt_updated
**s01_CFM_ProdValues**
field_id,
product_id,
value,
value_long
**s01_CFM_ProdFields**
id,
code,
name,
group_id,
fieldtype,
info,
facet
**s01_Options**
id,
product_id,
attr_id,
disp_order,
code,
prompt,
price,
cost,
weight,
image
**s01_SRCH_MerchandisingProduct**
id,
product_id,
query_id,
position
**s01_SRCH_MerchandisingQuery**
id,
query
"Over-normalization" and EAV are the problems.
Put prodfields and prodvalues in the same table. Splitting them into two tables leads to performance-eating overhead.
More on optimizing an EAV table: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta
More discussion of why EAV is bad: http://mysql.rjweb.org/doc.php/eav
SELECT p.id,
Group_concat(pc.cat_id) AS groups,
p.code,
p.NAME,
p.price,
p.thumbnail,
p.image,
mc.queries AS merch_queries,
mc.position AS merch_position,
Group_concat(op.image) AS option_images,
IF(pf.code = 'RETAIL',pv.value , NULL) AS custom_RETAIL,
IF(pf.code = 'rating',pv.value , NULL) AS custom_rating,
IF(pf.code = 'reviews',pv.value , NULL) AS custom_reviews,
IF(pf.code = 'brand',pv.value , NULL) AS custom_brand,
IF(pf.code = 'custom_thumbnail',pv.value , NULL) AS custom_custom_thumbnail,
pvr.value AS custom_rating,
FROM s01_products AS p
LEFT JOIN s01_categoryxproduct AS pc ON p.id = pc.product_id
LEFT JOIN s01_cfm_prodfields AS cf_RETAIL ON p.id = cf_RETAIL.product_id
LEFT JOIN s01_cfm_prodvalues AS pv ON cf_RETAIL.id = pv.field_id
LEFT JOIN (SELECT p.product_id AS product_id,
Group_concat(q.query) AS queries,
Min(p.position) AS position
FROM s01_srch_merchandisingproduct AS p
LEFT JOIN s01_srch_merchandisingquery AS q
ON q.id = p.query_id
GROUP BY p.product_id) AS mc
ON p.id = mc.product_id
LEFT JOIN s01_options AS op
ON p.id = op.product_id
AND op.image <> ''
WHERE p.active = 1
GROUP BY p.id`;

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

Mysql left join 4 possible values

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

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