Related
I have the following query:
select
`stories`.*
from
`stories`
inner join `communities` on `stories`.`community_id` = `communities`.`id`
inner join `communities_followers` on `communities`.`id` = `communities_followers`.`community_id`
where
`is_published` = 1
and `communities_followers`.`user_id` = 1
and `communities_followers`.`status` = 1
order by
`stories`.`created_at` desc
limit
20 offset 0
With an single index on communities_followers.user_id and a compound index on ['user_id', 'status']
When doing an explain on the query, this is the result:
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------------------+------------+--------+--------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------+---------+---------------------------------------------+------+----------+----------------------------------------------+
| 1 | SIMPLE | communities_followers | NULL | ref | communities_followers_user_id_index,communities_followers_community_id_index,communities_followers_community_id_user_id_status_index | communities_followers_user_id_index | 4 | const | 77 | 10.00 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | communities | NULL | eq_ref | PRIMARY,communities_id_default_community_index | PRIMARY | 4 | grepless.communities_followers.community_id | 1 | 100.00 | Using index |
| 1 | SIMPLE | stories | NULL | ref | stories_community_id_index | stories_community_id_index | 4 | grepless.communities_followers.community_id | 3968 | 100.00 | NULL |
+----+-------------+-----------------------+------------+--------+--------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------+---------+---------------------------------------------+------+----------+----------------------------------------------+
Explain
What else can I do to improve this? communities_followers does contain a ton of records.
Stories table:
CREATE TABLE `stories` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`user_id` int unsigned NOT NULL,
`publisher_id` int unsigned DEFAULT NULL,
`community_id` int unsigned NOT NULL,
`content_type_id` int unsigned NOT NULL DEFAULT '10',
`title` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`description` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`meta_description` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`score` int NOT NULL DEFAULT '0',
`score_alternate` int NOT NULL DEFAULT '0',
`views` int NOT NULL DEFAULT '0',
`url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`source_url` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`picture` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`meta` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`embed` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`picture_original` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`picture_huge` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`picture_big` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`picture_small` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`picture_extra` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`slug` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`is_published` tinyint(1) NOT NULL DEFAULT '1',
`is_summarized` tinyint(1) NOT NULL DEFAULT '0',
`language` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`show_in_feed` tinyint(1) NOT NULL DEFAULT '1',
`is_pinned` tinyint(1) NOT NULL DEFAULT '0',
`has_pictures_localized` tinyint(1) NOT NULL DEFAULT '0',
`has_pictures_optimized` tinyint(1) NOT NULL DEFAULT '0',
`has_audio` tinyint(1) NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`author` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`comments_count` bigint NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `stories_created_at_index` (`created_at`),
KEY `stories_user_id_index` (`user_id`),
KEY `stories_community_id_index` (`community_id`),
KEY `stories_content_type_id_index` (`content_type_id`),
KEY `stories_score_index` (`score`),
KEY `stories_slug_index` (`slug`),
KEY `stories_show_in_feed_index` (`show_in_feed`),
KEY `stories_publisher_id_index` (`id`),
KEY `stories_deleted_at_is_published_content_type_id_index` (`deleted_at`,`is_published`,`content_type_id`),
KEY `stories_publisher_id_deleted_at_index` (`publisher_id`,`deleted_at`),
KEY `stories_is_published_deleted_at_index` (`is_published`,`deleted_at`,`community_id`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=952978 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
communities table
communities | CREATE TABLE `communities` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`user_id` int unsigned DEFAULT NULL,
`name` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`slug` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`header` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`color` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`background` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`background_cover` tinyint(1) NOT NULL DEFAULT '1',
`picture_big` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`picture_small` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`has_pictures_optimized` tinyint(1) NOT NULL DEFAULT '0',
`default_community` tinyint(1) NOT NULL DEFAULT '0',
`is_popular` tinyint(1) NOT NULL DEFAULT '0',
`status` smallint NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`stories_count` bigint NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `communities_user_id_index` (`user_id`),
KEY `communities_name_index` (`name`),
KEY `communities_slug_index` (`slug`),
KEY `communities_status_index` (`status`),
KEY `communities_default_community_index` (`default_community`),
KEY `communities_id_default_community_index` (`id`,`default_community`)
) ENGINE=MyISAM AUTO_INCREMENT=183 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
Communities followers table
communities_followers | CREATE TABLE `communities_followers` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`user_id` int unsigned NOT NULL,
`community_id` int unsigned NOT NULL,
`status` smallint NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `communities_followers_user_id_index` (`user_id`),
KEY `communities_followers_community_id_index` (`community_id`),
KEY `communities_followers_community_id_user_id_status_index` (`community_id`,`user_id`,`status`)
) ENGINE=MyISAM AUTO_INCREMENT=326484 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
SELECT
`stories`.*
FROM
`communities_followers`
INNER JOIN
`stories`
ON `stories`. `community_id` = `communities_followers`.`community_id`
WHERE
`communities_followers`.`user_id` = 1
AND `communities_followers`.`status` = 1
AND `stories`.`is_published` = 1
ORDER BY
`stories`.`created_at` DESC
LIMIT
20 OFFSET 0
Then to speed up the initial filter index...
communities_followers(user_id, status, community_id)
Then to speed up the join AND the ordering and limit...
stories(community_id, is_published, created_at)
OR
stories(is_published, created_at, community_id)
But I'd use EXISTS, as it will still work if you query against multiple users (without causing duplication in the results)...
SELECT
`stories`.*
FROM
`stories`
WHERE
`stories`.`is_published` = 1
AND EXISTS (
SELECT *
FROM `communities_followers`
WHERE `communities_followers`.`user_id` = 1
AND `communities_followers`.`status` = 1
AND `communities_followers`.`community_id` = `stories`.`community_id`
)
ORDER BY
`stories`.`created_at` DESC
LIMIT
20 OFFSET 0
I have worked my way around many challenges with MySQL, and i think right now i am able to build everything that i need, to get something to work. But now, for a pretty huge SQL statement that returns a lot of data, i need to work on MySQL performance for the first time.
I was hoping someone here could help me find out why the following statement is so incredibly slow. It takes over 3 minutes to collect 740 results out of different tables. The biggest table beeing the "reports" table, consisting of somewhere over 20.000 entries at the moment.
I can also educate myself if someone could just point me in the right direction. I don't even know where to search for answers for my current problem.
Okay, so here is the statement that i am talking about. Maybe, if someone has enough experience with SQL performance, something just right away jumps at them. I would be happy for any kind of feedback. I'll elaborate on the statement right after the code itself:
SELECT
R_ID,
R_From,
R_To,
SUM(UR_TotalTime) AS UR_TotalTime,
R_Reported,
U_ID,
U_Lastname,
U_Firstname,
C_ID,
C_Lastname,
C_Firstname,
R_Breaks,
MAX(CR_BID) AS CR_BID,
R_Type,
R_Distance,
R_AdditionalDistance,
R_Activities,
R_Description,
R_Signature,
CT_SigReq,
MAX(I_LastIntegration) AS I_LastIntegration
FROM
reports
LEFT JOIN
userreports ON R_ID = UR_RID
LEFT JOIN
users ON R_UID = U_ID
LEFT JOIN
customers ON R_CID = C_ID
LEFT JOIN
customerterms ON CT_CID = R_CID
LEFT JOIN
integration ON R_UID = I_UID
LEFT JOIN
customerreports ON R_ID = CR_RID
WHERE
(CAST(R_From AS DATE) BETWEEN CT_From AND CT_To
OR R_CID = 0)
AND ((R_From BETWEEN '2021-02-01 00.00.00' AND '2021-02-28 23.59.59')
OR (R_To BETWEEN '2021-02-01 00.00.00' AND '2021-02-28 23.59.59')
OR (R_From <= '2021-02-01 00.00.00'
AND R_To >= '2021-02-28 23.59.59'))
GROUP BY R_ID
ORDER BY R_From ASC
So what i have here is the following:
reports (R_*) - This is the main table that is queried. I need some of it's data, but it's also the filter, since i only need results between specific timestamps.
CREATE TABLE `reports` (
`R_ID` int(100) NOT NULL AUTO_INCREMENT,
`R_Type` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`R_UID` int(6) NOT NULL,
`R_CID` int(10) NOT NULL,
`R_From` datetime(0) NOT NULL,
`R_To` datetime(0) NOT NULL,
`R_Traveltime` int(11) NOT NULL,
`R_Breaks` int(11) NOT NULL DEFAULT 0,
`R_PayoutFlextime` decimal(20, 2) NOT NULL DEFAULT 0.00,
`R_Distance` int(11) NOT NULL DEFAULT 0,
`R_AdditionalDistance` int(11) NOT NULL DEFAULT 0,
`R_Activities` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`R_Description` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`R_Signature` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0',
`R_SignatureDate` datetime(0) DEFAULT NULL,
`R_Reported` datetime(0) NOT NULL,
`R_Status` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'New',
`R_EditedBy` int(11) DEFAULT NULL,
`R_EditedDateTime` datetime(0) DEFAULT NULL,
PRIMARY KEY (`R_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
userreports (UR_*) - Delivers some data that is calculated from the sourcedata in reports
CREATE TABLE `userreports` (
`UR_ID` int(11) NOT NULL AUTO_INCREMENT,
`UR_RID` int(100) NOT NULL,
`UR_UID` int(6) NOT NULL,
`UR_Date` date NOT NULL,
`UR_From` time(0) NOT NULL,
`UR_To` time(0) NOT NULL,
`UR_ReportedTime` decimal(20, 5) DEFAULT NULL,
`UR_ReportedTravel` decimal(20, 5) NOT NULL,
`UR_ReportedBreaks` decimal(20, 5) DEFAULT NULL,
`UR_TotalPercentageSurcharge` decimal(20, 2) DEFAULT NULL,
`UR_TotalTime` decimal(20, 5) DEFAULT NULL,
`UR_PercentageSurchargeTypes` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`UR_Distance` decimal(20, 2) DEFAULT NULL,
`UR_AdditionalDistance` decimal(20, 2) DEFAULT NULL,
`UR_TravelCompensation` decimal(20, 2) DEFAULT NULL,
PRIMARY KEY (`UR_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
customerreports (CR_*) - Same as userreports, but with calculated data from the customers perspective
CREATE TABLE `customerreports` (
`CR_ID` int(11) NOT NULL AUTO_INCREMENT,
`CR_RID` int(100) NOT NULL,
`CR_CID` int(6) NOT NULL,
`CR_Date` date NOT NULL,
`CR_From` time(0) NOT NULL,
`CR_To` time(0) NOT NULL,
`CR_ReportedTime` decimal(20, 2) DEFAULT NULL,
`CR_ReportedBreaks` decimal(20, 2) DEFAULT NULL,
`CR_Hourly` decimal(20, 2) DEFAULT NULL,
`CR_Salary` decimal(20, 2) DEFAULT NULL,
`CR_TotalPercentageSurcharge` decimal(20, 2) DEFAULT NULL,
`CR_TotalFixedSurcharge` decimal(20, 2) DEFAULT NULL,
`CR_TotalTime` decimal(20, 2) DEFAULT NULL,
`CR_TotalSalary` decimal(20, 2) DEFAULT NULL,
`CR_FixedSurchargeTypes` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`CR_PercentageSurchargeTypes` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`CR_Distance` decimal(20, 2) DEFAULT NULL,
`CR_AdditionalDistance` decimal(20, 2) DEFAULT NULL,
`CR_TravelCompensation` decimal(20, 2) DEFAULT NULL,
`CR_BID` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`CR_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
users (U_*) - Obviously delivers Data to the user that created the report, e.g. name,...
CREATE TABLE `users` (
`U_ID` int(6) NOT NULL AUTO_INCREMENT,
`U_PW` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_PWInitial` tinyint(1) NOT NULL,
`U_FailedAttempts` int(1) NOT NULL,
`U_Email` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Title` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`U_Firstname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Lastname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_ETC` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Street` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Housenumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Code` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_City` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Birthdate` date NOT NULL,
`U_Sex` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Maritalstatus` varchar(2) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Severelydisabled` tinyint(1) NOT NULL,
`U_Severelydisabledspecify` int(3) NOT NULL,
`U_Citizenship` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Education` varchar(70) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Vocationaltraining` varchar(70) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_CLID` tinyint(1) NOT NULL,
`U_CLSpecify` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_IBAN` varchar(27) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_BIC` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_INID` int(11) DEFAULT NULL,
`U_Insurancenumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Insurancetype` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Taxidentificationnumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Confession` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Entry` date NOT NULL,
`U_TEntry` date NOT NULL,
`U_Exit` date NOT NULL DEFAULT '9999-12-31',
`U_Hourscarryover` decimal(20, 2) NOT NULL,
`U_TotalHolidayCarryover` int(11) NOT NULL DEFAULT 0,
`U_UsedHolidayCarryover` int(11) NOT NULL DEFAULT 0,
`U_SIN` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_RVBDone` tinyint(1) NOT NULL DEFAULT 0,
`U_ClosedMonth` date NOT NULL DEFAULT '1970-01-01',
`U_DeleteDate` date DEFAULT NULL,
PRIMARY KEY (`U_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
customers (C_*) - Same as users, but for the data of the customer that the user worked on
CREATE TABLE `customers` (
`C_ID` int(10) NOT NULL AUTO_INCREMENT,
`C_MID` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Active` tinyint(1) NOT NULL,
`C_Email` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_Title` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_Firstname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Lastname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Birthdate` date NOT NULL,
`C_ETC` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Street` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Housenumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Code` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_City` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_Mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_IBAN` varchar(27) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_BIC` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Insurancenumber` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_INID` int(11) DEFAULT NULL,
`C_Insurancetype` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_Sex` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_Contact1` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_Contact2` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_ContactChoice` int(1) DEFAULT 0,
`C_DeleteDate` date DEFAULT NULL,
`C_DeactivationDate` date DEFAULT NULL,
`C_CreationDate` date DEFAULT NULL,
`C_DeceasedDate` date DEFAULT NULL,
PRIMARY KEY (`C_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
integration (I_*) - Provides data on whether or not the report is already integrated (and can no longer be changed)
CREATE TABLE `integration` (
`I_ID` int(11) NOT NULL AUTO_INCREMENT,
`I_UID` int(11) NOT NULL,
`I_LastIntegration` date NOT NULL DEFAULT '1970-01-01',
`I_SumFlextime` decimal(20, 2) NOT NULL DEFAULT 0.00,
`I_OldHolidays` int(5) NOT NULL DEFAULT 0,
PRIMARY KEY (`I_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
customerterms (CT_*) - In this case only provides if the specified customer needs to sign the report
CREATE TABLE `customerterms` (
`CT_ID` int(50) NOT NULL AUTO_INCREMENT,
`CT_CID` int(10) NOT NULL,
`CT_From` date NOT NULL,
`CT_To` date NOT NULL,
`CT_Hourly` decimal(20, 2) NOT NULL,
`CT_FixedTravelCompensation` decimal(20, 2) NOT NULL,
`CT_PerKMCompensationBase` decimal(20, 2) NOT NULL,
`CT_PerKMCompensationAdditional` decimal(20, 2) NOT NULL,
`CT_MaxTravelCompensationReport` decimal(20, 2) DEFAULT NULL,
`CT_MaxTravelCompensationMonthly` decimal(20, 2) DEFAULT NULL,
`CT_FixedSaturdaySurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageSaturdaySurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_FixedSundaySurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageSundaySurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_FixedHolidaySurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageHolidaySurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_SigReq` int(1) NOT NULL,
`CT_NighttimeFrom` time(0) NOT NULL DEFAULT '00:00:00',
`CT_NighttimeTo` time(0) NOT NULL DEFAULT '00:00:00',
`CT_FixedNighttimeSurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageNighttimeSurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_StackingSurcharge` tinyint(1) NOT NULL DEFAULT 0,
`CT_MinimumTime` int(11) NOT NULL DEFAULT 1,
`CT_TimeIncrement` int(11) NOT NULL DEFAULT 1,
PRIMARY KEY (`CT_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
The server is running MySQL 5.7, has 4 processors at 4,6Ghz, and 16GB of RAM available.
Since this is a hobby project, that i am supporting small care-businesses with, to allow them easier management of their daily tasks, i can change everything here. Code, Database Layout, you name it. As long as the poor people in the office don't have to wait for 5 minutes, just to sometimes even only get a timeout...
I'll add the result of EXPLAIN as image, since i can't get it to look good
otherwise...
+─────+──────────────+──────────────────+─────────────+─────────+──────────────────────+──────────+──────────+──────────────────────────+───────+───────────+─────────────────────────────────────────────────────+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+─────+──────────────+──────────────────+─────────────+─────────+──────────────────────+──────────+──────────+──────────────────────────+───────+───────────+─────────────────────────────────────────────────────+
| 1 | SIMPLE | reports | NULL | ALL | PRIMARY,R_From,R_To | NULL | NULL | NULL | 22249 | 29.76 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | userreports | NULL | ALL | NULL | NULL | NULL | NULL | 21359 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | users | NULL | eq_ref | PRIMARY | PRIMARY | 4 | dbs671769.reports.R_UID | 1 | 100.00 | NULL |
| 1 | SIMPLE | customers | NULL | eq_ref | PRIMARY | PRIMARY | 4 | dbs671769.reports.R_CID | 1 | 100.00 | NULL |
| 1 | SIMPLE | customerterms | NULL | ALL | NULL | NULL | NULL | NULL | 1429 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | integration | NULL | ALL | NULL | NULL | NULL | NULL | 1134 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | customerreports | NULL | ALL | NULL | NULL | NULL | NULL | 9078 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+─────+──────────────+──────────────────+─────────────+─────────+──────────────────────+──────────+──────────+──────────────────────────+───────+───────────+─────────────────────────────────────────────────────+
Is there any way to consolidate all this data faster, but as reliable?
Thanks a lot in advance, for any help or idea on this.
Let's start by adding an index for each of the foreign keys used in your query -
ALTER TABLE `userreports`
ADD INDEX `FK_UR_RID` (`UR_RID`);
ALTER TABLE `customerterms`
ADD INDEX `FK_CT_CID` (`CT_CID`);
ALTER TABLE `integration`
ADD INDEX `FK_I_UID` (`I_UID`);
ALTER TABLE `customerreports`
ADD INDEX `FK_CR_RID` (`CR_RID`);
Please add these indices and then add the updated EXPLAIN output plus the result of the following query to your question.
-- this just retrieves some basic stats about size of each table used in your query
SELECT TABLE_NAME, ENGINE, VERSION, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH, INDEX_LENGTH
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'dbs671769'
AND TABLE_NAME IN('customerreports', 'customers', 'customerterms', 'integration', 'reports', 'userreports', 'users');
WHERE (CAST(R_From AS DATE) BETWEEN CT_From AND CT_To
OR R_CID = 0
)
OR is sluggish. Is there some way to get rid of R_CID = 0? If not, we can talk about UNION.
Rewrite the rest thus:
R_From >= CT_From AND R_From < CT_To + INTERVAL 1 DAY
AND ((R_From BETWEEN '2021-02-01 00.00.00'
AND '2021-02-28 23.59.59')
OR (R_To BETWEEN '2021-02-01 00.00.00'
AND '2021-02-28 23.59.59')
OR (R_From <= '2021-02-01 00.00.00'
AND R_To >= '2021-02-28 23.59.59')
)
Is R_From guaranteed to be < R_To? If so, would this simplification (OR-removal) do the same
AND R_From < '2021-03-01'
AND R_To >= '2021-02-01'
This requires two passes over the intermediate results:
GROUP BY R_ID
ORDER BY R_From ASC
This requires one pass, while usually giving the same results, maybe even better results:
GROUP BY R_From, R_ID
ORDER BY R_From, R_ID
(Pet peeve: Don't prefix column names with the table name (or 'R_'); Do use aliases for all columns when JOINing: SELECT R.ID ... FROM reports AS R JOIN ... )
Another Answer has mentioned some INDEXes; that may give you a lot of speed-up. After some of my suggestions, there may be more index tips.
TEXT columns have some overhead; many of the cases you list could be done with something smaller, like VARCHAR(100). For example, currently, the longest "city" name in the world has only 91 chars: "Poselok Uchebnogo Khozyaystva Srednego Professionalno-Tekhnicheskoye Uchilishche Nomer Odin"
You seem to be running an old version of MySQL? Else you might have been dinged for the GROUP BY; cf "only_full_group_by".
I have 3 large table around 1 table have 1,000,000 rows other 2 have 500,000 rows.
When I use ORDER BY in query it takes 15 seconds (without ORDER BY 0.001s).
I already try add index on invoice.created_at and invoice.invoice_id
But still take a lot of time.
SELECT
`i`.*,
GROUP_CONCAT(DISTINCT ii.item_id SEPARATOR ',') AS item_id,
GROUP_CONCAT(DISTINCT it.transaction_id SEPARATOR ',') AS transactions,
SUM(DISTINCT IF(ii.status = 1, ii.subtotal, 0)) AS subtotal,
SUM(DISTINCT it.subtotal) AS total_paid_amount,
SUM(DISTINCT it.additional_fee) AS total_additional_fee_amount,
SUM(ii.quantity) AS total_quantity,
(total_amount - SUM(DISTINCT COALESCE(it.amount, 0))) AS total_balance
FROM
`invoices` `i`
LEFT JOIN
`invoices_items` `ii` ON `ii`.`invoice_id` = `i`.`invoice_id`
LEFT JOIN
`invoices_transactions` `it` ON `it`.`invoice_id` = `i`.`invoice_id`
AND `it`.`process_status` = 1
AND `it`.`status` = 1
WHERE
`i`.`status` = '1'
GROUP BY `i`.`invoice_id`
ORDER BY `i`.`created_at` DESC
LIMIT 50
Query with EXPLAIN:
+----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+------------------------------+--------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+------------------------------+--------+----------+----------------------------------------------+
| 1 | SIMPLE | i | NULL | index | PRIMARY,customer_id,code,invoice_id_2,created_at | PRIMARY | 4 | NULL | 473309 | 10.00 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | ii | NULL | ref | invoice_id | invoice_id | 5 | test.i.invoice_id | 2 | 100.00 | NULL |
| 1 | SIMPLE | it | NULL | ref | invoice_id,status | invoice_id | 5 | test.i.invoice_id | 1 | 100.00 | Using where |
+----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+------------------------------+--------+----------+----------------------------------------------+
I try to remove SUM ,GROUP_CONCAT and WHERE CASE,
But still need 10 sec
SELECT
`i`.*
FROM
`invoices` `i`
LEFT JOIN
`invoices_items` `ii` ON `ii`.`invoice_id` = `i`.`invoice_id`
LEFT JOIN
`invoices_transactions` `it` ON `it`.`invoice_id` = `i`.`invoice_id`
GROUP BY `i`.`invoice_id`
ORDER BY `i`.`created_at` DESC
LIMIT 50
Create Table:
CREATE TABLE `invoices` (
`invoice_id` int NOT NULL AUTO_INCREMENT,
`warehouse_id` tinyint DEFAULT NULL,
`invoice_type` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i NOT NULL DEFAULT 'C',
`invoice_date` date DEFAULT NULL,
`customer_id` int DEFAULT NULL,
`contact_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`contact_no` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`contact_email` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`address` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`total_amount` deimal(10,2) DEFAULT '0.00',
`total_cost` deimal(20,2) DEFAULT NULL,
`delivery_type` tinyint DEFAULT '0',
`remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i,
`payment_status` tinyint(1) NOT NULL DEFAULT '3',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT NULL,
`updated_by` int NOT NULL,
`confirmed_at` datetime DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT '2'
PRIMARY KEY (`invoice_id`),
KEY `customer_id` (`customer_id`),
KEY `code` (`code`),
KEY `invoice_id_2` (`invoice_id`,`created_at`),
KEY `created_at` (`created_at`)
) ENGINE=InnoDB AUTO_INCREMENT=513697 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_i;
CREATE TABLE `invoices_items` (
`item_id` int NOT NULL AUTO_INCREMENT,
`invoice_id` int NOT NULL,
`warehouse_id` int DEFAULT NULL,
`product_id` int DEFAULT NULL,
`variant_id` int DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`quantity` int DEFAULT '1',
`unit_price` deimal(10,2) DEFAULT NULL,
`cost` deimal(10,2) DEFAULT NULL,
`subtotal` deimal(10,2) DEFAULT NULL,
`serial_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`StockoutDate` datetime DEFAULT NULL,
`ReturnDate` datetime DEFAULT NULL,
`Return_StockLocationID` tinyint DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT '1',
`priority` int NOT NULL DEFAULT '0',
PRIMARY KEY (`item_id`),
KEY `invoice_id` (`invoice_id`),
KEY `variant_id` (`variant_id`),
KEY `quantity` (`quantity`),
KEY `unit_price` (`unit_price`),
KEY `subtotal` (`subtotal`),
KEY `created_at` (`created_at`),
KEY `updated_at` (`updated_at`),
KEY `status` (`status`),
KEY `priority` (`priority`),
KEY `variant_id_2` (`variant_id`,`quantity`),
KEY `variant_id_3` (`variant_id`,`name`,`quantity`),
KEY `product_id` (`product_id`),
KEY `StockoutDate` (`StockoutDate`)
) ENGINE=InnoDB AUTO_INCREMENT=1200951 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_i;
CREATE TABLE `invoices_transactions` (
`transaction_id` int NOT NULL AUTO_INCREMENT,
`invoice_id` int DEFAULT NULL,
`warehouse_id` int DEFAULT NULL,
`invoice_date` date DEFAULT NULL,
`payment_id` int DEFAULT NULL,
`balance` deimal(10,2) DEFAULT NULL,
`amount` deimal(10,2) DEFAULT NULL,
`additional_rate` deimal(10,2) DEFAULT NULL,
`additional_fee` deimal(10,2) DEFAULT NULL,
`subtotal` deimal(10,2) DEFAULT NULL,
`process_status` int DEFAULT '1',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`payment_status` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT NULL,
`status` tinyint NOT NULL DEFAULT '1',
`CreatedByID` int DEFAULT NULL,
`ModifiedByID` int DEFAULT NULL,
`priority` int DEFAULT '0',
PRIMARY KEY (`transaction_id`),
KEY `invoice_id` (`invoice_id`),
KEY `payment_status` (`payment_status`),
KEY `created_at` (`created_at`),
KEY `updated_at` (`updated_at`),
KEY `status` (`status`),
KEY `amount` (`amount`),
KEY `additional_fee` (`additional_fee`),
KEY `subtotal` (`subtotal`),
KEY `transaction_id` (`transaction_id`,`amount`,`additional_fee`,`subtotal`),
KEY `invoice_id_2` (`invoice_id`,`process_status`,`status`),
KEY `process_status` (`process_status`),
KEY `transaction_id_2` (`transaction_id`,`invoice_id`,`amount`,`additional_fee`,`subtotal`,`process_status`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=543606 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_i;
Let's find the 50 invoices first, then reach for the rest of the stuff:
SELECT ... (all the current stuff)
FROM ( SELECT invoice_id
FROM invoices
WHERE status = 1
ORDER BY created_at DESC
LIMIT 50
) AS ids
JOIN invoices AS i USING(invoice_id)
LEFT JOIN ... ON ...
LEFT JOIN ... ON ...
GROUP BY invoice_id
ORDER BY created_id DESC
These composite indexes may help:
i: (status, created_at, invoice_id)
it: (status, process_status, invoice_id)
ii: (invoice_id, quantity, subtotal, status, item_id)
Redundant index:
KEY `variant_id` (`variant_id`) -- since this is the prefix of others
Because of PRIMARY KEY(transaction_id), the keys starting with transaction_id are redundant.
Summary:
I provided a "covering index" with the columns in the right order to allow finding the 50 ids directly from that index.
The rest of the work is limited to the 50 rows (or sets of rows, since I suspect that the rest are many:1 with respect to invoices.)
Your EXPLAIN shows lots of "Rows" and a "filesort" -- indicating that it gathered everything, then did the group by, then sorted, and finally, peeled of 50.
I suspect the "filesort" was really two sorts. See EXPLAIN FORMAT=JSON ... to find out.
I have a store system with orders, which have status for both the order and it's items. I have these six tables:
Order (pedido).
Order item (pedido_item).
Order status (status_pedido).
Order item status (status).
Order status log (pedido_status_pedido).
Order item status log (pedido_item_status).
So, when I have to update the status from the order, I insert a new row on pedido_status_pedido and I update the status_pedido_id on table pedido. Same goes for items.
One order status have many item status associated, for example, the order status "Pending" is related to the item statuses "Waiting for file", "File with error" and "File approved".
The current order status is based on it's current items status, on the most "delayed" status, for example, and item "File with error" is behind an "Finished production" item. For that there are ordenation columns both on order and item status.
So, if I have 3 items on an order, with the status "File with error", "In production", "Finished production", the order status is "Pending", because it's the correspondent order status for the order item status "File with error", which is further behind.
The problem is when I have to update an specific order status. I came up with a query that got so complex I need to SET SQL_BIG_SELECTS=1 to make it run. Obviously the query is so slow it's making my entire site slow (it's called every 10 minutes, for a large number of orders)
Here is my query, explained:
INSERT INTO pedido_status_pedido (pedido_id, status_pedido_id) VALUES ({$this->pedido_id}, ( --Insert into order status log the id, and the status id
SELECT sp.status_pedido_id FROM `status` s --Subquery for the order status id, get it from the relationship inside the item status
LEFT JOIN status_pedido sp ON sp.status_pedido_id = s.status_pedido_id
WHERE s.status_id = ( --Subquery for the further behind item status
SELECT s.status_id FROM pedido_item_status p1
LEFT JOIN `status` s ON s.status_id = p1.status_id
LEFT JOIN pedido_item ON pedido_item.pedido_item_id = p1.pedido_item_id
INNER JOIN ( --Get the LATEST status of each item and compare
SELECT MAX( si.sta_ordem ) AS maxordem, pedido_item_id FROM pedido_item_status pi
LEFT JOIN status si ON pi.status_id = si.status_id
WHERE pi.excluido IS NULL AND pi.pedido_id = {$this->pedido_id}
GROUP BY pi.pedido_item_id
) p2 ON ( s.sta_ordem = p2.maxordem ) AND p1.excluido IS NULL AND p1.pedido_item_id = p2.pedido_item_id
WHERE p1.pedido_id = {$this->pedido_id}
ORDER BY s.sta_ordem ASC
LIMIT 1
)
)
And here are the tables definitions (sorry, it's a bit large):
CREATE TABLE `pedido` (
`pedido_id` int(11) NOT NULL AUTO_INCREMENT,
`cliente_id` int(11) NULL DEFAULT NULL,
`forma_envio_id` int(11) NULL DEFAULT NULL,
`balcao_retirada_id` int(11) NULL DEFAULT NULL,
`ped_responsavel_retirada` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`ped_codigo_rastreio` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`ped_prazo_entrega` int(11) NULL DEFAULT NULL,
`ped_prazo_producao` int(11) NULL DEFAULT 0,
`ped_data` datetime(0) NULL DEFAULT '0000-00-00 00:00:00',
`ped_data_producao` date NULL DEFAULT NULL,
`ped_data_entrega` date NULL DEFAULT NULL,
`ped_notificado` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT 'N',
`ped_transacao_pagarme` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`ped_cmd` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT '',
`ped_data_confirmacao_pagamento` datetime(0) NULL DEFAULT NULL,
`forma_pagamento_id` int(11) NULL DEFAULT NULL,
`ped_vencimento_boleto` date NULL DEFAULT NULL,
`ped_comprovante` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`ped_pago` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT 'N',
`status_id` int(11) NOT NULL DEFAULT 0,
`status_pedido_id` int(11) NOT NULL DEFAULT 0,
`ped_valor_adicionais` decimal(10, 2) NULL DEFAULT NULL,
`ped_valor_frete` decimal(10, 2) NULL DEFAULT NULL,
`ped_valor_produtos` decimal(10, 2) NULL DEFAULT 0.00,
`ped_valor_desconto` decimal(10, 2) NULL DEFAULT 0.00,
`ped_valor_total` decimal(10, 2) NULL DEFAULT 0.00,
`excluido` datetime(0) NULL DEFAULT NULL,
`cadastrado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
`atualizado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (`pedido_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15876 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;
CREATE TABLE `pedido_item` (
`pedido_item_id` int(11) NOT NULL AUTO_INCREMENT,
`pedido_id` int(11) NULL DEFAULT NULL,
`pei_indice` int(11) NULL DEFAULT NULL,
`produto_id` int(11) NULL DEFAULT NULL,
`produto_variacao_id` int(11) NULL DEFAULT NULL,
`produto_preco_id` int(11) NULL DEFAULT NULL,
`tipo_arquivo_id` int(11) NULL DEFAULT NULL,
`pei_arquivo` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`pei_arquivo_nome` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`pei_nome` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`pei_quantidade` int(11) NULL DEFAULT NULL,
`pei_valor_unitario` decimal(10, 2) NULL DEFAULT NULL,
`pei_valor_total` decimal(10, 2) NULL DEFAULT NULL,
`pei_valor_frete` decimal(10, 2) NULL DEFAULT NULL,
`pei_codigo_preco` int(11) NULL DEFAULT NULL,
`pei_codigo_interno` varchar(40) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`status_id` int(11) NOT NULL DEFAULT 0,
`excluido` timestamp(0) NULL DEFAULT NULL,
`cadastrado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
`atualizado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
`pei_producao_finalizada` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT 'N',
`pei_arquivo_erro` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
PRIMARY KEY (`pedido_item_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 17528 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;
CREATE TABLE `pedido_item_status` (
`pedido_item_status_id` int(11) NOT NULL AUTO_INCREMENT,
`pedido_id` int(11) NOT NULL,
`pedido_item_id` int(11) NOT NULL,
`status_id` int(11) NOT NULL,
`pis_texto` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL,
`pis_notificar_cliente` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT 'S',
`excluido` datetime(0) NULL DEFAULT NULL,
`cadastrado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
`atualizado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (`pedido_item_status_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 35743 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;
CREATE TABLE `pedido_status_pedido` (
`pedido_status_pedido_id` int(11) NOT NULL AUTO_INCREMENT,
`pedido_id` int(11) NULL DEFAULT NULL,
`status_pedido_id` int(11) NULL DEFAULT NULL,
`psp_notificar_cliente` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT 'S',
`psp_texto` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL,
`excluido` timestamp(0) NULL DEFAULT NULL,
`cadastrado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
`atualizado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (`pedido_status_pedido_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 38216 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;
CREATE TABLE `status` (
`status_id` int(11) NOT NULL AUTO_INCREMENT,
`sta_nome` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`sta_observacao` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`sta_ordem` int(11) NULL DEFAULT NULL,
`status_pedido_id` int(11) NULL DEFAULT NULL,
`excluido` datetime(0) NULL DEFAULT NULL,
`cadastrado` datetime(0) NULL DEFAULT NULL,
`atualizado` datetime(0) NULL DEFAULT NULL,
`sta_cor` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`sta_icon` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`sta_alert` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
PRIMARY KEY (`status_id`) USING BTREE,
INDEX `idx_1`(`excluido`, `status_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;
INSERT INTO `status` VALUES (1, 'Aguardando pagamento', NULL, 1, 1, NULL, NULL, NULL, '#CACACA', 'fa-clock-o', 'alert-warning');
INSERT INTO `status` VALUES (2, 'Aguardando arquivo', NULL, 2, 2, NULL, NULL, NULL, '#FAC08C', 'fa-file-image-o', 'alert-warning');
INSERT INTO `status` VALUES (3, 'Arquivo em análise', NULL, 4, 2, NULL, NULL, NULL, '#8CBCFA', 'fa-spinner', 'alert-info');
INSERT INTO `status` VALUES (4, 'Produção finalizada', NULL, 9, 4, NULL, NULL, NULL, '#DCBCA5', 'check-square-o', 'alert-info');
INSERT INTO `status` VALUES (5, 'Arquivo com erro', NULL, 5, 2, NULL, NULL, NULL, '#FF8C8C', 'fa-exclamation-circle', 'alert-danger');
INSERT INTO `status` VALUES (6, 'Em produção', NULL, 7, 3, NULL, NULL, NULL, '#8CBCFA', 'fa-cogs', 'alert-info');
INSERT INTO `status` VALUES (7, 'Em transporte', NULL, 11, 5, NULL, NULL, NULL, '#DCA5A5', 'fa-truck', 'alert-info');
INSERT INTO `status` VALUES (8, 'Entregue', NULL, 12, 6, NULL, NULL, NULL, '#5CCE90', 'fa-check-circle-o', 'alert-success');
INSERT INTO `status` VALUES (9, 'Cancelado', NULL, 13, 7, NULL, NULL, NULL, '#FF7979', 'fa-times-circle-o', 'alert-danger');
INSERT INTO `status` VALUES (10, 'Pronto para retirada', NULL, 10, 4, NULL, NULL, NULL, '#FFD24D', 'fa-check-circle-o', 'alert-info');
INSERT INTO `status` VALUES (11, 'Produto com defeito', NULL, 8, 3, NULL, NULL, NULL, '#FF8C8C', 'fa-exclamation-circle', 'alert-danger');
INSERT INTO `status` VALUES (12, 'Arquivo aprovado', NULL, 6, 20, NULL, NULL, NULL, '#C0ED85', 'fa-check-circle-o', 'alert-info');
INSERT INTO `status` VALUES (13, 'Em Espera', NULL, 3, 1, NULL, NULL, NULL, '#8CBCFA', 'fa-spinner', 'alert-info');
CREATE TABLE `status_pedido` (
`status_pedido_id` int(11) NOT NULL AUTO_INCREMENT,
`stp_nome` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`stp_observacao` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`stp_ordem` int(11) NULL DEFAULT NULL,
`excluido` datetime(0) NULL DEFAULT NULL,
`cadastrado` datetime(0) NULL DEFAULT NULL,
`atualizado` datetime(0) NULL DEFAULT NULL,
`stp_cor` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`stp_icon` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`stp_alert` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
PRIMARY KEY (`status_pedido_id`) USING BTREE,
INDEX `idx_1`(`excluido`, `status_pedido_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;
INSERT INTO `status_pedido` VALUES (1, 'Aguardando pagamento', NULL, 1, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `status_pedido` VALUES (2, 'Pendente', NULL, 2, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `status_pedido` VALUES (3, 'Em produção', NULL, 4, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `status_pedido` VALUES (4, 'Pronto', NULL, 5, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `status_pedido` VALUES (5, 'Em transporte', NULL, 6, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `status_pedido` VALUES (6, 'Entregue', NULL, 7, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `status_pedido` VALUES (7, 'Cancelado', NULL, 8, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `status_pedido` VALUES (20, 'Aprovado', NULL, 3, NULL, NULL, NULL, NULL, NULL, NULL);
The query itself works fine, but I need a way to make it faster. Perhaps it would be better if I break it down on smaller queries? Or I'm using too much unnecessary data?
EDIT:
Following the answer, the new query now looks like this:
SELECT s.status_pedido_id AS status_pedido_atual, p.status_pedido_id AS status_pedido_anterior FROM `status` s
LEFT JOIN pedido p ON p.pedido_id = {$_POST['pedido_id']}
WHERE s.sta_ordem =
(
SELECT MAX( si.sta_ordem ) AS max_ordem
FROM pedido_item_status pis
LEFT JOIN `status` si ON si.status_id = pis.status_id
WHERE pis.pedido_id = {$_POST['pedido_id']}
AND pis.excluido IS NULL
GROUP BY pis.pedido_item_id
ORDER BY max_ordem ASC
LIMIT 1
)
And I insert the order status log on another query using the result. I got much faster. Also the were other problems with the logic of the whole process I solved.
Well, my guess is to run EXPLAIN ... and run it for your INSERT ... query
+---+----------+----------------------+--+--------+-------------+-------------+--+--+---+--------+----------------------------------------------+
| 1 | INSERT | pedido_status_pedido | | ALL | | | | | | | |
+---+----------+----------------------+--+--------+-------------+-------------+--+--+---+--------+----------------------------------------------+
| 2 | SUBQUERY | | | | | | | | | | no matching row in const table |
| 3 | SUBQUERY | p1 | | ALL | | | | | 1 | 100.00 | Using where; Using temporary; Using filesort |
| 3 | SUBQUERY | s | | eq_ref | PRIMARY | PRIMARY | | | 1 | 100.00 | Using where |
| 3 | SUBQUERY | pedido_item | | eq_ref | PRIMARY | PRIMARY | | | 1 | 100.00 | Using index |
| 3 | SUBQUERY | <derived4> | | ref | <auto_key0> | <auto_key0> | | | 2 | 100.00 | Using index |
| 4 | DERIVED | pi | | ALL | | | | | 1 | 100.00 | Using where; Using temporary; Using filesort |
| 4 | DERIVED | si | | eq_ref | PRIMARY | PRIMARY | | | 1 | 100.00 | |
+---+----------+----------------------+--+--------+-------------+-------------+--+--+---+--------+----------------------------------------------+
I ran with all your data and found that (possibly) you're having 3 of 7 subqueries with no possible key for searching
I would start de-compositing your query and checking them with EXPLAIN for possible bottlenecks.
Otherwise I ran this query (previosly I've filled some fake data in tables of course) and it performed for 0.016sec. That's nuisance. And that's why another guess is in backend code AND/OR volume of your data / your server capabilities.
So, at the end:
Check query with EXPLAIN
Decompose your complex query, check inner SELECTs for possible bottleneck
Change your logic to avoid bottlenecks - break complex queries into simple ones. For example, you can use trigger and set variable to store MAX( si.sta_ordem ) without recalculating it for every query. If you have constantly running service - you can use temporary tables for caching purposes.
If your MySQL query is ok, check you backend code (PHP). Maybe you're having a deadlock here.
Check you data volume.
If it's possible, reboot server and check how it's performing under load. Performance counters are available for all OSes
This is what I saw in slow-query.log
# User#Host: appUser[appUser] # [192.168.1.10]
# Query_time: 65.118800 Lock_time: 0.000257 Rows_sent: 7 Rows_examined: 78056
SET timestamp=1312883519;
select
this_.id as id2_0_,
this_.appearance_count as appearance2_2_0_,
this_.author as author2_0_,
this_.date_added as date4_2_0_,
this_.date_last_ranked as date5_2_0_,
this_.date_updated as date6_2_0_,
this_.description as descript7_2_0_,
this_.frequency_updated as frequency8_2_0_,
this_.hidden as hidden2_0_,
this_.link as link2_0_,
this_.lock_id as lock11_2_0_,
this_.plain_text_description as plain12_2_0_,
this_.processor_done as processor13_2_0_,
this_.processor_lock_id as processor14_2_0_,
this_.published_date as published15_2_0_,
this_.raw_link as raw16_2_0_,
this_.retweet_count as retweet17_2_0_,
this_.source_url as source18_2_0_,
this_.subtype as subtype2_0_,
this_.thumbnail_img_url as thumbnail20_2_0_,
this_.title as title2_0_,
this_.total_hit_count as total22_2_0_,
this_.total_lift_count as total23_2_0_,
this_.total_share_count as total24_2_0_,
this_.trend_value as trend25_2_0_,
this_.type as type2_0_
from
news this_
where
exists (select 1 from news_sub_topic where this_.id=news_sub_topics_id)
order by
this_.trend_value desc,
this_.retweet_count desc,
this_.total_lift_count desc,
this_.published_date desc
limit 7;
this is what EXPLAIN gives:
+----+--------------------+----------------+------+--------------------------------------+-------------------+---------+------------------------+-------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+----------------+------+--------------------------------------+-------------------+---------+------------------------+-------+-----------------------------+
| 1 | PRIMARY | this_ | ALL | NULL | NULL | NULL | NULL | 50910 | Using where; Using filesort |
| 2 | DEPENDENT SUBQUERY | news_sub_topic | ref | FK420C980470B0479,FK420C98041F791FA1 | FK420C980470B0479 | 9 | causelift_web.this_.id | 1 | Using where; Using index |
+----+--------------------+----------------+------+--------------------------------------+-------------------+---------+------------------------+-------+-----------------------------+
2 rows in set (3.28 sec)
this is the CREATE sytax for the news and news_sub_topic table:
CREATE TABLE `news` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`appearance_count` int(11) NOT NULL,
`author` longtext COLLATE utf8_unicode_ci,
`date_added` datetime NOT NULL,
`date_updated` datetime DEFAULT NULL,
`description` longtext COLLATE utf8_unicode_ci,
`link` longtext COLLATE utf8_unicode_ci,
`published_date` datetime NOT NULL,
`raw_link` longtext COLLATE utf8_unicode_ci,
`subtype` varchar(5) COLLATE utf8_unicode_ci NOT NULL,
`thumbnail_img_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`title` longtext COLLATE utf8_unicode_ci,
`type` varchar(5) COLLATE utf8_unicode_ci NOT NULL,
`date_last_ranked` datetime DEFAULT NULL,
`lock_id` int(11) DEFAULT NULL,
`plain_text_description` longtext COLLATE utf8_unicode_ci,
`source_url` longtext COLLATE utf8_unicode_ci,
`retweet_count` int(11) DEFAULT NULL,
`frequency_updated` bit(1) DEFAULT NULL,
`hidden` bit(1) DEFAULT NULL,
`processor_done` bit(1) DEFAULT NULL,
`processor_lock_id` int(11) DEFAULT NULL,
`total_hit_count` int(11) DEFAULT NULL,
`total_lift_count` int(11) DEFAULT NULL,
`total_share_count` int(11) DEFAULT NULL,
`trend_value` float DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `link_idx` (`link`(255)),
KEY `type_idx` (`type`),
KEY `processor_lock_id_idx` (`processor_lock_id`),
KEY `processor_done_idx` (`processor_done`),
KEY `hidden_idx` (`hidden`),
KEY `title_idx` (`title`(255)),
KEY `published_date_idx` (`published_date`)
) ENGINE=InnoDB AUTO_INCREMENT=321136 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `news_sub_topic` (
`news_sub_topics_id` bigint(20) DEFAULT NULL,
`sub_topic_id` bigint(20) DEFAULT NULL,
KEY `FK420C98045D8019D4` (`sub_topic_id`),
KEY `FK420C980470B0479` (`news_sub_topics_id`),
CONSTRAINT `FK420C98041F791FA1` FOREIGN KEY (`news_sub_topics_id`) REFERENCES `news` (`id`),
CONSTRAINT `FK420C98045D8019D4` FOREIGN KEY (`sub_topic_id`) REFERENCES `sub_topic` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
anyone can explain why sometimes it took 65 seconds for this query to finish? but sometimes it is fast? what other factors should i look into (DB config, OS, etc.).
thanks for any leads on this.
my feeling is the 'WHERE' clause is your problem.
Try this code instead:
from
news this_
inner join news_sub_topic topic on this.id=topic.news_sub_topics_id
The explanation is the because the lookup against news is using a filesort and not an index, likely because the where clause isn't giving a good hint to the optimize.