查询的座右铭非常简单,找出外键列的最后一个条目。我能说的伪代码是
select vehicleid , last_journey_point , last_journey_time from journeyTable.
这是我的SQL语句
- loconumber是一个索引列 - journeyserla是一个自动编号主键int(11) - 表locojourney包含400,000条记录
- 下面的代码块在19秒内执行
with LocomotiveLastRun AS(
-- this block of code runs in 0.016 sec
SELECT locojourney.loconumber , MAX(locojourney.journeyserla) as lastrunid
FROM locojourney GROUP BY loconumber)
SELECT locojourney.CurrentCombiners , locojourney.JourneySerla ,
locojourney.From_RunPoint , locojourney.NEXT_RunPoint
FROM LocomotiveLastRun FORCE INDEX(lastrunid)
JOIN locojourney FORCE INDEX(PRIMARY) ON x.lastrunid = locojourney.journeyserla
WHERE locojourney.ishoc = 'n'
EXPLAIN命令显示一个派生表,该表不使用索引并使用where和type ALL
这是表定义:
-- SHOW CREATE TABLE locojourney
CREATE TABLE `locojourney` (
`trainID` smallint(5) NOT NULL,
`LocoNumber` varchar(5) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`CurrentLocoBase` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`CurrentDuedate` date DEFAULT NULL,
`LocoConsist` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`CurrentLocoDomain` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`DomainChange` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`FEDR` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`LADR` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`ISBANKER` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`TrainName` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`WithOutLoad` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 'N',
`runRoute` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`From_RunPoint` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`From_RunTime` datetime NOT NULL,
`NEXT_RunPoint` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`NEXT_RunTime` datetime NOT NULL,
`Affects_Outage` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`Affects_Mileage` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`GroundDistance` double(5,2) DEFAULT '0.00',
`SHGallowance` int(11) DEFAULT '0',
`Outage` double(5,4) DEFAULT '0.0000',
`UnderServiceType` enum('FHT','CHG','DEP','MIX','DETN') CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 'FHT',
`SubServiceHead` varchar(25) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 'RUN',
`IShoc` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`CurrentCombiners` varchar(28) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`RunSetSerla` varchar(25) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`JourneySerla` int(11) NOT NULL AUTO_INCREMENT,
`NominationSerla` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`Traction` enum('DSL','AC') CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 'DSL',
`Trainload` smallint(4) NOT NULL DEFAULT '0',
`LeadAssist` enum('Y','N') CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 'N',
`DEO` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`DEOtime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`JourneySerla`),
KEY `trainID` (`trainID`) USING BTREE,
KEY `routesection_idx` (`runRoute`) USING BTREE,
KEY `loconumber_idx` (`LocoNumber`) USING BTREE,
KEY `runsetserla_idx` (`RunSetSerla`) USING BTREE,
KEY `subservicehead_idx` (`SubServiceHead`) USING BTREE,
CONSTRAINT `locojourney_ibfk_1` FOREIGN KEY (`SubServiceHead`) REFERENCES `ineffective` (`IneffectiveHead`) ON UPDATE CASCADE,
CONSTRAINT `locojourney_ibfk_3` FOREIGN KEY (`runRoute`) REFERENCES `routesections` (`Sectionname`) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT `loconumber_fk` FOREIGN KEY (`LocoNumber`) REFERENCES `lococontainer` (`LocoNumber`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=345719 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
with LocomotiveLastRun AS(
-- this block of code runs in 0.016 sec
SELECT locojourney.loconumber , MAX(locojourney.journeyserla) as lastrunid
FROM locojourney
GROUP BY loconumber)
为什么这个CTE子查询速度快?因为你的表已经在(loconumber, journeyserla)
上有一个索引。 (InnoDb会自动将主键附加到每个索引。)对于该索引上的松散索引扫描,可以满足此查询,并且这些查询速度很快。
现在为您的主要查询:
FORCE INDEX()
。除非你有至少十年的SQL经验,或者你已经阅读过MySQL中InnoDB索引的源代码,否则甚至不要梦想使用它。值得注意的是,它在CTE上完全没用,因为CTE没有索引。WHERE...IN...
这三个建议给了我们这个:
WITH LocomotiveLastRun AS (...)
SELECT locojourney.CurrentCombiners , locojourney.JourneySerla ,
locojourney.From_RunPoint , locojourney.NEXT_RunPoint
FROM locojourney
WHERE journeyserla IN (SELECT lastrunid FROM LocomotiveLastRun)
AND locojourney.ishoc = 'n'
现在,很明显索引可以帮助这个查询。
关于(ishoc)
的指数将有所帮助。 (它实际上是一个索引,因为InnoDB,在(ishoc, journeyserla)
所以它有助于两个WHERE
条件。)查询规划器使用BTREE随机访问找到第一个索引行ishoc
值'n'
,然后扫描主键的值以匹配它们与IN
条款。
覆盖查询的复合索引将有助于实现更多,而不是该索引。这样的覆盖索引特别有用,因为表的每一行都很大,有很多列。该索引提到了WHERE
子句中的列以及您要选择的列,如下所示:
(ishoc, journeyserla, CurrentCombiners, From_RunPoint, NEXT_RunPoint)
查询计划程序可以完全从索引中满足您的查询,从而节省磁盘读取时间以满足查询。如果你经常使用你的查询,这个索引是个好主意。但是,它确实消耗了磁盘空间并且减慢了INSERT
和UPDATE
操作的速度。
尝试一下:
SELECT lj.CurrentCombiners , lj.JourneySerla , lj.From_RunPoint , lj.NEXT_RunPoint
FROM ( SELECT MAX(journeyserla) as lastrunid
FROM locojourney
GROUP BY loconumber
) AS llr
JOIN locojourney AS lj ON llr.lastrunid = lj.journeyserla
WHERE lj.ishoc = 'n'
(给它时间并提供EXPLAIN
)