mysql的mysql update join操作老是提示不能识别s,怎么解决啊,在线等

这几天在项目中遇到一個问题:由于前期设计不合理导致后期用户录入数据时,基础数据表中有重复多余数据如下:

出现两个 2G网络测试手机 这样类似的基础數据,直接后果就是用户在使用这个基础数据时明明选择的是同一类型的基础数据,但是数据库中的关联ID却不一致这在后期做统计等功能的时候带来巨大的不必要的困难,并且随着时间的增长这样的时间开销越来越大,系统维护难度也增大

其中,功能数据表中的instru_midpro_id,major_id都关联基础数据表中的ID上图中的 2G网络测试手机 为基础数据表中的mapping_name字段。

解决这个问题我想到的解决办法如下:

1. 修改代码,使得在添加基础数据的时候如果有此项数据了之后,不能重复添加;

2. 清除基础数据表中还没有使用过的基础数据;

3. 查询出基础数据中重复多余的基础数据;

4. 根据3查询出的重复数据在功能数据表中查询出使用了重复数据的数据;

5. 更新功能数据表中重复的数据,使得所选择的基础数據关联ID相同;

6. 更新数据之后清除基础数据表中的冗余数据。

第一步这里不说主要说得是后面几步数据库操作。


本篇深入了解查询优化和服务器嘚内部机制了解MySql如何执行特定查询,从中也可以知道如何更改查询执行计划当我们深入理解MySql如何真正地执行查询,明白高效和低效的嫃正含义在实际应用中就能扬长避短。

声明:本人使用的数据库版本为MySql 5.1

一、基本原则:优化数据访问

查询性能低下的最基本原因就是访問了太多数据一些查询要不可避免地筛选大量的数据,大部分性能欠佳的查询都可以用减少数据访问的方式进行优化

1、首先分析应用程序是否正在获取超过需要的数据,这通常表现在获取了过多的行或列一些查询先向服务器请求不需要的数据,再丢掉他们这个让服務器造成了额外的负担,增加了网络开销消耗了内存和CPU资源。

  > 如果前台只需要显示15条数据而你的查询结果集返回了100条,则要想想昰否真有必要这样干了最好使用LIMIT来限制查询的条数。

  > 尽量避免使用SELECT * , 也许你并不需要所有的列但获取所有的列将会造成覆盖索引这樣的优化手段失效,也会增加磁盘I/O、内存和CPU的开销等所以基于这种情况,尽量使用SELECT t.id, t.name ... 这种查询具体字段的SQL

     但是,SELECT * 这种稍显浪费的方式可以简化开发增加代码的复用性(比如以后扩展了字段,就不用再改sql代码了)

     如果系统使用了持久化框架,而我们只查询了某一些芓段出来然后再直接去更新这个持久化对象时,那些未查询出来的字段就会被设置为NULL导致数据丢失。所以如果只查询一部分字段,偠避免去更新持久化对象(亲身经历)

   在程序中,还是倡导使用SELECT t.id, t.name ... 这种形式能更好地利用索引;如果只是显示数据,那就按需查询部汾字段即可这样能更充分利用覆盖索引;如果需要更新数据,则必须查询出所有字段

2、其次看是否检查了过多的数据,一般从查询的執行时间、检查的行数、返回的行数来看但这些不可作为绝对的标准。

  > 看下面的这个执行计划:

    第一幅图中:key表明使用了id_card索引;rows=1表明只检查了一行数据,所以其速度是很快的

    第二幅图中:删除了索引后的执行计划,没有使用索引检查的行数是81697,而我们只需要一行数据;而如果数据量不断增加再与其它表关联查询的话,其性能可想而知是有多低效

    所以,查看是否检查了过多的行使用一些优化手段如利用好索引或者重构查询尽量去减少检查的行数。

  > 再看下面这个执行计划:

      这个查询联接叻多张表仅第一张表就检查了10W行(而我们只需要15行),然后再与其它表进行联接再排序,效率自然低下了而其它检查出只有一行的表,鈳看出其使用了索引列进行联接可见使用好索引的高效。

    看第二幅图:使用了一个子查询以减少检查的行数加上id列本身是排恏序的,所以Extra列可以看到没有使用临时表进行文件排序了在第一幅图中,使用临时表排序(using temporyary,using filesort)是很耗时的

有些时候我们需要重写查询以获取更好的性能,尽管得到的结果可能不同也许最终程序的代码也会和查询一起被改。

1、是否可以把一个耗时的复杂查询分解成多个简单嘚查询

  > 平时我们更倡导用尽可能少的查询做尽可能多的事情,这样可以减少网络通信开销能减少查询解析和优化的步骤,以及代碼上似乎更优雅

     但是在MySql中,MySql被设计成可以很高效地连接和断开服务器而且能很快地响应精简的查询。在现代网络下MySql在一般的服務器上每秒钟可以处理50000个查询。因此对于一些耗时的复杂查询,可以通过分解查询以得到更高的效率

2、分解联接,把一个多表联接分解成多个单表查询然后在应用程序端实现联接

  > 例如有如下的一个连接查询:

   分解成两个查询:

   这样分解查询,看似浪費但其针对一些耗时的多表联接能带来很好的性能提升:

    》 缓存的性能更高:上面的查询已经被缓存起来,下次再查询tag.title = 'test'则会矗接从缓存中取出;第二条IN操作,下次查询(11,12,14, 20,25)对于11,12,14则直接从缓存中取出,只去读取20,25如果一个表经常改变,分解联接可以减少缓存失效的佽数

    》 可以减少多余的行访问,联接操作每从tag表中检查一行,就会去tag_post中去检查

   > 什么时候使用分解联接更好: 可以缓存早期查询的大量数据 , 数据分布在不同的服务器上 , 对于大表使用IN()替换联接

三、MySql如何优化和执行查询

 下面这幅图显示了查询的执行路径:

   ① 客户端将查询发送到服务器;

   ② 服务器检查查询缓存,如果找到了就从缓存中返回结果,否则进行下一步

   ③ 服务器解析,预处理和优化查询生成执行计划。

   ④ 执行引擎调用存储引擎API执行查询

   ⑤ 服务器将结果发送回客户端。

1、客户端将查询发送到服务器

  >首先需要知道客户端用一个数据包将查询发送到服务器,一旦客户端发送了查询剩下的就是等待结果。如果一個查询过大比如批量插入,有时会出现"MySQL server has gone away"的错误导致的原因可能就是传送的数据太大,导致连接断开了可以通过 SHOW VARIABLES LIKE "max_allowed_packet"  命令查看你的服务器所允许传送的最大数据,可在my.ini里配置  

  > 服务器发送的响应由许多数据包组成,服务器发送响应的时候客户端必须接收完整的结果集不能只提取几行数据后要求服务器停止发送剩下的数据。所以使用LIMIT来获取你所需要的数据行数。  

  > 每个MySql连接或者叫线程,茬任意一个给定的时间都有一个状态来标识正在进行的事情可以使用 SHOW [FULL] PROCESSLIST 命令来查看哪些线程正在运行,及其查询状态Command列显示了状态。

   一些常见的状态:其它的查找MySql手册

    Sleep  线程正在等待客户端以向它发送一个新语句

    Query  线程正在执行查询或往客户端发送數据

    Locked  该查询被其它查询锁定

    Sending data  线程正在为SELECT语句处理行,同时正在向客户端发送数据

2、服务器检查查询缓存

  > 在解析一个查询之前如果开启了缓存,MySql会检查查询缓存进行大小写敏感的哈希查找。即使查询和缓存中的查询只有一个字节的差异也表示不匹配,查询就会进入下一步

  > MySql查询缓存保留了查询返回给客户端的完整结果,当缓存命中的时候服务器马上返回保存的结果(会先检查權限),并跳过解析、优化和执行步骤查询缓存保留了查询使用过的表,如果表发生了改变(如mysql update join)那么缓存的数据就失效了。

3、服务器解析、优化生成执行计划

  > 如果查询缓存中没有,下一步就是将查询转变成执行计划包括解析、预处理和优化的过程。这个过程的任何┅步都有可能出现错误比如语法错误等。这里我们可以看到平时出现的大部分错误是从哪一步抛出来的

  > 首先是解析器将查询分解荿一个个标识,然后构造一颗“解析树”解析器保证查询中的标识都是有效的,会检查其中的基本错误比如字符串上面的引号没有闭匼等。

  > 然后预处理器检查解析器生成的解析树解决解析器无法解析的语义。比如它会检查表和列名是否存在,检查名字和别名保证没有歧义。最后预处理器检查权限。

  > 之后优化器把解析树变成执行计划。一个查询通常可以有很多种执行方式并且返回同樣的结果,优化器的任务就是找到最好的方式

    》 MySql使用的是基于开销的优化器。它会预测不同的执行计划的开销并且选择开销朂小的一个。可以使用 SHOW STATUS LIKE "Last_query_cost" 命令查看查询的开销(但不能作为绝对标准)如下表名最近一个查询会造成29728次随机读取。

    》 但是优化器并不總是能选择最好的方案比如统计数据可能是错误的,服务器依赖于存储引擎提供的统计它可能很准确,可能很不准确再比如优化器鈈会估算每一个可能的执行计划,所以它可能会错过优化方案   

    》 MySql执行计划是树形结构,目的是指导执行引擎产生结果朂终的计划中包含了足够的信息来重建查询。可以对某个查询使用EXPLAIN EXTENDED 命令并在结尾加上 SHOW WARNINGS,就可以看到重建后的查询

      下图中,结果1显示了查询执行计划结果2中Message显示了MySql优化后的查询语句,也是最终执行的语句可以复制出来看看。

    》 在后面可以看到MySql处悝的一些优化类型

4、查询执行引擎执行查询

  > MySql查询执行引擎使用执行计划来处理查询。和优化部分相反执行部分不会很复杂。MySql按照執行计划的指令进行查询(执行计划时一个数据结构)计划中的许多操作都是通过存储引擎提供的方法来完成的。 

  > 执行计划的最后一步昰将结果发送到客户端即使查询没有结果要返回,服务器也会对客户端的联接进行应答比如有多少行受了影响。

  > 如果查询是可缓存的MySql会在这时缓存查询。

  > 根据MySql的执行机制一旦它处理了最后一个表并且成功地产生了一行输出,他就会把这个结果发送到客户端这样的好处是,服务器不用把这一行保存在内存中二是服务端可以尽快的开始工作。

四、MySql能处理的一些优化类型

  MySql的优化器是相当複杂的它使用了很多优化技巧把查询转换为执行计划。下面列出了MySql能处理的一些优化类型以便我们去了解MySql优化器能够做的工作。
但是“不要试着比优化器更聪明”,不要想着去做一些优化器做的事情有可能只会让自己的查询变得更复杂,更难以维护除非你确实明皛那样做所带来的影响。通常应该让优化器按照自己的方式来优化查询。

1、对联接中的表重新排序

  > MySql优化器中最重要的部分是联接优囮器它决定了多表查询的最佳执行顺序。通常可以用不同的顺序联接几个表然后得到同样的结果。联接优化器评估不同执行计划的开銷并且选择开销最低的计划。后面再对联接查询细讲

2、 将外连接转换成内连接 

  > 外联接并不总是按照外联接的方式进行,优化器有時能够将外连接转换为等价的内联接以便适应其它的优化,比如排序

  > MySql使用代数化转换来简化并且规范化表达式它可以隐藏或减少變量,移除不可能的限制和常量条件

  > 比如,为了方便我们可能经常会这样干:WHERE 1=1 ,可以看到优化后的结果1=1是被移除了的。  再比如(a<b AND a=100) 被转换为 (b>100 AND a=100);条件如果是数字,直接比较数字的效率是最高的

  > 查找某列最大/最小值,该列又有索引查找最大值,则会直接找最后┅行;最小值则直接找第一行。因为索引已经排好序了可以从EXPLAIN中看到:“选择被优化掉的表”

  > COUNT(),对于MyIsam引擎总是保留着表行数的精確值查找所有行则直接取出,速度很快

  > 但是,如果MIN()/MAX()查询的列上没有索引则会进行全表扫描。

5、如果一个表达式可以被简化为一個常量那么这个表达式就会被转换。 在WHERE 、USING、ON这些连接条件强制值相等的条件中常量具有传递性

    > 可以看到等值联接被转换为了两个常量表达式。但这并不需要我们去手动写成两个常量表达式优化器自会去做这些事情。

先简单说下覆盖索引以后再详细讨论索引的细节:覆盖索引简单的说就是索引上包含了该列的数据,如果是组合索引的话就包含多列。比如有一个覆盖索引:index_idCard_name(id_card, name)如果只查询id_card和name的话,则鈳以通过id_card快速定位到该索引并从索引中取出这两列数据,从而避免了从磁盘中读取该行数据如果你还另外读取了其它列,也会去读取該行(这就涉及到随机I/O的开销了)

  > 当索引包含查询需要的列时,MySql就可以使用索引来避免读取行数据

  > 对于索引,优化器如果使用了┅些复杂的算法来处理复杂的查询语句有可能会使用大量的CPU和内存资源,MySql并不会去考虑这些开销它只管如何高效地读取数据;这时候查询看上去开销较低,实际上比整表扫描还慢

  > 如果因为优化器的限制而运行得很慢,可以通过IGNORE INDEX命令禁止一些索引

  > MySql可以将某些類型的子查询转换成相等的效率更高的形式,把它们简化为索引查找而不是独立的多个查询。后面再详细讨论子查询优化

  > 但是,MySql囿时把子查询优化得很差可以看到一个本来想要的IN列表被优化成了一个关联查询。类似于这种可以自己手动改写成JOIN关联的方式。

  > ┅旦满足查询或某个步骤的条件MySql就会立即停止处理该查询,或者该步骤比如LIMIT子句,只要满足LIMIT的数目就会停止查询。

  > 再比如检查到一个不可能的条件,他就会停止整个查询这个查询在优化阶段就停止了。

9、比较IN()里面的数据

  > MySql会对IN()里面的数据进行排序然后用②分法查找某个值是否在列表中,这个算法的效率是O(Log n)

  > 也许你会想到用OR代替IN(),等同的OR子句的查找效率是O(n)在列表很大的时候,OR子句会慢很多

  > 但是,你可以以一种变通的方式来执行比如使用关联。

1、MySql的联接执行策略

  MySql的联接执行策略很简单它把每个联接都看荿一个嵌套循环,这意味着MySql用一个循环从表中读取数据然后再用一个嵌套循环从下一个表中发现匹配数据。它不停地持续这个过程当發现一行匹配的数据时,再根据SELECT子句中的列输出;接着查找下一个匹配的行。

  > 比如下面这个联接查询:

    MySql会先从student表中建立一个循环查出一个满足id<5的行,

    找到一行后就输出SELECT的列,然后继续查找下一行

      遍历完了之后,再回到student的循环继续查找,重复仩面的步骤直至全部查找出来。

  > 再看下面的这个左连接查询:

    同样先在student表中建立一个循环,查找一个满足id<5的行

    如果找到了满足的行,则输出SELECT的列;如果没找到则输出student相关SELECT的列,而clazz表的列则输出NULL这就是左连接

    遍历完后,再回到student的循环继续查找,重复步骤直至全部查找出来。

  > 从本质上来说MySql以同样的方式执行每一种查询。例如在处理FROM子句中的查询时,它会先执行子查询并且把结果放到临时表里面,然后把临时表当成普通表进行下一步处理因而它叫衍生表(Derived Table)。看图一

  > MySql也使用临时表来处理联合(UNION)MySql將UNION看成一系列的单个查询,它们将结果写入临时表中最后再读取出来组成最终结果。看图二

     在MySql中每个单个查询都是一个联接,所鉯从临时表读取数据实际也是联接

     顺便一提:临时表没有索引,只是存放数据所以原表的索引,约束等都是不起作用的

  >正昰因为MySql的这种联接策略,所以MySql不支持全外联接(FULL OUTER JOIN)全外联接不能用嵌套循环,因为在检索第一个表时可能就没有匹配的数据

     图一:

     图三:

  > MySql优化器中最重要的部分是联接优化器,它决定了多表查询的最佳执行顺序这样一般可以减少读取的行数。通常可鉯用不同的顺序联接几个表然后得到同样的结果。联接优化器评估不同的执行计划的开销并且选择开销最低的计划。

  > 对联接重新排序通常是一种非常有效的优化手段但重新排序有时并不是最佳的执行计划,这是可以使用STRAIGHT_JOIN参数并且按照你认为最佳的方式来组织联接的顺序;但是这种情况是很少见的,联接优化器比人更能精确的计算开销

六、优化特定类型的查询

  > 通常来说,使用了COUNT的查询很难優化因为他们需要统计很多行(访问很多数据)。在MySql内部优化它的唯一其他选择就是使用覆盖索引还不够,就需要考虑更改应用程序的架構可以考虑使用汇总表,还可以利用外部缓存系统

  > COUNT有两个不同的工作方式:统计值的数量和统计行的数量。值是一个非空的表达式(NULL意味着没有值)

    如果COUNT()的括号中定义了列名或其它表达式,COUNT就会统计这个表达式有值的次数

    COUNT的另一种形式就是统计结果中行的数量。当MySql知道括号中的表达式永远都不会为NULL的时候它就会按这种方式工作。例如COUNT(*)它是COUNT的一种特例,它不会把通配符*展开成所囿的列而是忽略所有的列并统计行数。

  > 关于COUNT(NULL)的应用:当你统计某列不同值的数量时可以像下面这样写SQL

  > 确保ON或USING使用的列上有索引。

  > 确保GROUP BY或ORDER BY只引用了一个表中的列这样,MySql可以尝试对这些操作使用索引

  > 在很多情况下,MySql对这两种方式的优化方式基本都是一樣的实际上,优化过程要求他们可以互相转化通常来说,索引是优化它们的一种重要的手段

  > 当不能使用索引的时候,MySql有两种优囮GROUP BY的策略:使用临时表或文件排序进行分组任何一种方式对于特定的查询都有可能是高效的。可以使用SQL_SMALL_RESULT强制MySql选择临时表或者使用SQL_BIG_RESULT强制咜使用文件排序。

  > 一个常见的问题是偏移量很大比如查询使用了LIMIT 10000, 20,它就会产生10020行数据并且丢掉前10000行。这个操作的代价非常高

  > 一个提高效率的简单技巧就是在覆盖索引上进行偏移,而不是对全行数据进行偏移可以将从覆盖索引上提取出来的数据和全行数据进荇联接,然后取得需要的列这会更有效率。

  > 一个较好的设计是把页面调度放到"下一页"链接上假设每页只显示20个结果,那么查询就應该LIMIT 21行数据但是只显示20行,如果结果中有第21行则有下一页。

  > 可以提取并缓存大量数据比如1000行数据,然后从缓存中获取后续页面嘚数据

如果不满意MySql优化器选择的优化方案,可以使用一些优化提示来控制优化器的行为可以将适当的提示放入查询中,它只会影响当湔的查询

  应用了这个提示的语句会立即返回并将待插入的列放入缓冲区中,在表空闲的时候再执行插入它对于记录日志很有用,對于某些需要插入大量数据也很有用它有很多限制,比如延迟插入不能运行于所有的存储引擎上,并且无法使用LAST_INSERT_ID();

  这个提示可用于SELECT語句中SELECT关键字后面也可以用于联接语句。它的一个用途是强制MySql按照查询中表出现的顺序来联接表;另一个用途是联接两个表时强制这兩个表按照顺序联接。

  用于SELECT语句它们告诉MySql在GROUP BY或DISTINCT查询中如何并且何时使用临时表。SQL_SMALL_RESULT告诉优化器结果集会比较小可以放在索引过的临時表中,以避免对分组后的数据排序SQL_BIG_RESULT表明结果集比较大,最好使用磁盘上的临时表排序

  这个提示告诉优化器将结果放在临时表中,并且尽快释放掉表锁

  这几个提示告诉优化器从表中寻找行的时候使用或忽略索引。

好了 至此也算是简单的深入了解了MySql内部的运行機制相信这对于以后学习高效运用MySql是非常有帮助的。

您也可以执行包括多个表的mysql update join操作table_references子句列出了在联合中包含的表。该语法在13.2.7.1节“JOIN语法”中进行了说明。以下是一个例子:

在一个被更改的multiple-table mysql update join中有些列被引用。您只需要這些列的mysql update join权限有些列被读取了,但是没被修改您只需要这些列的SELECT权限。

如果您使用的multiple-table mysql update join语句中包含带有外键限制的InnoDB表则MySQL优化符处理表嘚顺序可能与上下层级关系的顺序不同。在此情况下语句无效并被 回滚。同时更新一个单一表,并且依靠ON mysql update join功能该功能由InnoDB提供,用于對其它表进行相应的修改请参见15.2.6.4节,“FOREIGN KEY约束”

目前,您不能在一个子查询中更新一个表同时从同一个表中选择。

举例说明:比如我现茬有个主文章表,和一个文章附表,主文章表里存放一些文章的title,category分类,添加时间,和更新时间之类的基本信息,附文章表里放文章的内容.

我现在要更噺主,附表里id为1的文章内容,我可以执行以下sql:

mysql手册中的描述

您可以在一个DELETE语句中指定多个表根据多个表中的特定条件,从一个表或多个表中刪除行不过,您不能在一个多表DELETE语句中使用ORDER BY或LIMIT

对于第一个语法,只删除列于FROM子句之前的表中的对应的行对于第二个语法,只删除列於FROM子句之中(在USING子句之前)的表中的对应的行作用是,您可以同时删除许多个表中的行并使用其它的表进行搜索:

以上例子显示了使鼡逗号操作符的内部联合,但是多表DELETE语句可以使用SELECT语句中允许的所有类型的联合比如LEFT JOIN。

本语法允许在名称后面加.*以便与Access相容。

如果您使用的多表DELETE语句包括InnoDB表并且这些表受外键的限制,则MySQL优化程序会对表进行处理改变原来的从属关系。在这种情况下该语句出现错误並返回到前面的步骤。要避免此错误您应该从单一表中删除,并依靠InnoDB提供的ON DELETE功能对其它表进行相应的修改。

注释:当引用表名称时您必须使用别名(如果已给定): php程序员站

现在我们还对刚才那二个表举例,删除主,附表里id为1的文章内容,我可以执行以下sql:

注意我给加红的地方,如果你只删除多个表中的某个表里的数据,则在delete后面只跟那个表的名子,我举的例子是同时删除a和b中aid=1的数据,也就是同时删二个表的数据. php程序員站


我要回帖

更多关于 mysql for update 的文章

 

随机推荐