Mysql foreign key check taking too much time - mysql

SELECT
*
FROM products
WHERE products.deleted_at is null
AND products.section_id = '1'
AND products.status = '2'
ORDER BY products.created_at DESC
LIMIT 10 OFFSET 0
Time: 3 seconds
SELECT
*
FROM products
WHERE products.deleted_at is null
AND products.status = '2'
ORDER BY products.created_at DESC
LIMIT 10 OFFSET 0
Time:0.2 seconds
Why this foreign key check (where section_id='1') is taking too much of time?
section_id refers to id of sections table.
The table structure is:
CREATE TABLE IF NOT EXISTS `bishal`.`products` (
......
`section_id` INT(10) UNSIGNED NOT NULL,
......
`status` TINYINT(4) NOT NULL,
......
`deleted_at` TIMESTAMP NULL DEFAULT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
......
INDEX `products_section_id_index` (`section_id` ASC),
......
CONSTRAINT `products_section_id_foreign`
FOREIGN KEY (`section_id`)
REFERENCES `bishal`.`sections` (`id`),
......
ENGINE = InnoDB
AUTO_INCREMENT = 225532
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_unicode_ci;

Related

MySQL 8.0.26 Slow Query When Counting Results on Three Tables with Indexes

I have a statistics page on my internal admin site to show some traffic information on individual sites. However, the query is taking nearly 80 seconds to run, even with Indexes placed on the keys for each of the tables.
I'm typically running this query searching for session status within 7 days of the date ran.
SELECT
*,
(
SELECT
COUNT(`session_id`)
FROM
`my-db`.`sessions`
WHERE
`my-db`.`sessions`.`site_id` = `my-db`.`sites`.`site_id`
AND `session_datetime` > '2021-10-17 00:00:00'
) as session_count,
(
SELECT
`session_datetime`
FROM
`my-db`.`sessions`
WHERE
`my-db`.`sessions`.`site_id` = `my-db`.`sites`.`site_id`
AND `session_datetime` > '2021-10-17 00:00:00'
ORDER BY
`session_id` ASC
LIMIT
1
) as first_session,
(
SELECT
`session_datetime`
FROM
`my-db`.`sessions`
WHERE
`my-db`.`sessions`.`site_id` = `my-db`.`sites`.`site_id`
AND `session_datetime` > '2021-10-17 00:00:00'
ORDER BY
`session_id` DESC
LIMIT
1
) as last_session,
(
SELECT
COUNT(`site_profiles_id`)
FROM
`my-db`.`sites_profiles`
WHERE
`my-db`.`sites_profiles`.`site_id` = `my-db`.`sites`.`site_id`
AND `origin` = 1
AND `date_added` > '2021-10-17 00:00:00'
) as profiles_originated,
(
SELECT
COUNT(`site_profiles_id`)
FROM
`my-db`.`sites_profiles`
WHERE
`my-db`.`sites_profiles`.`site_id` = `my-db`.`sites`.`site_id`
AND `scanned` = 1
AND `date_added` > '2021-10-17 00:00:00'
) as profiles_scanned,
(
SELECT
COUNT(`site_profiles_id`)
FROM
`my-db`.`sites_profiles`
WHERE
`my-db`.`sites_profiles`.`site_id` = `my-db`.`sites`.`site_id`
AND `date_added` > '2021-10-17 00:00:00'
) as profiles_collected
FROM
`my-db`.`sites`
WHERE
`site_id` in (
SELECT
DISTINCT(`site_id`)
FROM
`my-db`.`sessions`
WHERE
`session_datetime` > '2021-10-17 00:00:00'
)
ORDER BY
`session_count` DESC
LIMIT
25;
I'm trying to understand the results of EXPLAIN, but I believe the issue is because of the RANGE type of the index used on the datetime.
It's worth noting, I'm dynamically changing the ORDER BY clause depending on a sort dropdown selected by the admin user to sort the results by - site_id ASC/DESC, session_count ASC/DESC and profiles_collected ASC/DESC.
The performance of the profiles_collected DESC is significantly impacted when compared to the others.
network_sites
CREATE TABLE `sites` (
`site_id` bigint NOT NULL AUTO_INCREMENT,
`account_id` bigint NOT NULL,
`site_hash` varchar(128) CHARACTER SET utf8 NOT NULL,
`site_address` varchar(255) CHARACTER SET utf8 NOT NULL,
`site_status` int NOT NULL,
`site_created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`site_updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`site_id`),
UNIQUE KEY `site_id_UNIQUE` (`site_id`),
UNIQUE KEY `site_hash_UNIQUE` (`site_hash`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
network_profiles_sessions
CREATE TABLE `sessions` (
`session_id` bigint NOT NULL AUTO_INCREMENT,
`site_id` bigint NOT NULL,
`profile_id` bigint DEFAULT NULL,
`session_hash` varchar(128) CHARACTER SET utf8 NOT NULL,
`session_ip_address` varchar(45) CHARACTER SET utf8 DEFAULT NULL,
`session_useragent` text CHARACTER SET utf8,
`session_page_uri` text CHARACTER SET utf8,
`session_datetime` datetime DEFAULT CURRENT_TIMESTAMP,
`session_has_data` tinyint DEFAULT '0',
`session_processed` tinyint DEFAULT '0',
`session_queued` tinyint DEFAULT '0',
PRIMARY KEY (`session_id`),
UNIQUE KEY `session_id_UNIQUE` (`session_id`),
KEY `session_has_data` (`session_has_data`,`session_id`),
KEY `session_processed` (`session_processed`,`session_id`),
KEY `session_queued` (`session_queued`,`session_id`),
KEY `session_datetime` (`session_datetime`,`session_id`),
KEY `session_hash` (`session_hash`,`session_id`),
KEY `site_id` (`site_id`,`session_id`),
FULLTEXT KEY `session_page_uri` (`session_page_uri`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
network_sites_profiles
CREATE TABLE `sites_profiles` (
`site_profiles_id` bigint NOT NULL AUTO_INCREMENT,
`site_id` bigint NOT NULL,
`profile_id` bigint NOT NULL,
`origin` int DEFAULT NULL,
`scanned` int DEFAULT NULL,
`date_added` datetime DEFAULT CURRENT_TIMESTAMP,
`date_lastseen` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`site_profiles_id`),
UNIQUE KEY `site_users_id_UNIQUE` (`site_profiles_id`),
KEY `site_id` (`site_id`,`site_profiles_id`),
KEY `date_added` (`date_added` DESC,`site_profiles_id`),
KEY `origin` (`origin`,`site_profiles_id`),
KEY `scanned` (`scanned`,`site_profiles_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
PRIMARY KEY(a)
UNIQUE KEY(a) -- redundant, DROP it
A PK is a UNIQUE key is an INDEX.
The last 3 subqueries can be combined:
SELECT SUM(origin = 1) AS profiles_originated,
SUM(scanned = 1) AS profiles_scanned,
COUNT(*) AS profiles_collected
FROM profiles
WHERE date_added >= '2021-10-17'
And then JOIN to that. However, there are some potential problems...
How do session.datetime and date_added compare? I'm assuming that a session is added before it happens?
I assume you want to include midnight of the morning of Oct 17?
The first 3 subqueries can perhaps be similarly simplified. Note that MAX(session_datetime) is sufficient for last_session.

EXPLAIN filter value changes on changing column

I have experienced a strange behavior in MySQL 5.7
SELECT orderId
FROM `order`
WHERE vendor = 11 AND status >= 0 AND city = 1
AND created_date >= '2020-12-01' AND created_date <= '2020-12-31'
SELECT userId
FROM `order`
WHERE vendor = 11 AND status >= 0 AND city = 1
AND created_date >= '2020-12-01' AND created_date <= '2020-12-31'
I have a composite index on all the 4 columns
INDEX is vendor_city_date (vendor, city, created_date, status).
But for some columns I get a filter of 50 for others I get a filter of 5.55.
This is the structure of the table it has 50 other columns I have removed those.
CREATE TABLE `order` (
`orderId` int(11) NOT NULL AUTO_INCREMENT,
`vendor` int(11) NOT NULL DEFAULT '1',
`userId` int(11) NOT NULL,
`offer` int(11) NOT NULL DEFAULT '0',
`city` int(11) NOT NULL,
`offset` int(11) NOT NULL DEFAULT '',
`status` tinyint(4) NOT NULL DEFAULT '0',
`payment` tinyint(4) NOT NULL DEFAULT '0',
`created_date` date NOT NULL,
PRIMARY KEY (`orderId`),
KEY `city_id` (`city`),
KEY `user_id` (`userId`),
KEY `status` (`status`),
KEY `vendor` (`vendor`),
KEY `user_status` (`userId`,`status`),
KEY `vendor_city_date` (`vendor`,`city`,`created_date`,`status`) USING BTREE
);

Not equals query is so slow

I have a query like that:
SELECT * , (
( 1584392725 ) - ( suprayts.time )
) AS timeDiff
FROM (
`suprayts`
)
WHERE `suprayts`.`is_deleted` = '0'
AND `suprayts`.`is_approved` =1
AND `suprayts`.`username` != 'rayben1'
AND `suprayts`.`time` >1584306325
ORDER BY `suprayts`.`is_boosted_by_user` DESC , `suprayts`.`id` ASC
LIMIT 10
This query runs very slow (avg 0.2 seconds), if i delete the following line:
AND `suprayts`.`username` != 'rayben1'
It runs 10x faster. (avg 0.02 secs) How can i speed up this query?
My indexes:
Explain:
My table:
CREATE TABLE IF NOT EXISTS `suprayts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(15) CHARACTER SET utf8 NOT NULL,
`question` varchar(70) COLLATE utf8mb4_unicode_ci NOT NULL,
`suprayt_photo` varchar(50) CHARACTER SET utf8 NOT NULL,
`time` int(11) NOT NULL,
`endTime` int(11) NOT NULL,
`datetext` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`like_count` int(10) unsigned NOT NULL DEFAULT '0',
`dislike_count` int(10) unsigned NOT NULL DEFAULT '0',
`is_approved` bit(1) NOT NULL DEFAULT b'0',
`is_deleted` enum('1','0') CHARACTER SET utf8 NOT NULL DEFAULT '0',
`is_end_notification_sent` bit(1) NOT NULL DEFAULT b'0',
`open_vote` enum('0','1') CHARACTER SET utf8 NOT NULL DEFAULT '1',
`boost` int(11) NOT NULL DEFAULT '0',
`is_boosted_by_user` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `username_3` (`username`,`suprayt_photo`),
KEY `id` (`id`,`time`,`is_approved`,`is_deleted`),
KEY `username` (`username`,`is_deleted`),
KEY `username_2` (`username`,`datetext`),
KEY `id_2` (`id`,`username`,`time`,`is_approved`,`is_deleted`),
KEY `username_4` (`username`,`time`,`is_approved`,`is_deleted`),
KEY `ix1` (`id`,`time`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=130789 ;
Depending on the SQL pre-compiler if one is being used, or the SQL itself, possible it is sensitive to the order of the query "where" clause not being in DB key order and it is not using the index and instead doing a sequential scan? Just to eliminate the possibility, try putting the WHERE items in the DB index key order.
USE NOT EXISTS
SELECT * , (
( 1584392725 ) - ( suprayts.time )
) AS timeDiff
FROM (
`suprayts`
)
WHERE `suprayts`.`is_deleted` = '0'
AND `suprayts`.`is_approved` =1
AND NOT EXISTS (
SELECT x.no FROM (SELECT 1 AS no) x WHERE `suprayts`.`username` = 'rayben1'
)
AND `suprayts`.`time` >1584306325
ORDER BY `suprayts`.`is_boosted_by_user` DESC , `suprayts`.`id` ASC
LIMIT 10

How to make MySQl update faster for large tables

I would like to optimise an update query of this table as it currently takes a couple of seconds to execute. I am using MySQL 5.6
UPDATE skus1 SET `available` = '1', `locked` = NULL WHERE `locked` IS NOT NULL AND `locked` < (NOW() - INTERVAL 30 MINUTE)
I would like to optimise this query also
UPDATE skus1 SET `available` = '2', `locked` = NOW() WHERE sku = '111111'
I have a SELECT query that runs fast now I've added my indexes
Here is my table structure
CREATE TABLE `skus1` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`sku` varchar(9) NOT NULL,
`available` tinyint(1) NOT NULL DEFAULT '1',
`locked` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `sku_2` (`sku`,`locked`),
KEY `available` (`available`),
KEY `locked` (`locked`),
KEY `sku` (`sku`)
) ENGINE=MyISAM AUTO_INCREMENT=500001 DEFAULT CHARSET=utf8

mySQL query is very slow after using DISTINCT and GROUP BY?

I have tables with following structure:
-- Table structure for table `temp_app`
--
CREATE TABLE IF NOT EXISTS `temp_app` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`vid` int(5) NOT NULL,
`num` varchar(64) NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `vid` (`vid`),
KEY `num` (`num`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=69509;
-- Table structure for table `inv_flags`
--
CREATE TABLE IF NOT EXISTS `inv_flags` (
`num` varchar(64) NOT NULL,
`vid` int(11) NOT NULL,
`f_special` tinyint(1) NOT NULL, /*0 or 1*/
`f_inserted` tinyint(1) NOT NULL, /*0 or 1*/
`f_notinserted` tinyint(1) NOT NULL, /*0 or 1*/
`userID` int(11) NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY `num` (`num`),
KEY `userID` (`userID`),
KEY `vid` (`vid`),
KEY `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Execution time of the following query is 9 seconds to display 30 records. What is wrong?
SELECT date_format(ifs.`timestamp`,'%y/%m/%d') as `date`
,count(DISTINCT ta.num) as inserted /*Unique nums*/
,SUM(ifs.f_notinserted) as not_inserted
,SUM(ifs.f_special) as special
,count(ta.num) as links /*All nums*/
from inventory_flags ifs
LEFT JOIN temp_app ta ON ta.num = ifs.num AND ta.vid = ifs.vid
WHERE ifs.userID = 3
GROUP BY date(ifs.`timestamp`) DESC LIMIT 30
EXPLAIN RESULT
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE ifs ref userID userID 4 const 12153 Using where
1 SIMPLE ta ref vid,num num 194 ifs.num 1
COUNT DISTINCT can sometimes cause rotten performance with MySql. Try this instead:
select count(*) from (select distinct...
as it can sometimes prevent MySql from writing the entire interim result to disk.
Here is the MySql bug info:
http://bugs.mysql.com/bug.php?id=21849