mysql特有语法_MySQL详细的基础语法

常用函数

IFNULL,ISNULLMySQL详细基础语法

math?formula=%5Ccolor%7Bred%7D%7B%E5%9C%A8MySQL%E4%B8%ADNULL%E5%92%8C''%E6%98%AF%E4%B8%8D%E4%B8%80%E6%A0%B7%E7%9A%84%7D

SELECT IFNULL(null, 1);

ece38c3407a7

ifnull1.png

SELECT IFNULL(0, 1);

ece38c3407a7

ifnull2.png

SELECT IF('', 1), HEX('');

-- HEX()函数可将数字或者字符串转换为16进制格式的字符串

ece38c3407a7

ifnull3.png

SELECT ISNULL(NULL),ISNULL(0),ISNULL(1/0);

ece38c3407a7

isnull.png

NULL的特点

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

SELECT 1=1,NULL=NULL,1=NULL,1<>NULL,NULL is NULL;

ece38c3407a7

null.png

SELECT 1<=>1,NULL<=>NULL,1<=>NULL,1 IS NOT NULL,NULL IS NOT NULL;

ece38c3407a7

null1.png

时间函数now,sysdate

Now() 返回的是语句执行的时间,而sydate()返回的是实时的时间戳

SELECT now(),sleep(2),now();

ece38c3407a7

time.png

SELECT sysdate(),sleep(2),sysdate();

ece38c3407a7

time1.png

math?formula=%5Ccolor%7Bred%7D%7Bnow%E5%8F%AF%E4%BB%A5%E4%BD%BF%E7%94%A8%E7%B4%A2%E5%BC%95%EF%BC%8C%E4%BD%86%E6%98%AFsydate%E4%B8%8D%E5%8F%AF%E4%BB%A5%EF%BC%8C%E5%9B%A0%E4%B8%BAsydate%E7%9A%84%E7%BB%93%E6%9E%9C%E6%98%AF%E5%8A%A8%E6%80%81%E5%8F%98%E5%8C%96%E7%9A%84%E3%80%82%E6%89%80%E4%BB%A5MySQL%E5%AF%B9%E5%8A%A8%E6%80%81%E5%8F%98%E5%8C%96%E7%9A%84%E6%95%B0%E6%8D%AE%E4%B8%8D%E8%83%BD%E4%BD%BF%E7%94%A8%E7%B4%A2%E5%BC%95%7D

ece38c3407a7

time2.png

该写方案如下

ece38c3407a7

time3.png

math?formula=%5Ccolor%7Bred%7D%7BP.S%20%E4%B8%8A%E8%BF%B0%E4%BC%98%E5%8C%96%E5%8F%AF%E8%83%BD%E5%87%BA%E7%8E%B0%E6%97%A0%E6%95%88%E7%9A%84%E6%83%85%E5%86%B5%EF%BC%8C%E5%8E%9F%E5%9B%A0%E6%98%AFMySQL%E4%BC%98%E5%8C%96%E5%99%A8%E8%BF%9B%E8%A1%8C%E4%BA%86%E8%AF%95%E5%9B%BE%E5%90%88%E5%B9%B6%EF%BC%8C%E4%BC%98%E5%8C%96%E6%8E%89%E4%BA%86%E5%AD%90%E6%9F%A5%E8%AF%A2%EF%BC%8C%E6%AD%A4%E6%97%B6%E9%9C%80%E8%A6%81%E5%9C%A8%E5%AD%90%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5%E4%B8%AD%E5%8A%A0%E5%85%A5limit%E7%AD%89%E5%85%B3%E9%94%AE%E5%AD%97%EF%BC%8C%E5%BC%BA%E5%88%B6%E4%BD%BF%E7%94%A8%E5%AD%90%E6%9F%A5%E8%AF%A2%EF%BC%8C%E9%98%BB%E6%AD%A2MySQL%E8%A7%86%E5%9B%BE%E5%90%88%E5%B9%B6%7D

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

ece38c3407a7

time4.png

字符串处理函数RPAD(),LPAD(),RTRIM(),LTRIM(),TRIM(),CONCAT()

ece38c3407a7

str.png

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

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

CONCAT字符串合并函数

math?formula=%5Ccolor%7Bred%7D%7BRPAD%2FLPAD%E5%92%8CCONCAT%E7%BB%93%E5%90%88%E4%BD%BF%E7%94%A8%20%E5%8F%AF%E4%BB%A5%E5%AE%9E%E7%8E%B0%E5%AF%B9%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E5%87%86%E7%A1%AE%E6%8B%BC%E6%8E%A5%E5%92%8C%E5%88%87%E5%89%B2%7D

日期类型的特性

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

ece38c3407a7

date.png

使用日期格式转化后

ece38c3407a7

date2.png

BETWEEN IN

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

ece38c3407a7

between.png

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

ece38c3407a7

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

math?formula=%5Ccolor%7Bred%7D%7BBetween%E5%92%8Cin%E6%80%A7%E8%83%BD%E5%8C%BA%E5%88%AB%E5%9C%A8%E4%BA%8E%E7%B4%A2%E5%BC%95%E4%BD%BF%E7%94%A8%E6%83%85%E5%86%B5%E4%B8%8D%E5%90%8C%20%7D

math?formula=%5Ccolor%7Bred%7D%7Bbetween%E4%BC%9A%E5%9C%A8%E5%8C%BA%E9%97%B4%E8%8C%83%E5%9B%B4%E4%B9%8B%E5%86%85%E9%80%90%E8%A1%8C%E6%89%AB%E6%8F%8F%EF%BC%8C%E7%84%B6%E5%90%8E%E6%A0%B9%E6%8D%AE%E8%81%94%E5%90%88%E7%B4%A2%E5%BC%95%E4%BA%8C%E6%AC%A1%E8%BF%87%E6%BB%A4%7D

math?formula=%5Ccolor%7Bred%7D%7B%E8%80%8Cin%E6%98%AF%E4%BD%BF%E7%94%A8dept_no%E5%92%8Cemp_no%E7%9A%84%E8%81%94%E5%90%88%E7%B4%A2%E5%BC%95%E7%9B%B4%E6%8E%A5%E8%8E%B7%E5%8F%96%E6%95%B0%E6%8D%AE%7D

math?formula=%5Ccolor%7Bred%7D%7B%E5%85%B7%E4%BD%93%E6%83%85%E5%86%B5%E5%A6%82%E4%B8%8B%E5%9B%BE%EF%BC%8C%E5%9B%BE%E4%B8%AD%E5%B7%A6%E8%BE%B9%E6%98%AFbetween%E6%9C%89%E5%8F%98%E6%98%AFin%7D

ece38c3407a7

between2.png

math?formula=%5Ccolor%7Bred%7D%7B%E5%A6%82%E6%9E%9C%E5%88%9B%E5%BB%BA%E8%81%94%E5%90%88%E7%B4%A2%E5%BC%95%E6%97%B6%EF%BC%8Cemp_no%E5%9C%A8%E5%89%8Ddept_no%E5%9C%A8%E5%90%8E%EF%BC%8C%E5%88%99%E4%BD%BF%E7%94%A8between%E5%92%8Cin%E6%95%88%E7%8E%87%E7%9B%B8%E5%90%8C%7D

like的基本用法和特点

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

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

选择率

对优秀的选择率的说明

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

ece38c3407a7

change.png

OR

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

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

SELECT * FROM employees WHERE (emp_no = 10001 OR emp_no = 10002 OR emp_no = 10003);

ece38c3407a7

or.png

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;

执行计划如下

ece38c3407a7

or1.png

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

ece38c3407a7

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;

```

执行计划如下

ece38c3407a7

or3.png

优化前为7s,优化后为0.029s

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 = '洪哥';

执行计划如下

ece38c3407a7

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;

```

优化后,执行计划如下

ece38c3407a7

or5.png

SUBQUERY(子查询)

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

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

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

如果subquery不包含集合函数 union all,union,limit等关键字。查看执行计划,保证该subquery第一个执行或者不出现temp file,file sort等

如果包含上述关键字的subquery必须保证第一个执行,否则对性能有重大影响。原因是当数据量很大时,要尽量减少auth_key的数量,否则产生太大auth_key会严重影响速度

鼓励第二种情况下的subquery 且保持一个subquery

SCALA SUBQUERY(标量子查询) 是放在select和from之间的subquery

这种subquery相当于函数

math?formula=%5Ccolor%7Bred%7D%7B%E8%A6%81%E6%B1%82%E5%BF%85%E9%A1%BB%E8%BF%94%E5%9B%9E%E4%B8%80%E6%9D%A1%E6%88%96%E8%80%850%E6%9D%A1%E6%95%B0%E6%8D%AE%EF%BC%8C%E5%90%A6%E5%88%99%E4%BC%9A%E6%8A%A5%E9%94%99%7D

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

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

ece38c3407a7

subquery.png

如果没有良好的索引,并且要对处于业务高峰的语句进行优化,下面有如下例子

ece38c3407a7

subquery1.png

由于标量子查询只能返回一条数据或者空,则优化代码如下

ece38c3407a7

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条件

math?formula=%5Ccolor%7Bred%7D%7B%E5%BD%93in%E5%90%8E%E9%9D%A2%E7%9A%84%E6%9D%A1%E4%BB%B6%E5%BE%88%E5%A4%9A%E7%9A%84%E6%97%B6%E5%80%99%20%E5%8F%AF%E4%BB%A5%E5%B0%9D%E8%AF%95%E5%B0%86in%E6%9D%A1%E4%BB%B6%E6%94%B9%E4%B8%BA%E4%B8%B4%E6%97%B6%E8%A1%A8%20join%E6%9F%A5%E8%AF%A2%EF%BC%8C%E6%95%88%E6%9E%9C%E5%8F%AF%E8%83%BD%E4%BC%9A%E5%A5%BD%E4%B8%80%E4%BA%9B%7D

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

ece38c3407a7

in.png

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

ece38c3407a7

in1.png

使用in查询时,参数类型要保持一致,否则会导致索引无法使用

math?formula=%5Ccolor%7Bred%7D%7B(%E5%92%8C%E7%89%88%E6%9C%AC%E6%9C%89%E5%85%B3)%7D

ece38c3407a7

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,实例如下图

ece38c3407a7

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

如下图所示

ece38c3407a7

limit.png

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

ece38c3407a7

limit1.png

in + limit是不支持的

例如

ece38c3407a7

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

执行计划如下

ece38c3407a7

limit3.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值