Optimize multiple JOINs in MySQL - mysql

I have this MySQL which should be correct syntax:
select c.cat_id,c.cat_name as cat_name,
c.cat_desc, c.cat_image, mi.filename,
l.link_id, l.user_id, l.address,l.city,
l.country,l.link_created,l.link_desc,
l.email,l.fax,l.link_hits, l.link_modified,
l.link_name,l.postcode, l.price,l.link_rating,
l.state,l.telephone,l.link_votes,
l.website, l.link_id, l.link_visited, cf.value
from j25_mt_cats as c,
j25_mt_links as l
LEFT OUTER JOIN j25_mt_cfvalues AS cf ON (cf.link_id = l.link_id),
j25_mt_images AS mi,
j25_mt_cl as cl
UNION ALL
select c.cat_id,c.cat_name as cat_name,
c.cat_desc, c.cat_image, mi.filename,
l.link_id, l.user_id, l.address,l.city,
l.country,l.link_created,l.link_desc,
l.email,l.fax,l.link_hits, l.link_modified,
l.link_name,l.postcode, l.price,l.link_rating,
l.state,l.telephone,l.link_votes,
l.website, l.link_id, l.link_visited, cf.value
FROM j25_mt_cats as c,
j25_mt_links as l
RIGHT OUTER JOIN j25_mt_cfvalues AS cf ON cf.link_id = l.link_id,
j25_mt_images AS mi,
j25_mt_cl as cl
where cf.cf_id = 40 and cl.link_id = l.link_id
AND mi.link_id = l.link_id AND mi.ordering < 2
AND c.cat_id = cl.cat_id and c.cat_published = 1
AND c.cat_approved = 1 and l.link_published = 1 and l.link_approved = 1
AND cf.link_id IS NULL;
The query eats up 3GB+ in the tmp directory and ends up timing out. I'm missing something here, how can I increase the efficiency? My goal here was just adding onto an existing query to grab a value from an additional table (j25_mt_cfvalues).
explain:
+----+--------------+------------+-------+---------------+---------+---------+--------------------------+------+------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+------------+-------+---------------+---------+---------+--------------------------+------+------------------+
| 1 | PRIMARY | mi | ALL | NULL | NULL | NULL | NULL | 165 | |
| 1 | PRIMARY | c | ALL | NULL | NULL | NULL | NULL | 301 | |
| 1 | PRIMARY | l | ALL | NULL | NULL | NULL | NULL | 2139 | |
| 1 | PRIMARY | cf | ref | link_id | link_id | 4 | db_table.l.link_id | 2 | |
| 1 | PRIMARY | cl | index | NULL | PRIMARY | 4 | NULL | 2742 | Using index |
| 2 | UNION | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
| NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+------------+-------+---------------+---------+---------+--------------------------+------+------------------+
j25_mt_cats schema:
CREATE TABLE j25_mt_cats (
cat_id int(11) NOT NULL auto_increment,
cat_name varchar(255) NOT NULL,
alias varchar(255) NOT NULL,
title varchar(255) NOT NULL,
cat_desc text NOT NULL,
cat_parent int(11) NOT NULL default '0',
cat_links int(11) NOT NULL default '0',
cat_cats int(11) NOT NULL default '0',
cat_featured tinyint(4) NOT NULL default '0',
cat_image varchar(255) NOT NULL,
cat_published tinyint(4) NOT NULL default '0',
cat_created datetime NOT NULL default '0000-00-00 00:00:00',
cat_approved tinyint(4) NOT NULL default '0',
cat_template varchar(255) NOT NULL default '',
cat_usemainindex tinyint(4) NOT NULL default '0',
cat_allow_submission tinyint(4) NOT NULL default '1',
cat_show_listings tinyint(3) unsigned NOT NULL default '1',
metakey text NOT NULL,
metadesc text NOT NULL,
ordering int(11) NOT NULL default '0',
lft int(11) NOT NULL default '0',
rgt int(11) NOT NULL default '0',
PRIMARY KEY (cat_id),
KEY cat_id (cat_id,cat_published,cat_approved),
KEY cat_parent (cat_parent,cat_published,cat_approved,cat_cats,cat_links),
KEY dtree (cat_published,cat_approved),
KEY lft_rgt (lft,rgt),
KEY func_getPathWay (lft,rgt,cat_id,cat_parent),
KEY alias (alias)
) ENGINE=MyISAM AUTO_INCREMENT=3851 DEFAULT CHARSET=utf8 |
j25_mt_links schema:
CREATE TABLE j25_mt_links (
link_id int(11) NOT NULL auto_increment,
link_name varchar(255) NOT NULL,
alias varchar(255) NOT NULL,
link_desc mediumtext NOT NULL,
user_id int(11) NOT NULL default '0',
link_hits int(11) NOT NULL default '0',
link_votes int(11) NOT NULL default '0',
link_rating decimal(7,6) unsigned NOT NULL default '0.000000',
link_featured smallint(6) NOT NULL default '0',
link_published tinyint(4) NOT NULL default '0',
link_approved int(4) NOT NULL default '0',
link_template varchar(255) NOT NULL,
attribs text NOT NULL,
metakey text NOT NULL,
metadesc text NOT NULL,
internal_notes text NOT NULL,
ordering int(11) NOT NULL default '0',
link_created datetime NOT NULL default '0000-00-00 00:00:00',
publish_up datetime NOT NULL default '0000-00-00 00:00:00',
publish_down datetime NOT NULL default '0000-00-00 00:00:00',
link_modified datetime NOT NULL default '0000-00-00 00:00:00',
link_visited int(11) NOT NULL default '0',
address varchar(255) NOT NULL,
city varchar(255) NOT NULL,
state varchar(255) NOT NULL,
country varchar(255) NOT NULL,
postcode varchar(255) NOT NULL,
telephone varchar(255) NOT NULL,
fax varchar(255) NOT NULL,
email varchar(255) NOT NULL,
website varchar(255) NOT NULL,
price double(9,2) NOT NULL default '0.00',
lat float(10,6) NOT NULL COMMENT 'Latitude',
lng float(10,6) NOT NULL COMMENT 'Longitude',
zoom tinyint(3) unsigned NOT NULL COMMENT 'Map''s zoom level',
PRIMARY KEY (link_id),
KEY link_rating (link_rating),
KEY link_votes (link_votes),
KEY link_name (link_name),
KEY publishing (link_published,link_approved,publish_up,publish_down),
KEY count_listfeatured (link_published,link_approved,link_featured,publish_up,publish_down,link_id),
KEY count_viewowner (link_published,link_approved,user_id,publish_up,publish_down),
KEY mylisting (user_id,link_id),
FULLTEXT KEY link_name_desc (link_name,link_desc)
) ENGINE=MyISAM AUTO_INCREMENT=3229 DEFAULT CHARSET=utf8 |
j25_mt_cfvalues schema:
CREATE TABLE j25_mt_cfvalues (
id int(11) NOT NULL auto_increment,
cf_id int(11) NOT NULL,
link_id int(11) NOT NULL,
value mediumtext NOT NULL,
attachment int(10) unsigned NOT NULL default '0',
counter int(11) NOT NULL default '0',
PRIMARY KEY (id),
KEY cf_id (cf_id,link_id),
KEY link_id (link_id),
KEY value (value(8))
) ENGINE=MyISAM AUTO_INCREMENT=20876 DEFAULT CHARSET=utf8 |

The problem is that your first SQL query does not have any WHERE criteria and is causing a global cartesian across EVERY table your are working. Only in the second query does the WHERE clause get applied.
That said, you had a left and right join for the CF table, but you can't have both a cf=40 and cf IS NULL, so I simplified to just a left join on the ID AND 40... so if there IS a record in the CF table it only shows if it's value is 40... any other value would be ignored..
that said, your query can be simplified down to a single query. I also changed to JOIN syntax instead of WHERE so you and others can see the relation to the tables vs guessing.
select
(all your fields)
from
j25_mt_cats as c
JOIN j25_mt_cl as cl
ON c.cat_id = cl.cat_id
JOIN j25_mt_links as l
ON cl.link_id = l.link_id
AND l.link_published = 1
AND l.link_approved = 1
JOIN j25_mt_images AS mi
ON l.link_id = mi.link_id
AND mi.ordering < 2
LEFT OUTER JOIN j25_mt_cfvalues AS cf
ON l.link_id = cf.link_id
AND cf.cf_id = 40
where
c.cat_published = 1
AND c.cat_approved = 1
ORDER BY
RAND() DESC;
TO help optimize the query, your
j25_mt_cats should have an index on (cat_published, cat_approved)
j25_mt_cl on (cat_id)
j25_mt_links on (link_id, link_published, link_approved)
j25_mt_images on (link_id, ordering)
j25_mt_cfvalues on (link_id, cf_id)

Related

need help in query optimization

I have below query where it is taking long time to execute as it is having OR opeartion.Here Institution 59958 is a global institution. It can have stats itself and its children can have stats; so 'parentinstitutionid' with 59958 or institutionid 59958 we require usage data for.so I am using OR opeartor.
select date(SUBDATE(m.timestamp, INTERVAL (day(m.timestamp) -1) day)) as month,
count(*) as c,
sum(case when a.streamid = 5 then 1 else 0 end) as education,
sum(case when a.streamid in(7, 1) then 1 else 0 end) as research,
sum(case when searchterms <> '' then 1 else 0 end) as search
from stats_to_institution as s
join masterstats_innodb as m on s.statid = m.id
left join articles as a on (a.productid >= 49 and a.productid = m.article)
where m.timestamp >= '2022-01-01'
and (s.institutionid = 59958 or s.institutionid in ( select institutionid from institutions where parentinstitutionid = 59958))
group by month;
Here below condition is taking time
(s.institutionid = 59958 or s.institutionid in (select institutionid from institutions where parentinstitutionid = 59958))
I cannot use CTE as it is on 5.6 version.Is any other way to modify above condition for good performance?.
If I remove s.institutionid = 59958 it takes only 5 secs to run as it will not have OR operator.
Any suggestion on this?
table structure as follows
CREATE TABLE `institutions` (
`InstitutionID` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(200) DEFAULT NULL,
`Approved` tinyint(1) NOT NULL DEFAULT '0',
`DateAdded` datetime DEFAULT CURRENT_TIMESTAMP,
`IsAcademic` tinyint(1) DEFAULT NULL,
`IsIndustry` tinyint(1) DEFAULT NULL,
`LogoFile` varchar(50) DEFAULT NULL,
`NotifyLibEveryXRequests` int(11) DEFAULT NULL,
`IsParentInstitution` int(1) NOT NULL DEFAULT '0',
`ParentInstitutionID` int(11) DEFAULT NULL,
PRIMARY KEY (`InstitutionID`),
KEY `Institutions_Name` (`Name`),
KEY `ParentInstitutionID` (`ParentInstitutionID`),
FULLTEXT KEY `Name` (`Name`)
) ;
CREATE TABLE `masterstats_innodb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`page` text COLLATE utf8_unicode_ci NOT NULL,
`video` int(11) NOT NULL,
`language` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`referrer` text COLLATE utf8_unicode_ci NOT NULL,
`joveuser` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
`timestamp` date NOT NULL DEFAULT '0000-00-00',
PRIMARY KEY (`id`,`timestamp`),
KEY `joveuser` (`joveuser`),
KEY `institutionid` (`institutionid`),
KEY `timestamp` (`timestamp`),
KEY `idx__video_timestamp` (`video`,`timestamp`)
) ;
CREATE TABLE `stats_to_institution` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`statid` int(11) NOT NULL,
`institutionid` int(11) NOT NULL,
PRIMARY KEY (`id`,`institutionid`),
UNIQUE KEY `statid_2` (`statid`,`institutionid`),
KEY `statid` (`statid`),
KEY `institutionid` (`institutionid`)
) ;
CREATE TABLE `articles` (
`ProductID` int(11) NOT NULL,
`Name` varchar(1000) DEFAULT NULL,
`Tags` varchar(1000) NOT NULL,
`D` varchar(2000) DEFAULT NULL,
`Active` tinyint(1) DEFAULT NULL,
`UserID` int(11) DEFAULT NULL,
`DateAdded` datetime DEFAULT NULL,
`Detail_Abstract` text,
`StreamID` int(11) DEFAULT NULL COMMENT '-1 = Errata, 1= Article, 2= Advertisment, 3 = Editorial, 4= Junk, 5=SE',
`DatePublished` datetime DEFAULT NULL,
`AccessType` int(11) DEFAULT NULL COMMENT '-1=Unpublished, 0=Closed, 1=Free, 2=Open, 3 = Open UK',
`Rep_Results` text,
`Stage` int(11) DEFAULT NULL,
`SectionID` int(11) DEFAULT NULL,
PRIMARY KEY (`ProductID`),
KEY `Articles_StreamID_Active_DatePublished` (`StreamID`,`Active`,`DatePublished`),
KEY `articles_idx_sectionid` (`SectionID`),
FULLTEXT KEY `DetailAbstractTest` (`Detail_Abstract`,`Name`),
FULLTEXT KEY `Materials` (`Materials`),
FULLTEXT KEY `title` (`Name`)
);
explain result
+----+-------------+--------------+--------+------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------+-----------------+----------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------+-----------------+----------+----------------------------------------------+
| 1 | PRIMARY | m | ALL | PRIMARY,timestamp_video,joveuser,institutionid,video_institutionid,user_id,ip_binary,time_on_page,Article,timestamp,idx__video_timestamp | NULL | NULL | NULL | 19653526 | Using where; Using temporary; Using filesort |
| 1 | PRIMARY | a | eq_ref | PRIMARY | PRIMARY | 4 | stats.m.Article | 1 | Using where |
| 1 | PRIMARY | s | ref | statid_2,statid,institutionid | statid_2 | 4 | stats.m.id | 1 | Using where; Using index |
| 2 | SUBQUERY | institutions | ref | PRIMARY,ParentInstitutionID | ParentInstitutionID | 5 | const | 173 | Using index |
+----+-------------+--------------+--------+------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------+-----------------+----------+----------------------------------------------+
No CTE how about join?
select date(SUBDATE(m.timestamp, INTERVAL (day(m.timestamp) -1) day)) as month,
count(*) as c,
sum(case when a.streamid = 5 then 1 else 0 end) as education,
sum(case when a.streamid in(7, 1) then 1 else 0 end) as research,
sum(case when searchterms <> '' then 1 else 0 end) as search
from stats_to_institution as s
join (select institutionid
from institutions
where parentinstitutionid = 59958
union all select 59998
) x on x.institutionid = s.institutionid
join masterstats_innodb as m on s.statid = m.id
left join articles as a on (a.productid >= 49 and a.productid = m.article)
where m.timestamp >= '2022-01-01'
group by month;

How to optimize the below query and what is (Using where; Using join buffer (Block Nested Loop)) With EXPLAIN

I have been facing an issue with the query. whenever I try to select the query it taking 10 to 15 seconds to execute. and what is (Using where; Using join buffer (Block Nested Loop)) in the explain
The query is
SELECT wp_posts.ID, post_title, post_content, wp_pvc_total.postcount,
wp_pvc_total.postnum
FROM wp_posts
LEFT JOIN wp_term_relationships
ON ( wp_posts.ID =
wp_term_relationships.object_id )
LEFT JOIN wp_term_taxonomy
ON ( wp_term_relationships.term_taxonomy_id =
wp_term_taxonomy.term_taxonomy_id )
LEFT JOIN wp_pvc_total ON ( wp_pvc_total.postnum = wp_posts.ID )
WHERE wp_posts.post_author = 630
AND wp_posts.post_author NOT IN(675)
AND wp_posts.ID != 48075
GROUP BY wp_posts.ID
ORDER BY wp_pvc_total.postcount DESC
LIMIT 0, 9;
Query with explain
+----+-------------+-----------------------+--------+---------------------------------------------------------------------------+-------------+---------+-------------------------------------------------------+-------+--------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------------+--------+---------------------------------------------------------------------------+-------------+---------+-------------------------------------------------------+-------+--------------------------------------------------------+
| 1 | SIMPLE | wp_posts | range | PRIMARY,post_name,type_status_date,post_parent,post_author,idx_post_title | post_author | 16 | NULL | 1682 | Using index condition; Using temporary; Using filesort |
| 1 | SIMPLE | wp_term_relationships | ref | PRIMARY | PRIMARY | 8 | marriai1_topic.wp_posts.ID | 1 | Using index |
| 1 | SIMPLE | wp_term_taxonomy | eq_ref | PRIMARY | PRIMARY | 8 | marriai1_topic.wp_term_relationships.term_taxonomy_id | 1 | Using index |
| 1 | SIMPLE | wp_pvc_total | ALL | NULL | NULL | NULL | NULL | 19670 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-----------------------+--------+---------------------------------------------------------------------------+-------------+---------+-------------------------------------------------------+-------+--------------------------------------------------------+
The MySQL Version is 5.6, Innodb Engine and The table structures are
CREATE TABLE `wp_posts` (
`ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`post_author` bigint(20) unsigned NOT NULL DEFAULT '0',
`post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`post_date_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`post_content` longtext NOT NULL,
`post_title` text NOT NULL,
`post_excerpt` text NOT NULL,
`post_status` varchar(20) NOT NULL DEFAULT 'publish',
`comment_status` varchar(20) NOT NULL DEFAULT 'open',
`ping_status` varchar(20) NOT NULL DEFAULT 'open',
`post_password` varchar(255) NOT NULL DEFAULT '',
`post_name` varchar(200) NOT NULL DEFAULT '',
`to_ping` text NOT NULL,
`pinged` text NOT NULL,
`post_modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`post_modified_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`post_content_filtered` longtext NOT NULL,
`post_parent` bigint(20) unsigned NOT NULL DEFAULT '0',
`guid` varchar(255) NOT NULL DEFAULT '',
`menu_order` int(11) NOT NULL DEFAULT '0',
`post_type` varchar(20) NOT NULL DEFAULT 'post',
`post_mime_type` varchar(100) NOT NULL DEFAULT '',
`comment_count` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`),
KEY `post_name` (`post_name`(191)),
KEY `type_status_date` (`post_type`,`post_status`,`post_date`,`ID`),
KEY `post_parent` (`post_parent`),
KEY `post_author` (`post_author`),
FULLTEXT KEY `idx_post_title` (`post_title`)
)
and
CREATE TABLE `wp_pvc_total` (
`id` mediumint(9) NOT NULL AUTO_INCREMENT,
`postnum` varchar(255) NOT NULL,
`postcount` int(11) NOT NULL DEFAULT '750',
UNIQUE KEY `id` (`id`)
)
Add INDEX(postnum, postcount)
That will make reaching into wp_pvc_total more efficient. And it is "covering".
BNL is quite efficient, so I am no sure that this index will be better. But it seems like having no useful index is inefficient.
On a separate issue, KEY post_name (post_name(191)) is problematic. See http://mysql.rjweb.org/doc.php/limits#767_limit_in_innodb_indexes

Optimizing a MySQL query on big tables

I have a SQL query with 3 tables joined on a distant MySQL DB- Two of these tables have size of about 18GByte (STEP_RESULT and meas_numericlimit) and then the distant server create a TMP table which takes age (about 25 min) to end
How can I optimize this query ?
select
t1.UUT_NAME,
t1.STATION_NUM,
t1.START_DATE_TIME,
t3.LOW_LIMIT,
t3.DATA,
t3.HIGH_LIMIT,
t3.UNITS,
t2b.STEP_NAME
from
meas_numericlimit t3
inner join STEP_RESULT t2a on t3.ID = t2a.STEP_ID
inner join STEP_RESULT t2b on t2a.STEP_PARENT = t2b.STEP_ID
inner join uut_result t1 on t2b.UUT_RESULT = t1.ID
where
t1.UUT_NAME like 'Variable1-1%' and
t1.STATION_NUM = 'variable2' and
t2b.STEP_NAME = 'variable3' and
t2b.STEP_TYPE = 'constant'
Here the SHOW TABLES and EXPLAIN output queries :
+--------------------+
| Tables_in_spectrum |
+--------------------+
| cal_dates |
| calibrage |
| execution_time |
| meas_numericlimit |
| station_feature |
| step_callexe |
| step_graph |
| step_msgjnl |
| step_msgpopup |
| step_passfail |
| step_result |
| step_seqcall |
| step_stringvalue |
| syst_event |
| uptime |
| users |
| uut_result |
+--------------------+
and
+----+-------------+-------+--------+-------------------------+---------+
| id | select_type | table | type | possible_keys | key |
+----+-------------+-------+--------+-------------------------+---------+
| 1 | SIMPLE | t2a | ALL | NULL | NULL |
| 1 | SIMPLE | t3 | eq_ref | PRIMARY | PRIMARY |
| 1 | SIMPLE | t2b | ALL | NULL | NULL |
| 1 | SIMPLE | t1 | eq_ref | PRIMARY,FK_uut_result_1 | PRIMARY |
+----+-------------+-------+--------+-------------------------+---------+
---------+----------------------+----------- +---------------------------+
key_len | ref | rows | Extra |
---------+----------------------+----------- +---------------------------+
NULL | NULL | 48120004 | |
40 | spectrum.t2a.STEP_ID | 1 | |
NULL | NULL | 48120004 | Using where; Using join
buffer |
40 | spectrum.t2b.UUT_RESULT | 1 | Using where |
-------+----------------------+------------+---------------------------+
Here the SHOW CREATE TABLE :
CREATE TABLE `uut_result` (
`ID` varchar(38) NOT NULL DEFAULT '',
`STATION_NUM` varchar(255) DEFAULT NULL,
`SOFTVER_ODTGEN` varchar(10) DEFAULT NULL,
`HARDVER_ODTGEN` varchar(10) DEFAULT NULL,
`NEXT_CAL_DATE` date DEFAULT NULL,
`UUT_NAME` varchar(255) DEFAULT NULL,
`UUT_SERIAL_NUMBER` varchar(255) DEFAULT NULL,
`UUT_VERSION` varchar(255) DEFAULT NULL,
`USER_LOGIN_NAME` varchar(255) DEFAULT NULL,
`USER_LOGIN_LOGIN` varchar(255) NOT NULL DEFAULT '',
`START_DATE_TIME` datetime DEFAULT NULL,
`EXECUTION_TIME` float DEFAULT NULL,
`UUT_STATUS` varchar(255) DEFAULT NULL,
`UUT_ERROR_CODE` int(11) DEFAULT NULL,
`UUT_ERROR_MESSAGE` varchar(1023) DEFAULT NULL,
`PAT_NAME` varchar(255) NOT NULL DEFAULT '',
`PAT_VERSION` varchar(10) NOT NULL DEFAULT '',
`TEST_LEVEL` varchar(50) DEFAULT NULL,
`INTERFACE_ID` int(10) unsigned NOT NULL DEFAULT '0',
`EXECUTION_MODE` varchar(45) DEFAULT NULL,
`LOOP_MODE` varchar(45) DEFAULT NULL,
`STOP_ON_FAIL` tinyint(4) unsigned NOT NULL DEFAULT '0',
`EXECUTION_COMMENT` text,
PRIMARY KEY (`ID`),
KEY `FK_uut_result_1` (`STATION_NUM`)
) ENGINE=MyISAM DEFAULT CHARSET=latin;
and
CREATE TABLE `meas_numericlimit` (
`ID` varchar(38) NOT NULL DEFAULT '',
`STEP_RESULT` varchar(38) NOT NULL DEFAULT '',
`NAME` varchar(255) DEFAULT NULL,
`COMP_OPERATOR` varchar(30) DEFAULT NULL,
`HIGH_LIMIT` double DEFAULT NULL,
`LOW_LIMIT` double DEFAULT NULL,
`UNITS` varchar(255) DEFAULT NULL,
`DATA` double DEFAULT NULL,
`STATUS` varchar(255) DEFAULT NULL,
`FORMAT` varchar(15) DEFAULT NULL,
`NANDATA` int(11) DEFAULT '0',
PRIMARY KEY (`ID`),
KEY `FK_meas_numericlimit_1` (`STEP_RESULT`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
and
CREATE TABLE `step_result` (
`ID` varchar(38) NOT NULL DEFAULT '',
`UUT_RESULT` varchar(38) NOT NULL DEFAULT '',
`STEP_PARENT` varchar(38) DEFAULT NULL,
`STEP_NAME` varchar(255) DEFAULT NULL,
`STEP_ID` varchar(38) NOT NULL DEFAULT '',
`STEP_TYPE` varchar(255) DEFAULT NULL,
`STATUS` varchar(255) DEFAULT NULL,
`REPORT_TEXT` text,
`DIAG` text,
`ERROR_OCCURRED` tinyint(1) NOT NULL DEFAULT '0',
`ERROR_CODE` int(11) DEFAULT NULL,
`ERROR_MESSAGE` varchar(1023) DEFAULT NULL,
`MODULE_TIME` float DEFAULT NULL,
`TOTAL_TIME` float DEFAULT NULL,
`NUM_LOOPS` int(11) DEFAULT NULL,
`NUM_PASSED` int(11) DEFAULT NULL,
`NUM_FAILED` int(11) DEFAULT NULL,
`ENDING_LOOP_INDEX` int(11) DEFAULT NULL,
`LOOP_INDEX` int(11) DEFAULT NULL,
`INTERACTIVE_EXENUM` int(11) DEFAULT NULL,
`STEP_GROUP` varchar(30) DEFAULT NULL,
`STEP_INDEX` int(11) DEFAULT NULL,
`ORDER_NUMBER` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `FK_step_result_1` (`UUT_RESULT`),
KEY `IDX_step_parent` (`STEP_PARENT`)
) ENGINE=MyISAM DEFAULT CHARSET=latin
Thank you for your help
What is the value of join_buffer_size? It should not be more than about 1% of RAM. If it is much bigger, you run the risk of swapping, which is especially bad for performance.
One thing jumps out in the EXPLAIN: NULL | 48120004 saying that this is needed: INDEX(STEP_ID);
However, the SELECT and the EXPLAIN do not seem to match. Please double check.
uut_result needs INDEX(station_num, uut_name) -- in that order; replaces just (station_num).
What is varchar(38)? UUIDs are only 36. IPv6 needs 39.
UUIDs are terribly inefficient when the data is too big to be cached. More discussion: http://mysql.rjweb.org/doc.php/uuid
Lots of datatypes could (should) be shrunken -- this shrinkage will cut down on I/O, which will speed up queries. If you provide some sample values for some typical columns, I can give more advice.
For example, STATUS is (usually) a small number of distinct values. That could be represented as a 1-byte ENUM or a 1-byte TINYINT; but maybe your app has hundreds of different status values? If so, "normalizing" it may be the better answer.
DOUBLE takes 8 bytes; FLOAT takes only 4 bytes, but limits precision to only ~7 significant digits -- perhaps that is sufficient?
(Presumably you meant latin1, not latin?)
Also consider switching to InnoDB.
How much RAM do you have? How big (GB) are the tables?

How to optimize this query in MySQL

I have these two tables (Moodle 2.8):
CREATE TABLE `mdl_course` (
`id` bigint(10) NOT NULL AUTO_INCREMENT,
`category` bigint(10) NOT NULL DEFAULT '0',
`sortorder` bigint(10) NOT NULL DEFAULT '0',
`fullname` varchar(254) NOT NULL DEFAULT '',
`shortname` varchar(255) NOT NULL DEFAULT '',
`idnumber` varchar(100) NOT NULL DEFAULT '',
`summary` longtext,
`summaryformat` tinyint(2) NOT NULL DEFAULT '0',
`format` varchar(21) NOT NULL DEFAULT 'topics',
`showgrades` tinyint(2) NOT NULL DEFAULT '1',
`newsitems` mediumint(5) NOT NULL DEFAULT '1',
`startdate` bigint(10) NOT NULL DEFAULT '0',
`marker` bigint(10) NOT NULL DEFAULT '0',
`maxbytes` bigint(10) NOT NULL DEFAULT '0',
`legacyfiles` smallint(4) NOT NULL DEFAULT '0',
`showreports` smallint(4) NOT NULL DEFAULT '0',
`visible` tinyint(1) NOT NULL DEFAULT '1',
`visibleold` tinyint(1) NOT NULL DEFAULT '1',
`groupmode` smallint(4) NOT NULL DEFAULT '0',
`groupmodeforce` smallint(4) NOT NULL DEFAULT '0',
`defaultgroupingid` bigint(10) NOT NULL DEFAULT '0',
`lang` varchar(30) NOT NULL DEFAULT '',
`theme` varchar(50) NOT NULL DEFAULT '',
`timecreated` bigint(10) NOT NULL DEFAULT '0',
`timemodified` bigint(10) NOT NULL DEFAULT '0',
`requested` tinyint(1) NOT NULL DEFAULT '0',
`enablecompletion` tinyint(1) NOT NULL DEFAULT '0',
`completionnotify` tinyint(1) NOT NULL DEFAULT '0',
`cacherev` bigint(10) NOT NULL DEFAULT '0',
`calendartype` varchar(30) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `mdl_cour_cat_ix` (`category`),
KEY `mdl_cour_idn_ix` (`idnumber`),
KEY `mdl_cour_sho_ix` (`shortname`),
KEY `mdl_cour_sor_ix` (`sortorder`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `mdl_log` (
`id` bigint(10) NOT NULL AUTO_INCREMENT,
`time` bigint(10) NOT NULL DEFAULT '0',
`userid` bigint(10) NOT NULL DEFAULT '0',
`ip` varchar(45) NOT NULL DEFAULT '',
`course` bigint(10) NOT NULL DEFAULT '0',
`module` varchar(20) NOT NULL DEFAULT '',
`cmid` bigint(10) NOT NULL DEFAULT '0',
`action` varchar(40) NOT NULL DEFAULT '',
`url` varchar(100) NOT NULL DEFAULT '',
`info` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `mdl_log_coumodact_ix` (`course`,`module`,`action`),
KEY `mdl_log_tim_ix` (`time`),
KEY `mdl_log_act_ix` (`action`),
KEY `mdl_log_usecou_ix` (`userid`,`course`),
KEY `mdl_log_cmi_ix` (`cmid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And this query:
SELECT l.id,
l.userid AS participantid,
l.course AS courseid,
l.time,
l.ip,
l.action,
l.info,
l.module,
l.url
FROM mdl_log l
INNER JOIN mdl_course c ON l.course = c.id AND c.category <> 0
WHERE
l.id > [some large id]
AND
l.time > [some unix timestamp]
ORDER BY l.id ASC
LIMIT 0,200
mdl_log table has over 200 milion records, and I need to export it into file using PHP and not die in intent. The main problem here is that executing this is too slow. The main killer here is the join to the mdl_course table. If I remove it, everything works fast.
Here is the explain:
+----+-------------+-------+-------+---------------------------------------------+----------------------+---------+----------------+------+-----------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------------------------------------+----------------------+---------+----------------+------+-----------------------------------------------------------+
| 1 | SIMPLE | c | range | PRIMARY,mdl_cour_cat_ix | mdl_cour_cat_ix | 8 | NULL | 3152 | Using where; Using index; Using temporary; Using filesort |
| 1 | SIMPLE | l | ref | PRIMARY,mdl_log_coumodact_ix,mdl_log_tim_ix | mdl_log_coumodact_ix | 8 | xray2qasb.c.id | 618 | Using index condition; Using where |
+----+-------------+-------+-------+---------------------------------------------+----------------------+---------+----------------+------+-----------------------------------------------------------+
Is there any way to remove usage of temporary and filesort? What do you propose here?
After some testing this query works fast as expected:
SELECT l.id,
l.userid AS participantid,
l.course AS courseid,
l.time,
l.ip,
l.action,
l.info,
l.module,
l.url
FROM mdl_log l
WHERE
l.id > 123456
AND
l.time > 1234
AND
EXISTS (SELECT * FROM mdl_course c WHERE l.course = c.id AND c.category <> 0 )
ORDER BY l.id ASC
LIMIT 0,200
Thanks to JamieD77 for his suggestion!
execution plan:
+----+--------------------+-------+--------+-------------------------+---------+---------+--------------------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+--------+-------------------------+---------+---------+--------------------+----------+-------------+
| 1 | PRIMARY | l | range | PRIMARY,mdl_log_tim_ix | PRIMARY | 8 | NULL | 99962199 | Using where |
| 2 | DEPENDENT SUBQUERY | c | eq_ref | PRIMARY,mdl_cour_cat_ix | PRIMARY | 8 | xray2qasb.l.course | 1 | Using where |
+----+--------------------+-------+--------+-------------------------+---------+---------+--------------------+----------+-------------+
Try moving the category selection outside the JOIN. Here I put it in an IN() which the engine will cache on successive runs. I don't have 200M rows to test on, so YMMV.
DESCRIBE
SELECT l.id,
l.userid AS participantid,
l.course AS courseid,
l.time,
l.ip,
l.action,
l.info,
l.module,
l.url
FROM mdl_log l
WHERE
l.id > 1234567890
AND
l.time > 1234567890
AND
l.course IN (SELECT c.id FROM mdl_course c WHERE c.category > 0)
ORDER BY l.id ASC
LIMIT 0,200;
(In addition to using EXISTS...)
l.id > 123456 AND l.time > 1234
seems to beg for a 2-dimensional index.
99962199 -- the table is very big, correct?
Consider PARTITION BY RANGE on mdl_log on time. But...
Don't have more than about 50 partitions; other inefficiencies kick in then.
Partitioning probably won't help id and time are sorta in lock-step. Typical case: id is AUTO_INCREMENT and time is approximately the time of the INSERT.
If that applies, consider:
PRIMARY KEY(time, id) -- see below
INDEX(id) -- Yes, this is sufficient for `id AUTO_INCREMENT`.
With those indexes, you could efficiently do
WHERE time > ...
ORDER BY time, id
which is probably what you really wanted.

MySQL Get rows that only exist in one table performance

The basics of this query have been asked, and answered, many times before, but I'm still having trouble with performance. Here are the details:
I have the table, Products, that has 105724 rows.
I have an update table, _e360products, that has 51813 rows.
I am matching on an alphanumeric 10 character code, that is indexed (unique) on both tables.
I have tried:
SELECT _e360products.Product_Code, products.StockCode
FROM _e360products Left Join Products ON _e360products.Product_Code = Products.StockCode
WHERE products.StockCode IS NULL
and:
SELECT Product_Code
FROM _e360products
WHERE Product_code NOT IN (SELECT StockCode FROM Products)
and, just for a laugh, even:
SELECT Product_Code
FROM _e360products
WHERE (SELECT count(*) FROM Products WHERE StockCode = Product_code) = 0
None of these have returned results within 20 mins!
If I reverse the queries, i.e. getting unique rows from _e360products, I get results very quickly.
Does anyone have any ideas?
~~~~~ Update ~~~~~
Explain results are:
+----+-------------+---------------+--------+---------------+--------------+---------+------------------------------------------+-------+--------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------+--------+---------------+--------------+---------+------------------------------------------+-------+--------------------------------------+
| 1 | SIMPLE | _e360products | index | NULL | Product_code | 12 | NULL | 50811 | Using index |
| 1 | SIMPLE | Products | eq_ref | stockcode | stockcode | 12 | plumbase_bkup._e360products.Product_code | 1 | Using where; Using index; Not exists |
+----+-------------+---------------+--------+---------------+--------------+---------+------------------------------------------+-------+--------------------------------------+
CREATE TABLE `_e360products` (
`Product_code` varchar(10) CHARACTER SET latin1 NOT NULL DEFAULT '',
`Manufacturers_code` varchar(255) DEFAULT '',
`Description` varchar(255) DEFAULT '',
`Supplier` varchar(255) DEFAULT '',
`Price` varchar(20) DEFAULT '',
`VAT` varchar(20) DEFAULT '',
`Analysis_code` varchar(20) DEFAULT NULL,
PRIMARY KEY (`Product_code`),
KEY `Product_code` (`Product_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `products` (
`productid` int(11) NOT NULL AUTO_INCREMENT,
`QPUM2` varchar(50) NOT NULL DEFAULT '1',
`NWID` varchar(50) NOT NULL DEFAULT '0',
`NHEI` varchar(50) NOT NULL DEFAULT '0',
`NLEN` varchar(50) NOT NULL DEFAULT '0',
`donotdisplayprice` tinyint(2) DEFAULT '0',
`productname` text,
`stockcode` varchar(10) NOT NULL DEFAULT '',
`analysiscode` varchar(50) DEFAULT '',
`usestockcontrol` int(11) DEFAULT '0',
`stockvalue` int(11) DEFAULT '0',
`stock_notification_level` int(11) DEFAULT '0',
`sectionid` int(11) DEFAULT '0',
`productprice` varchar(50) DEFAULT '',
`productprice_incvat` varchar(50) DEFAULT '',
`deleted` int(11) DEFAULT '0',
PRIMARY KEY (`productid`),
UNIQUE KEY `stockcode` (`stockcode`) USING BTREE,
KEY `deleted` (`deleted`),
KEY `allowordering` (`allowordering`),
) ENGINE=MyISAM AUTO_INCREMENT=147440 DEFAULT CHARSET=latin1;
NoteL Products table doesn't include ALL the fields, as there are quite a few...
Please provide a query execution plan (EXPLAIN), it seems your index is not used. Also show as CREATE TABLEs for both tables.
Typo? StockCode[add space here]IS
SELECT _e360products.Product_Code, products.StockCode
FROM _e360products Left Join Products ON _e360products.Product_Code = Products.StockCode
WHERE products.StockCode IS NULL