mysql self join performance - mysql

Image there is a table
CREATE TABLE `message`(
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`user_id` int(11) NOT NULL,
`content` text NOT NULL,
`create_time` int(11) NOT NULL,
`thumbs_up` int(11) NOT NULL DEFAULT '0', /*Vote Count*/
PRIMARY KEY (`id`),
KEY `thumbs_up_key` (`thumbs_up`,`id`)
)
from some artitle,is says,when doing paging query(or maybe irrelevant),
query using sql
select m2.* from message m1,message m2
where m1.id=m2.id and m1.thumbs_up <=98
and (m1.id<13 or m1.thumbs_up<98)
order by m1.thumbs_up desc,m1.id desc
is better than sql
select *
from message
where thumbs_up <=98 and (id<13 or thumbs_up<98)
order by thumbs_up desc,id desc
limit 100
why?
I read it on this article https://www.slideshare.net/suratbhati/efficient-pagination-using-mysql-6187107 , page 19
Thanks

Related

MySql Using filesort when i using a group by

I have a little problem with optimizing a query, I have 2 tables, one which records the participation (participation) in a quiz, and the other which records the answer to each question (participation_rep), participation is linked to the campaign table.
SELECT count(DISTINCT p.id) as number_of_participation
FROM participation_rep prep
INNER JOIN participation p
ON p.id = prep.id_participation
AND p.trash <> 1
WHERE prep.id_question IN (780,787,794,801,809)
AND prep.trash <> 1
GROUP BY pp.id_campaign
Explain of the query
And the problem is that this request is very heavy to execute when there is a lot of data which is concerned by the request and I do not know how to optimize it.
This query take 30-50ms to execute.
Structure of table participation :
CREATE TABLE IF NOT EXISTS `participation` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_campagne` int(11) NOT NULL,
`id_identifiant` int(11) DEFAULT NULL,
`firstname` varchar(255) DEFAULT NULL,
`surname` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`date_p` date NOT NULL,
`hour_p` time NOT NULL,
`comment` text,
`trash` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Structure of table participation_rep :
CREATE TABLE IF NOT EXISTS `participation_rep` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_participation` int(11) NOT NULL,
`id_question` int(11) NOT NULL,
`id_rep` int(11) NOT NULL,
`trash` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `id_participation` (`id_participation`,`id_question`,`id_reponse`),
KEY `id_question` (`id_question`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

index_merge full table scan - 2 seconds mysql select

I have this select:
SELECT MAX(id) FROM chat
WHERE (`to` = 1 and `del_to_status` = '0') or (`from` = 1 and `del_from_status` = '0')
GROUP BY CASE WHEN 1 = `to` THEN `from` ELSE `to` END
chat:
`chat` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`from` int(11) UNSIGNED NOT NULL,
`to` int(11) UNSIGNED NOT NULL,
`message` text NOT NULL,
`del_from_status` tinyint(1) NOT NULL DEFAULT '0',
`del_to_status` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `from` (`from`),
KEY `to` (`to`),
);
The problem is it is using full table scan:
it is taking a lot of time. any ideas to get faster results?
What do you think about this solution:
select grouped_by_to.user, greatest(grouped_by_to.id, grouped_by_from.id ) from
(
select c1.to as user, max(id) as id from chat c1
group by c1.to
) grouped_by_to
join
(
select c1.from as user, max(id) as id from chat c1
group by c1.from
) grouped_by_from on grouped_by_from.user = grouped_by_to.user
Note that i ignored the del_to_status columns, you can add them easily.
But actually I think your whole db schema is wrong, I think you need something more like :
`messages` (
`message_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(11) UNSIGNED NOT NULL,
`message` text NOT NULL,
`message_date` timestamp NOT NULL,
PRIMARY KEY (`message_id`),
);
`conversatinos` (
`conversation_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`message_id` int(11) UNSIGNED NOT NULL,
PRIMARY KEY (`conversation_id`),
);
`users` (
`user_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_name` int(11) UNSIGNED NOT NULL,
PRIMARY KEY (`user_id`),
);
AND maybe if you need:
`chat` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`message_id` int(11) UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
);

Mysql selecting user friendes likes

i have these tables
posts table
CREATE TABLE `users_posts` (
`pid` INT(11) NOT NULL AUTO_INCREMENT, /*post id*/
`uid` INT(11) NOT NULL, /*user who created the post, owner*/
`created_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_date` DATETIME NOT NULL,
`post_status` TINYINT(1) NOT NULL,
`content` TEXT NULL COLLATE 'utf8_general_ci',
PRIMARY KEY (`pid`),
)
connections table
CREATE TABLE `users_connections` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_1_uid` INT(11) NOT NULL, /*user id*/
`user_2_uid` INT(11) NOT NULL, /*friend user id of user id*/
`connections_status` TINYINT(4) NOT NULL,
PRIMARY KEY (`id`)
)
likes table
CREATE TABLE `post_ups` (
`upid` INT(11) NOT NULL AUTO_INCREMENT,
`uid` INT(11) NOT NULL, /*user id who like the post*/
`puid` INT(11) NOT NULL, /*post owner user id=users_posts.uid*/
`pid` INT(11) NOT NULL, /*post id=users_posts.pid*/
`up_status` TINYINT(1) NOT NULL,
PRIMARY KEY (`upid`),
INDEX `uid` (`uid`),
INDEX `up_status` (`up_status`),
INDEX `pid` (`pid`)
)
i need to select all posts and inside the query i need to check if some of my friend has like the post i'm currently selecting and if true , i can show this post on top of other post to show more relevant content
SELECT *,IF(CHECK IF FREIND LIKED THIS POST PUID == F_UID) FROM users_posts
is this possible ??
Query must like below:
SELECT up.*,
(SELECT count(*) FROM post_ups pups
WHERE up.pid=pups.pid AND up.uid <> pups.uid/*exclude own like*/
AND EXISTS (SELECT 0 FROM users_connections uc
WHERE uc.user_1_uid=up.uid/*pups.puid*/ AND uc.user_2_uid=pups.uid)) relevance
FROM users_posts up
ORDER BY relevance DESC;

Mysql case when getting another table

I have got two tables one is log details and another is user name list.
I can get with "INNER JOIN user_tbl ON log_user_id=user_id" but also I have got a super user id is exception and this exception user isn't in the user table. I am using this user at the back ground the relation with database is level of this user like as 9999.
How can I show the super user as a name like as "Supervisory"?
Example sql:
Table structures are:
CREATE TABLE `user_list_tb` (
`user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_name` varchar(40) COLLATE utf8_turkish_ci NOT NULL,
`user_pwd` varchar(40) COLLATE utf8_turkish_ci NOT NULL,
`user_level` int(11) NOT NULL DEFAULT '0',
`user_owner_id` int(10) unsigned NOT NULL,
`user_datetime` datetime NOT NULL,
`user_change_pwd` tinyint(1) DEFAULT NULL,
`user_pwd_try` int(3) unsigned DEFAULT NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `user_name_UNIQUE` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_turkish_ci;
CREATE TABLE `log_system_tb` (
`log_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`log_act_name` varchar(10) COLLATE utf8_turkish_ci NOT NULL,
`log_user_id` int(10) unsigned NOT NULL,
`log_datetime` datetime NOT NULL,
`log_message` varchar(256) COLLATE utf8_turkish_ci DEFAULT NULL,
PRIMARY KEY (`log_id`)
) ENGINE=InnoDB AUTO_INCREMENT=49720 DEFAULT CHARSET=utf8 COLLATE=utf8_turkish_ci;
SELECT
log_id,
log_act_name,
log_user_id,
user_name,
log_datetime,
log_message
from log_system_tb
JOIN user_list_tb
ON (log_user_id=user_id)
where log_datetime>="2016-04-01 00:00:00"
and
log_datetime<="2016-04-14 00:00:00"
order by log_id desc limit 1000;
Best regards
Mehmet
You need to use a left join because there will not be a row in the user_list that corresponds to the supervisor's logged events. Also please see the case statement below.
SELECT
log_id,
log_act_name,
log_user_id,
(case when user_id=9999 then 'Supervisory' ELSE user_name END),
log_datetime,
log_message
from log_system_tb
LEFT JOIN user_list_tb
ON (log_user_id=user_id)
where log_datetime>="2016-04-01 00:00:00"
and
log_datetime<="2016-04-14 00:00:00"
order by log_id desc limit 1000;
You can do it like this:
(case when user_id=9999 then 'Supervisory' END)

MySQL: Find "last modified" date from two tables

I have two MySQL tables: tech_requests and comments. I want to display each tech_request one time in a list ordered by the "last modified" date, whether that be the date of the tech_request creation or the latest comment tied to that tech_request. I was trying to use UNION but I got stuck. Any ideas would be much appreciated. Here are the tables:
CREATE TABLE `tech_requests` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`staff_member_id` int(3) NOT NULL,
`date_time` datetime NOT NULL,
`request` text NOT NULL,
`building_id` int(2) NOT NULL,
`technician_id` int(2) DEFAULT NULL,
`completed` tinyint(1) NOT NULL,
`subject` varchar(30) NOT NULL DEFAULT '',
`category_id` int(2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=203 DEFAULT CHARSET=utf8;
CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tech_request_id` int(11) NOT NULL,
`technician_id` int(2) NOT NULL,
`date_time` datetime NOT NULL,
`comment` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=234 DEFAULT CHARSET=utf8;
Are you looking for something like this?
SELECT r.id, r.staff_member_id, ...,
GREATEST(r.date_time, COALESCE(c.date_time, 0)) last_modified
FROM tech_requests r LEFT JOIN
(
SELECT tech_request_id, MAX(date_time) date_time
FROM comments c
GROUP BY tech_request_id
) c
ON r.id = c.tech_request_id
ORDER BY last_modified
Here is SQLFiddle demo