About a specific PHP&MySQLCode Performance Logic Helping - mysql

I use Laravel 6 for one of my app. I am stuck in a performance problem for post page in my app.
The most popular page is post page in my app. Typically the post page has:
PostPage content,
Comments of post content and subComments (as tree) for all comment,
and comment liking, rating (Is the member liked this comment) again for all comment
E.g: if the page has +10 comments, the app will be, slowly and mysqld high.
An example for SQL Query in my code block:
SELECT `comments`.`id`, `comments`.`comment`, `comments`.`parent_id`, `comments`.`date`,
`comments`.`ratingN`, `comments`.`ratingP`, `rating`.`rate`, `users`.`photo`,
`users`.`photo2`, `users`.`username`, `users`.`gold_end`,
(CASE WHEN rating.id_user = 7 THEN 1 ELSE 0 END) as liked
FROM `comments`
inner join `users` on `comments`.`id_user` = `users`.`id`
left join `rating` on `rating`.`id_comment` = `comments`.`id`
where `parent_id` = 284162 and `status` = 1
group by `comments`.`date` order by `date` asc
I use (query) cache but again the first opening and after any update the post page works slowly because of comments side. Using joins.
I call the main comments and than, look that mainComments have any subComments,
The comments are liked by this member?
PHP 7.2, MySQL 5.7
What are your suggestions?

These composite indexes might help:
comments: INDEX(parent_id, status, date)
rating: INDEX(id_comment, id_user, rate)
users: I assume you have `PRIMARY KEY(Id)`?

Related

MySQL Left outer join on Knex not returning correct data

I'm troubleshooting some errors encountered in Knex, and am trying to do a left join with 2 tables, namely, notifications and metadata, with both these tables having the same 2 columns, 'device_id' and 'channel' that I would like to match. However, the below query string doesn't work and returns the following even though there is a metadata record (metadata_id=1) with matching device_id and channel.
I checked that the datatypes are also the same for device_id and channel in both tables. Been stuck for some time and not sure what is wrong here, would be great if someone can help! Also having some problems with translating to Knex for nested queries, but this is probably a small problem.
{
notification_id: 1,
message: 'hello world',
mode: 'email',
metadata_id: null,
unit_conversion: null
}
SELECT `notifications`.`notification_id`, `notifications`.`message`, `notifications`.`mode`,
`metadata`.`metadata_id`, `metadata`.`unit_conversion` from `notifications`
LEFT OUTER JOIN `metadata` ON (`metadata`.`device_id` = `notifications`.`device_id` AND
`metadata`.`channel` = `notifications`.`channel` AND `metadata`.`deleted_at` = null )
WHERE `notifications`.`notification_id` = 1
metadata.deleted_at = null should be replaced with metadata.deleted_at is null, with the excellent explanation by Bohemian here: https://stackoverflow.com/a/9581790/6597774

Duplicate entries from database

ON closer inspection the problem is solved if I remove the comment table join, but obviously this isn't ideal help appreciated!!
I'm trying to write a blog with Codeigniter. At the moment i'm displaying all the information that I want to display, but for some reason, when viewing the blog, I've got duplicate content for the categories. There are only 3 test categories in my database but 1 of my test posts that has all 3 selected is showing six and the other is showing just the one category twice.
Even stranger than that, the first blog post is also counting the wrong number of comments. It's displaying 6 when there are only 4 in the database and only two are related to that post, but the strangest of all is that the second post displays the correct number of comments, two. How can that be.
This is the query from the model in question.
$this->db->select('posts.id,
posts.title,
posts.slug,
posts.content,
posts.author,
posts.date,
posts.time,
posts.tags,
posts.status,
GROUP_CONCAT(categories.name SEPARATOR \'-\') AS categories,
count(comments.id) as total_comments
');
$this->db->group_by(array('posts.id'));
$this->db->join('posts_categories', 'posts_categories.blog_entry_id = posts.id', 'left outer', 'left outer');
$this->db->join('categories', 'posts_categories.blog_category_id = categories.category_id', 'left outer');
$this->db->join('comments', 'comments.post_id = posts.id', 'left outer' );
$query = $this->db->get('posts', $config['per_page'], $this->uri->segment(3));
The standard query generated is this:
SELECT DISTINCT `posts`.`id`,
`posts`.`title`, `posts`.`slug`,
`posts`.`content`, `posts`.`author`,
`posts`.`date`, `posts`.`time`,
`posts`.`tags`, `posts`.`status`,
GROUP_CONCAT(categories.name SEPARATOR '-') AS categories,
count(comments.id) as total_comments
FROM (`posts`)
LEFT OUTER JOIN `posts_categories` ON `posts_categories`.`blog_entry_id` = `posts`.`id`
LEFT JOIN `categories` ON `posts_categories`.`blog_category_id` = `categories`.`category_id`
LEFT JOIN `comments` ON `comments`.`post_id` = `posts`.`id`
GROUP BY `posts`.`id` LIMIT 1
The database dump can be found here
Any help would be greatly appreciated, this is making me crazy!
Cheers.
Do you have any qualms running another query to get the comment count? You can do the following query (assuming Active Record):
$this->db->where('parent_id', $post_id);
$this->db->count_all_results('comments');
This would have to be run for each post, though, so your # of queries would be n+1 where n = number of posts.

mysql find only unique records in a subquery and show the count

i have two tables i am trying to get information from.
login table - which has the list of employees
projects table - which has the projects
in short, i am trying to write a query that will select the copywriters and perform a subquery on each that will return a field dubbed 'open_projects'. This, i can get to work with the below sql:
select web_login_id,
(select count(project_web_id) from project
where copywriter = web_login_id
and (`status` = 'open' or `status` = 'qual')) as open_projects from login
where roles like '%copywriter%'
and tierLevel like '%c1%'
order by open_projects asc
This returns something like:
1982983 3
1982690 22
2987398 5
The problem with this is that sometimes 5 or 6 of the projects will belong to the same client and are not actually being worked on as they are dealt with in a queue-ish fashion.
My question is how to modify the above sql so that the subquery will GROUP subset based on the client_login_id field.
This sql gives me an error of : subquery returns more than 1 row
select web_login_id,
(select count(project_web_id) from project
where copywriter = web_login_id
and (`status` = 'open' or `status` = 'qual') group by client_login_id) as open_projects from login
where roles like '%copywriter%'
and tierLevel like '%c1%'
order by open_projects asc
You need to rephrase this as an explicit join. I think the following does the trick:
select web_login_id, cw.open_projects
from login l left outer join
(select copywriter, count(project_web_id) as open_projects
from project
where `status` in ('open', 'qual')
group by copywriter
) cw
on l.web_login_id = cw.copywriter
where l.roles like '%copywriter%' and l.tierLevel like '%c1%'
order by open_projects asc
I'm not sure what the "group by client_login_id" is doing. It doesn't seem necessary.
Once you've done this, you can return as many columns as you like from the subquery.

MediaWiki 1.16.0 - In phpmyadmin select the current articles

I'm trying to get all articles that are the current/latest articles in Mediawiki 1.16.0. I need to do this in phpMyadmin and make a dump from those results.
my SQL:
SELECT
page.page_title, page.page_latest
, revision.rev_id, revision.rev_text_id
, text.old_id, text.old_text
FROM page, revision, text
WHERE rev_id = page_latest AND rev_text_id = old_id
I get the image names also but not a problem. I feel that this SQL above is not getting the latest version of the articles.
If there is a way to not show image names and redirects in the results it would also help.
First of all please don't use that ugly implicit join syntax. It's confusing and error-prone.
Change it to this:
SELECT
page.page_title, page.page_latest
, revision.rev_id, revision.rev_text_id
, text.old_id, text.old_text
FROM page
INNER JOIN revision ON (rev_id = page_latest)
INNER JOIN text ON (rev_text_id = old_id)
Now you can see why: it's getting all pages. There is no where clause, there are just join clauses.
This is the DB layout: http://upload.wikimedia.org/wikipedia/commons/b/b7/MediaWiki_database_schema_1-17_%28r82044%29.png
And here are the description of the fields in the various tables:
http://www.mediawiki.org/wiki/Manual:Database_layout
Revised query
SELECT
p.page_title, p.page_latest
, MAX(revision.rev_id) as rev_id, revision.rev_text_id
, text.old_id, text.old_text
FROM page p
INNER JOIN revision r ON (r.rev_id = p.page_latest)
INNER JOIN `text` t ON (r.rev_text_id = t.old_id)
WHERE p.page_is_redirect = 0 AND p.page_namespace <> 6 /*NS_IMAGE = 6*/
GROUP BY p.page_latest
ORDER BY p.page_title
This filters out the redirects and excludes the pages where namespace = ns_image.
I'm not 100% sure though 'cause I don't have MediaWiki to test.

SQL Query returns fake duplicate results?

I've been trying to write a few little plugins for personal use with WHMCS. Essentially what I'm trying to do here is grab a bunch of information about a certain order(s), and return it as an array in Perl.
The Perl bit I'm fine with, it's the MySQL query I've formed that's giving me stress..
I know it's big and messy, but what I have is:
SELECT tblhosting.id, tblhosting.userid, tblhosting.orderid, tblhosting.packageid, tblhosting.server, tblhosting.domain, tblhosting.username, tblorders.invoiceid, tblproducts.gid, tblservers.ipaddress, tblinvoices.status
FROM tblhosting, tblproducts, tblorders, tblinvoices, tblservers
WHERE tblorders.status = 'Pending'
AND tblproducts.gid = '2'
AND tblservers.id = tblhosting.server
AND tblorders.id = tblhosting.orderid
AND tblinvoices.id = tblorders.invoiceid
AND tblinvoices.status = 'Paid'
I don't know if this /should/ work, but I assume I'm on the right track as it does return what I'm looking for, however it returns everything twice.
For example, I created a new account with the domain 'sunshineee.info', and then in PHPMyAdmin ran the above query.
id userid orderid packageid server domain username invoiceid gid ipaddress status
13 7 17 6 1 sunshineee.info sunshine 293 2 184.22.145.196 Paid
13 7 17 6 1 sunshineee.info sunshine 293 2 184.22.145.196 Paid
Could anyone give me a heads up on where I've gone wrong with this one.. Obvioiusly (maybe not obviously enough) I want this as only one row returned per match.. I've tried it with >1 domain in the database and it returned duplicates for each of the matches..
Any help would be much appreciated
:)
SELECT distinct tblhosting.id, tblhosting.userid, tblhosting.orderid, tblhosting.packageid, tblhosting.server, tblhosting.domain, tblhosting.username, tblorders.invoiceid, tblproducts.gid, tblservers.ipaddress, tblinvoices.status
FROM tblhosting, tblproducts, tblorders, tblinvoices, tblservers
WHERE tblorders.status = 'Pending'
AND tblproducts.gid = '2'
AND tblservers.id = tblhosting.server
AND tblorders.id = tblhosting.orderid
AND tblinvoices.id = tblorders.invoiceid
AND tblinvoices.status = 'Paid'
Well, its near impossible without any table definitions, but you are doing a lot of joins there. You are starting with tblhosting.id and working your way 'up' from there. If any of the connected tables has a double entry, you'll get more hits
You could add a DISTINCT to your query, but that would not fix the underlying issue. It could be a problem with your data: do you have 2 invoices? Maybe you should select everything (SELECT * FROM) and check what is returned, maybe check your tables for double content.
Using DISTINCT is most of the time not a good choice: it means either your query or your data is incorrect (or you don't understand them thoroughly). It might get you the right result for now, but can get you in trouble later.
A guess about the reason this happens:
You do not connect the products table to the chain of id's. So you are basically adding a '2' to your result as far as I can see. You join on products, and the only thing that limits that table is that "gid" should be 2. So if you add a product with gid 2 you get another result. Either join it (maybe tblproduct.orderid = tblorders.id ? just guessing here) or just remove it, as it does nothing as far as I can see.
If you want to make your query a bit clearer, try not implicitly joining, but do it like this. So you can actually see what's happening
SELECT tblhosting.id, tblhosting.userid, tblhosting.orderid, tblhosting.packageid, tblhosting.server, tblhosting.domain, tblhosting.username, tblorders.invoiceid, tblproducts.gid, tblservers.ipaddress, tblinvoices.status
FROM tblhosting
JOIN tblproducts ON /*you're missing something here!*/
JOIN tblorders ON tblorders.id = tblhosting.orderid
JOIN tblinvoices ON tblinvoices.id = tblorders.invoiceid
JOIN tblservers ON tblservers.id = tblhosting.server
WHERE
tblorders.status = 'Pending'
AND tblproducts.gid = '2'
AND tblinvoices.status = 'Paid'
I don't see in your query JOIN to tblproducts, it seems to be a reason.