MySQL详细的基础语法

常用函数


  • IFNULL,ISNULLMySQL详细基础语法

    \color{red}{在MySQL中NULL和''是不一样的}

    SELECT IFNULL(null, 1);
    
ifnull1.png
SELECT IFNULL(0, 1);
ifnull2.png
SELECT IF('', 1), HEX('');
-- HEX()函数可将数字或者字符串转换为16进制格式的字符串
ifnull3.png
SELECT ISNULL(NULL),ISNULL(0),ISNULL(1/0);
isnull.png
  • NULL的特点

    null只能用is NULL比较,和数值、字符串等进行比较都没有结果集。'<=>'是MySQL特有的判断符号。叫等价于。

    SELECT 1=1,NULL=NULL,1=NULL,1<>NULL,NULL is NULL;
    
null.png
SELECT 1<=>1,NULL<=>NULL,1<=>NULL,1 IS NOT NULL,NULL IS NOT NULL;
null1.png
  • 时间函数now,sysdate

    <font color="red">Now() 返回的是语句执行的时间,而sydate()返回的是实时的时间戳</font>

    SELECT now(),sleep(2),now();
    
time.png
SELECT sysdate(),sleep(2),sysdate();
time1.png

\color{red}{now可以使用索引,但是sydate不可以,因为sydate的结果是动态变化的。所以MySQL对动态变化的数据不能使用索引}

time2.png

该写方案如下

time3.png

\color{red}{P.S 上述优化可能出现无效的情况,原因是MySQL优化器进行了试图合并,优化掉了子查询,此时需要在子查询语句中加入limit等关键字,强制使用子查询,阻止MySQL视图合并}

时间函数实际优化案例(CURDATE()函数为返回当前的日期)

time4.png
  • 字符串处理函数RPAD(),LPAD(),RTRIM(),LTRIM(),TRIM(),CONCAT()
str.png

RPAD/LPAD 数据库某列要保证某个长度,不足的使用特定字符右/左填充

RTRIM、LTRIM和TRIM是去除字段值得空字符

CONCAT字符串合并函数

\color{red}{RPAD/LPAD和CONCAT结合使用 可以实现对字符串的准确拼接和切割}

日期类型的特性

MySQL日期类型不需要使用日期函数转换,只要按照日期类型格式编写即可,其他数据库则必须进行转换

date.png

使用日期格式转化后

date2.png
  • BETWEEN IN

    字符类型不能使用between, 性能较差

between.png

可以将字符类型的between转化为in查询,性能较好

between1.png

也可以使用union all改写

SELECT * FROM dept_emp2 WHERE dept_no = 'd003' AND emp_no = 10005
UNION ALL
SELECT * FROM dept_emp2 WHERE dept_no = 'd004' AND emp_no = 10005
UNION ALL
SELECT * FROM dept_emp2 WHERE dept_no = 'd005' AND emp_no = 10005

\color{red}{Between和in性能区别在于索引使用情况不同 }

\color{red}{between会在区间范围之内逐行扫描,然后根据联合索引二次过滤}

\color{red}{而in是使用dept_no和emp_no的联合索引直接获取数据}

\color{red}{具体情况如下图,图中左边是between有变是in}

between2.png

\color{red}{如果创建联合索引时,emp_no在前dept_no在后,则使用between和in效率相同}

  • like的基本用法和特点

    使用like对字符类型的列进行模糊查询,最好把%写在后面,如‘aa%’。这样可以使用一些索引。而且尽量使用字段代替*来减少查询面积

    日期类型一定不能用like查询 无法使用索引 查询某一时间段的数据可以使用between and

  • 选择率

    对优秀的选择率的说明

    如下2个SQL中去除重复值后越接近count(*)选择率越高

change.png
  • OR

    使用OR的时候必须两边加括号 否则结果完全不一样 OR条件如果复杂的情况 可以适当考虑union all分离

    1. 对于相同的列 or条件等同于in

      SELECT * FROM employees WHERE (emp_no = 10001 OR emp_no = 10002 OR emp_no = 10003);
      
or.png
  1. OR优化

    DESC SELECT
       mci.PRODUCT_ID,
       mci.PRODUCT_CODE,
       mci.PRODUCT_NAME,
       mci.PRODUCT_LOGO,
       mci.SHOP_NAME,
       mci.IS_ENABLE,
       mci.CATEGORY_N01,
       mci.CATEGORY_N02,
       mci.CATEGORY_N03,
       ccv2_1.CATEGORYNAME CATEGORY_N01_NAME,
       ccv2_2.CATEGORYNAME CATEGORT_N02_NAME,
       ccv2_3.CATEGORYNAME CATEGORY_N03_NAME,
       mci.THIRD_STORE_COMMISSION,
       mci.COMMODITY_TYPE
    FROM
       mall_ccmmodity_info AS mci
       LEFT JOIN comnodity_category_v2 AS ccv2_1 ON ccv2_1.UNIQUE_NO = mci.CATEGORY_N01
       LEFT JOIN comnodity_category_v2 AS ccv2_2 ON ccv2_2.UNIQUE_NO = mci.CATEGORY_N02
       LEFT JOIN comnodity_category_v2 AS ccv2_3 ON ccv2_3.UNIQUE_NO = mci.CATEGORY_N03
       LEFT JOIN (
       SELECT
           pprod.PRODOCT_ID,
           COUNT( 0 ) count 
       FROM
           promotion_product pprod
           INNER JOIN promotion_info pinfo ON pinfo.PROMOTION_ID = pprod.PROMOTION_ID 
           AND pprod.IS_ENABLE = 1 
           AND pinfo.IS_ENABLE = 1 
           AND pinto.BELONG_T0 = 1 
           AND pinto.END_TIME >= NOW( ) 
           AND NOT ( pinfo.ONSHELVE_TIME > '2018-06-30 00:00:' OR pinfo.END_TIME < '2017-12-06 17:00:00' ) 
       GROUP BY
           pprod.PRODOCT_ID 
       ) AS pc ON pc.PRODUCT_ID = mci.PRODUCT_ID 
    WHERE
       mci.IS_ENABLE = 0 
       AND mci.COMMODITY_TYPE IN ( '1', '5', '6' ) 
       AND ( pc.count = 0 OR pc.count IS NULL ) 
       LIMIT 0,
       5;
    

    执行计划如下

or1.png
 分析上面的执行计划可以发现,在derived2这个位置,类型是all,发生了全表扫描,并且没有auth_key产生。注意pc.count = 0 OR pc.count IS NULL这个SQL,去掉之后执行计划就变成如下
or2.png
 产生了auth_key所以优化的关键点在于pc.count = 0 OR pc.count IS NULL这个位置,所以优化SQL如下

 ```mysql
 DESC SELECT
    mci.PRODUCT_ID,
    mci.PRODUCT_CODE,
    mci.PRODUCT_NAME,
    mci.PRODUCT_LOGO,
    mci.SHOP_NAME,
    mci.IS_ENABLE,
    mci.CATEGORY_N01,
    mci.CATEGORY_N02,
    mci.CATEGORY_N03,
    ccv2_1.CATEGORYNAME CATEGORY_N01_NAME,
    ccv2_2.CATEGORYNAME CATEGORT_N02_NAME,
    ccv2_3.CATEGORYNAME CATEGORY_N03_NAME,
    mci.THIRD_STORE_COMMISSION,
    mci.COMMODITY_TYPE
 FROM
    mall_ccmmodity_info AS mci
    LEFT JOIN comnodity_category_v2 AS ccv2_1 ON ccv2_1.UNIQUE_NO = mci.CATEGORY_N01
    LEFT JOIN comnodity_category_v2 AS ccv2_2 ON ccv2_2.UNIQUE_NO = mci.CATEGORY_N02
    LEFT JOIN comnodity_category_v2 AS ccv2_3 ON ccv2_3.UNIQUE_NO = mci.CATEGORY_N03
    LEFT JOIN (
    SELECT
        pprod.PRODOCT_ID,
        COUNT( 0 ) count 
    FROM
        promotion_product pprod
        INNER JOIN promotion_info pinfo ON pinfo.PROMOTION_ID = pprod.PROMOTION_ID 
        AND pprod.IS_ENABLE = 1 
        AND pinfo.IS_ENABLE = 1 
        AND pinto.BELONG_T0 = 1 
        AND pinto.END_TIME >= NOW( ) 
        AND NOT ( pinfo.ONSHELVE_TIME > '2018-06-30 00:00:' OR pinfo.END_TIME < '2017-12-06 17:00:00' ) 
    GROUP BY
        pprod.PRODOCT_ID 
    ) AS pc ON pc.PRODUCT_ID = mci.PRODUCT_ID 
 WHERE
    mci.IS_ENABLE = 0 
    AND mci.COMMODITY_TYPE IN ( '1', '5', '6' ) 
    AND case when pc.count is null then 0 else pc.count end = 0
    LIMIT 0,
    5;
 ```

 执行计划如下
or3.png
 优化前为7s,优化后为0.029s
  1. or优化案例2

    优化前的SQL如下

    DESC SELECT
       COUNT( t2.store_id ) 
    FROM
       lp_liquidator t1
       INNER JOIN lp_liquidator_store t2 ON t1.liquidator_id = t2.liquidator_id
       LEFT JOIN lp_store_bank t3 ON t2.store_id = t3.store_id
       LEFT JOIN lp_witness_bind_bank t4 ON t4.lp_store_id = t2.store_id 
    WHERE
       t4.bank_card_user = '洪哥' 
       OR t3.username = '洪哥';
    

    执行计划如下

or4.png
 查询耗时15s

 根据条件复杂时可考虑使用union替换or的原则,优化SQL如下

 ```mysql
 DESC SELECT
    COUNT( t2.store_id ) 
 FROM
    (
 SELECT
    t2.store_id 
 FROM
    lp_liquidator t1
    INNER JOIN lp_liquidator_store t2 ON t1.liquidator_id = t2.liquidator_id
    LEFT JOIN lp_store_bank t3 ON t2.store_id = t3.store_id 
 WHERE
    t3.username = '洪哥' UNION
 SELECT
    t2.store_id 
 FROM
    lp_liquidator t1
    INNER JOIN lp_liquidator_store t2 ON t1.liquidator_id = t2.liquidator_id
    LEFT JOIN lp_witness_bind_bank t4 ON t2.store_id = t4.lp_store_id 
 WHERE
    t4.bank_card_user = '洪哥' 
    ) b;
 ```

 优化后,执行计划如下
or5.png
  • SUBQUERY(子查询)

    SUBQUERY按照出现的位置可以分为INLINE VIEW SUBQUERY,SCALA SUBQUERY, IN EXISTS使用的SUBQUERY

    1. INLINE VIEW SUBQUERY 用在FORM后面的子查询 相当于临时表

      MySQL想要使用这种类型的子查询,需要满足如下条件

      1. 如果subquery不包含集合函数 union all,union,limit等关键字。查看执行计划,保证该subquery第一个执行或者不出现temp file,file sort等
      2. 如果包含上述关键字的subquery必须保证第一个执行,否则对性能有重大影响。原因是当数据量很大时,要尽量减少auth_key的数量,否则产生太大auth_key会严重影响速度
      3. 鼓励第二种情况下的subquery 且保持一个subquery
    2. SCALA SUBQUERY(标量子查询) 是放在select和from之间的subquery

      1. 这种subquery相当于函数 \color{red}{要求必须返回一条或者0条数据,否则会报错}

        但是这种subquery因为运行返回行数多 所以最好不要再返回行数多的query中运行 并且必须要有很好的索引

        标量子查询一定也写在最外面

subquery.png
    如果没有良好的索引,并且要对处于业务高峰的语句进行优化,下面有如下例子
subquery1.png
    由于标量子查询只能返回一条数据或者空,则优化代码如下
subquery2.png
    $\color{red}{优化前查询较慢的原因如下: 先忽略子查询,语句为}$

    ```mysql
    select t1.emp_no from t1 limit 100;
    ```

    $\color{red}{这样会得到100行数据,之后的执行操作就是双重for循环。假如子查询的结果集为200行。外层循环1次内层循环就要循环200次,并且就算在第一次循环时就满足了e.emp_no=t1.emp_no条件后续循环一样会执行,不会停止。这样就造成了没必要的循环操作。加上limit后,找到满足条件的之后,循环就会停止,进入第二次循环,这样就大大提升了效率。}$

 2. 这种子查询不会是最后结果集 往往可以和left join互相替换

    在外层查询结果较少,join连接的表row很大,并且需要进行group by操作的时候,将left join改写成标量子查询后,往往可以提升效率

 3. SCALA SUBQUERY可进行分页查询
  • IN 和 NOT IN

    in和not in 相当于or条件 \color{red}{当in后面的条件很多的时候 可以尝试将in条件改为临时表 join查询,效果可能会好一些}

    In 和not in不能识别NULL,并且not in的条件中绝对不能有NULL,否则永远没有查询结果如下图中以in为实例

in.png

将语句进行改写,使用or进行查询,查询条件改为id等价于null反而可以查询出结果。如下图

in1.png

使用in查询时,参数类型要保持一致,否则会导致索引无法使用\color{red}{(和版本有关)}

in2.png

not in语句改写left join示例

not in语句如下

SELECT * FROM t_order WHERE emp_no NOT IN (SELECT emp_no FROM employees WHERE emp_no > 20000);

改写后的语句如下

SELECT t.emp_no,s.emp_no FROM t_order t 
LEFT JOIN (SELECT emp_no FROM employees WHERE emp_no > 20000) s ON t.emp_no=s.emp_no
WHERE s.emp_no IS NULL
AND t.emp_no IS NOT NULL;
  • EXISTS和NOT EXISTS

    exists 和 not exists最大的区别是可以允许条件为null,实例如下图

exists.png
  • Limit

    主要用于分页优化空间在于延迟join方式的优化

    实例如下,源语句

    SELECT * FROM employees e ORDER BY first_name LIMIT 100000 , 0;
    

    优化后语句

    SELECT a.* FROM (
        SELECT first_name,emp_no FROM employees e ORDER BY first_name LIMIT 100000 , 0
    ) s straight_join employees a WHERE s.emp_no=a.emp_no;
    

    Union, union all中不能使用limit

    如下图所示

limit.png

为了避免这种操作,我们可以对查询语句进行如下优化,将原先的查询变为子查询进行优化

limit1.png

in + limit是不支持的

例如

limit2.png

解决办法也同样事改写成子查询绕过即可

SELECT t1.emp_no FROM t1 WHERE t1.emp_no in 
(
    SELECT b.emp_no FROM (
        SELECT e.emp_no FROM employees e WHERE e.emp_no BETWEEN 10001 AND 11000 LIMIT 1000
    ) b
) LIMIT 5

当然还可以使用join进行改写,语句如下

SELECT t1.emp_no FROM t1 JOIN
(
    SELECT DISTINCT a.emp_no FROM (
        SELECT e.emp_no FROM employees e WHERE e.emp_no BETWEEN 10001 AND 11000 LIMIT 1000
    ) a
) e ON t1.emp_no = e.emp_no limit 5

执行计划如下

limit3.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

  • 表 怎么查看一个表使用desc 包含列和属性 主要用于查看表中的列的时候 经常用于写SQLSHOW CREATE...
    岚心妍阅读 158评论 0 0
  • 关系数据库入门 关系数据库概述 数据持久化 - 将数据保存到能够长久保存数据的存储介质中,在掉电的情况下数据也不会...
    d4d98020ef88阅读 654评论 0 0
  • (1)-uroot -p 或者mysql -uroot :连接数据库 (2) mysql select versi...
    beyondflying阅读 291评论 0 0
  • 1. 为什么会有这篇文章? 选择DB作为Training的第一个系列,而不是其他,是因为这货太重要,而且也是大多数...
    Ryanwli阅读 724评论 0 4
  • 文章大纲 一、数据库简介二、Mysql数据库简介三、Mysql安装与服务启动(Windows版本)四、Mysql图...
    故事爱人c阅读 3,005评论 0 1