回答

收藏

如何告诉MySQL优化程序在派生表上使用索引?

技术问答 技术问答 329 人阅读 | 0 人回复 | 2023-09-14

假设您有这样的查询…
% r: u+ I' a# B  n+ |, c$ xSELECT T.TaskID, T.TaskName, TAU.AssignedUsers
; y3 k% S0 R$ l  ]" B( pFROM `tasks` T' a/ Y+ A7 t4 X# K3 h& X5 T
    LEFT OUTER JOIN (- X/ `8 a3 q9 R3 P
        SELECT TaskID, GROUP_CONCAT(U.FirstName, ' ',
+ Z( m+ J% l8 h) t6 D! K9 R# d+ i            U.LastName SEPARATOR ', ') AS AssignedUsers
% F$ D5 s+ X# \4 \& U        FROM `tasks_assigned_users` TAU8 z" @1 N4 V& N# h. H. {+ R
            INNER JOIN `users` U ON (TAU.UserID=U.UserID)& \3 E0 L# y3 K9 j; T2 X. f
        GROUP BY TaskID
5 [* p! S1 a# C- E8 f8 U. ^3 }    ) TAU ON (T.TaskID=TAU.TaskID)
! e6 y' N- c* x$ d- V; z可以将多个人分配给给定任务。该查询的目的是显示每个任务一行,但将分配给该任务的人员显示在单个列中1 h5 j! h$ T2 e9 L5 j* w
现在…假设你有正确的指标设置上tasks,users和tasks_assigned_users。连接tasks到派生表时,MySQL
9 F& H( Z6 S( g+ f) ?2 R8 }9 qOptimizer仍将不使用TaskID索引。WTF?!?!?  S; t) R8 T- m3 ]" C. J
因此,我的问题是…如何使此查询使用task_assigned_users.TaskID上的索引?临时表是la脚的,所以如果这是唯一的解决方案,那么MySQL" X( `" p+ F# J
Optimizer是愚蠢的。; M) m+ z# {5 d6 @& K7 X, G
使用的索引:& X6 Y7 s/ a. t. T$ r
任务
" D& B* ?4 W1 Q6 {+ ?3 a主要-TaskID9 N: z$ H! p, V
使用者
8 h+ x6 e* C- ^6 B5 z, W主要-用户ID) B: Y5 X) z: s4 r: C
task_assigned_users   ?' U1 U! y0 E7 x# A( ~
主要-(TaskID,UserID)7 f! {( U' w" P2 C# s; O- u9 p
附加索引UNIQUE-(UserID,TaskID)! o8 V" `7 k! u8 |4 u2 `
! F6 @3 u1 {4 o3 l2 s

* J4 I+ |) M4 G编辑: 此外,此页面还说派生表在联接发生之前已执行/实例化。为什么不重新使用键来执行联接?
( I8 X1 M8 a1 }& u; t编辑2: MySQL% {/ L) s. L, o7 y
Optimizer不允许您在派生表上放置索引提示(大概是因为派生表上没有索引)
1 Y% ~0 |! L- w% s9 D* C编辑3: 这是一个关于此的非常好的博客文章:http :, }5 o) r+ C, _" X/ G3 K; d
//venublog.com/2010/03/06/how-to-improve-subqueries-derived-tables-
) U# @8 |0 t0 m3 A' u& Xperformance/ 注意案例2是我的解决方案正在寻找,但MySQL目前不支持此功能。6 e! @& o8 R. C
编辑4: 刚刚发现这个:“在MySQL6 q! W. N$ f% `
5.6.3,优化更有效地处理子查询在FROM子句(即派生表):......在查询执行,优化程序可以添加一个索引来派生表加快从中进行行检索的速度。”1 o/ K8 T+ D* w
似乎很有希望…
- u; M$ C. Q9 H7 t                + a% I3 I6 g, z, t& ~
解决方案:
6 C5 [+ b$ O: b  x; E+ e% f4 i7 o               
4 V- ^; P) k3 O7 R. M- g4 ~% E- \- f) L1 B

( i7 f" q( r# A  c# v% a5 l& ?& D                在MySQL Server 5.6中,有一个解决方案-预览版(在撰写本文时)。2 y- h" I% w" j0 ?$ U2 f
http://dev.mysql.com/doc/refman/5.6/en/from-clause-subquery-: B& @5 `* s7 N: ^. E  ^
optimization.html
- E5 R9 E8 C7 w* C5 H5 d& v虽然,我不确定在“将索引添加到派生表中”时MySQL Optimizer是否会重用已经存在的索引。2 V* D$ O4 @8 T" O" p
考虑以下查询:, q' [/ E/ K3 G2 r
SELECT * FROM t1 JOIN(SELECT * FROM t2)AS named_t2 ON t1.f1 = derived_t2.f1;
/ s/ `6 D  I; K3 ~% M4 f8 P该文档说:“如果这样做可以允许对最低成本的执行计划使用ref访问,那么优化程序将在f1的f1列上构造一个索引。”+ k) V( D3 p2 S) h2 E
好的,那很好,但是优化程序会重用t2的索引吗?换句话说,如果存在针对t2.f1的索引该怎么办?该索引会被重用,还是优化程序为派生表重新创建该索引?谁知道?. b; ^8 @3 h6 }7 f6 R/ ?) s" H5 @  c
编辑: 直到MySQL 5.6,最好的解决方案是创建一个临时表,在该表上创建一个索引,然后在临时表上运行SELECT查询。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则