mysql的一些小技巧(遇到过的坑)

在实际开发过程中,各种问题千奇百怪,这里记录一些遇到的问题,和一些数据库的方法,不定期更新,给自己也给他人方便。

1、mysql获取前一个月的日期和前一年的日期

set @dt = now();
select extract(year_month from date_add(@dt, interval -1 month));
select extract(year_month from date_add(@dt, interval -1 year));

前一个月的日期:
获取上个月时间
前一年的日期:
前一年的日期
数据库中@代表是局部变量声明,如何没有@符会查找对应列名,没有会报错。同样的@符还可以解决下一个问题:

2、在数据库查询的结果集给每一列排序(排名)

SELECT
	s.*,
	( @i := @i + 1 ) num 
FROM
	`student` s,
	( SELECT @i := 0 ) a 
WHERE
	age > 17

排名
排名在数据库中执行没有问题,但是放到XML文件中,在程序中运行就不行了。

报错:net.sf.jsqlparser.parser.ParseException: Encountered unexpected token: “:” “:”

这是因为框架用了 Mybatis-Plus
框架中,有用到多租户的功能,Mybatis-plus 会 进行数据权限的过滤。要对该条sql,进行租户放行。

如果版本是3.1.1以上的 直接 添加注解 即可

参考Mybatis-plus官网,在Mapper的SQL上 加入注解 (亲测有效)

 @SqlParser(filter=true) 

3.11以下版本的要在配置文件加入(未测)

mybatis-plus:
  global-config:
    sql-parser-cache: true

3、将字符串字段转为数字排序或比大小

一次在查询后进行排序,发现排序的数字是varchar类型,排序完全错误。

解决方案:最简单的办法就是在字段后面加上+0

方法一:ORDER BY ‘123’+0;(首推)
方法二:ORDER BY CAST(‘123’ AS SIGNED);
方法三:ORDER BY CONVERT(‘123’,SIGNED);

错误版本:
在这里插入图片描述
正确方式:
方法一:
在这里插入图片描述
方法二:
在这里插入图片描述
方法三:
在这里插入图片描述

4、如何在mysql的查询结果中增加一个自定义字段进去

这是原本数据库查询字段,假如需要加一个"favorites"字段一并返回,看图二
在这里插入图片描述

SELECT id,name,age,address,datatime,num,'favorites' FROM `student`

在这里插入图片描述
在这里插入图片描述

5、mybatis动态sql中的trim标签的使用

1、trim标记是一个格式化的标记,可以完成set或者是where标记的功能,如下代码:

 select * from user 

  <trim prefix="WHERE" prefixoverride="AND |OR ">

    <if test="name != null and name.length()>0"> AND name=#{name}</if>

    <if test="gender != null and gender.length()>0"> AND gender=#{gender}</if>

  </trim>

假如说name和gender的值都不为null的话打印的SQL为:select * from user where name = ‘xx’ and gender = ‘xx’

在 where和name中间是不存在第一个and的,上面两个属性的意思如下:

prefix:前缀

prefixoverride:属性会忽略通过管道分隔的文本序列,去掉第一个and或者是or(注意此例中的空格也是必要的
  
2、

update user

  <trim prefix="set" suffixoverride="," suffix=" where id = #{id} ">

    <if test="name != null and name.length()>0"> name=#{name} , </if>

    <if test="gender != null and gender.length()>0"> gender=#{gender} ,  </if>

  </trim>

假如说name和gender的值都不为null的话打印的SQL为:update user set name=‘xx’ , gender=‘xx’ where id=‘x’

在’xx’ 和where中间不存在逗号,而且自动加了一个set前缀和where后缀,上面三个属性的意义如下,其中prefix意义如上:

suffixoverride:去掉最后一个逗号(也可以是其他的标记,就像是上面前缀中的and一样)

suffix:后缀

6、mybatis if-else(写法)

mybaits 中没有else要用chose when otherwise 代替

<choose>
    <when test="">
        //...
    </when>
    <otherwise>
        //...
    </otherwise>
</choose>

其中choose为一个整体
when是if
otherwise是else

范例

<!--批量插入用户-->
<insert id="insertBusinessUserList" parameterType="java.util.List">
    insert into `business_user` (`id` , `user_type` , `user_login` )
    values
    <foreach collection="list" index="index" item="item" separator=",">
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <choose>
                <when test="item.id != null and item.id !=''">
                    #{item.id,jdbcType=CHAR},
                </when>
                <otherwise>
                    '',
                </otherwise>
            </choose>
            <choose>
                <when test="item.userType != null and item.userType !=''">
                    #{item.userType,jdbcType=VARCHAR},
                </when>
                <otherwise>
                    '',
                </otherwise>
            </choose>
        </trim>
    </foreach>
</insert>

7、mybatis 中where标签

where 元素知道只有在一个以上的if条件有值的情况下才去插入“WHERE”子句。而且,若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。

如果 where 元素没有按正常套路出牌,我们还是可以通过自定义 trim 元素来定制我们想要的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ... 
</trim>
<select id="findActiveBlogLike"  resultType="Blog">
  SELECT * FROM BLOG 
  <where> 
    <if test="state != null">
         state = #{state}
    </if> 
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

8、mybatis 中foreach标签

foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

注意:你可以将任何可迭代对象(如列表、集合等)和任何的字典或者数组对象传递给foreach作为集合参数。当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。当使用字典(或者Map.Entry对象的集合)时,index是键,item是值。

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

9、mybatis 中selectKey标签

selectKey返回最近一次插入的id

<insert id="insertAuthor">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
  </selectKey>
  insert into Author
    (id, username, password, email,bio, favourite_section)
  values
    (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>

在上面的示例中,selectKey 元素将会首先运行,Author 的 id 会被设置,然后插入语句会被调用。这给你了一个和数据库中来处理自动生成的主键类似的行为,避免了使 Java 代码变得复杂。

如果设置order=“AFTER”,那么先执行插入语句,然后是 selectKey 元素

selectKey 元素描述如下:

<selectKey
  keyProperty="id"
  resultType="int"
  order="BEFORE"
  statementType="PREPARED">
属性描述
keyPropertyselectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
resultType结果的类型。MyBatis 通常可以推算出来,但是为了更加确定写上也不会有什么问题。MyBatis 允许任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。
order这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素 - 这和像 Oracle 的数据库相似,在插入语句内部可能有嵌入索引调用。
statementType与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。

10、mybatis 根据id批量删除

<delete id="deleteByLogic"  parameterType = "java.util.List">
     delete from user where 1>2
         or id in
      <foreach collection="list"  item="item" open="(" separator="," close=")"  >
           #{item}
      </foreach>
</delete>

11、mybatis 批量新增和修改

//批量新增
int batchInsertGoods(List<Goods> list);
 
//批量修改
int batchUpdateGoodsByIds(List<Map<String, Object>> list);

<!-- 批量新增-->
<insert id="batchInsertGoods" parameterType="java.util.List">
    INSERT INTO goods(i_id, goods_name)
    VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.id,jdbcType=VARCHAR},#{item.goodsName,jdbcType=VARCHAR})
    </foreach>
</insert>
 
<!-- 批量修改方式一-->
<update id="batchupdateGoodsByIds" parameterType="java.util.List">
      UPDATE goods
        <trim prefix="set" suffixOverrides=",">
            <trim prefix="goods_name = case" suffix="end">
                <foreach collection="list" item="item" index="index" >
                    <if test="item.goodsName != null and item.goodsName != ''">
                        when i_id = #{item.id, jdbcType=VARCHAR} then #{item.goodsName, jdbcType=VARCHAR}
                    </if>
                    <if test="item.goodsName == null or item.goodsName == ''">
                        when i_id = #{item.id, jdbcType=VARCHAR} then goods.goods_name
                    </if>
                </foreach>
            </trim>
            <trim prefix="iorder = case" suffix="end">
                <foreach collection="list" item="item" index="index" >
                    <if test="item.iorder != null and item.iorder != ''">
                        when i_id = #{item.id, jdbcType=VARCHAR} then #{item.iorder, jdbcType=VARCHAR}
                    </if>
                    <if test="item.iorder == null or item.iorder == ''">
                        when i_id = #{item.id, jdbcType=VARCHAR} then goods.iorder
                    </if>
                </foreach>
            </trim>
        </trim>
        WHERE i_id in
        <foreach collection="list" index="index" item="item" separator="," open="(" close=")">
            #{item.id, jdbcType=VARCHAR}
        </foreach>
</update>
 
<!--批量修改方式二 -->
<update id="batchUpdateOption" parameterType="java.util.List" >
    update goods set
        c_content =
      <foreach collection="list" item="item" index="index" separator=" " open="case c_id" close="end">
        when #{item.cId, jdbcType=VARCHAR} then #{item.content, jdbcType=VARCHAR}
      </foreach>
       ,dt_editdate = NOW()
    where c_id in
    <foreach collection="list" item="item" index="index" separator="," open="(" close=")">
      #{item.cId, jdbcType=INTEGER}
    </foreach>
  </update>
 
<!-- 根据ids批量获取Goods列表-->
<select id="getGoodsList" parameterType="java.util.List" resultType="java.util.Map" >
    SELECT id, goods_name WHERE id in
       <foreach collection="list" item="item" index="index" open="(" separator="," close=")">
           #{item}
       </foreach>
</select>
-- 批量新增
INSERT INTO goods(id, goods_name)
VALUES
(?, ?),
(?, ?);
 
-- 批量修改,不为空则修改,为空则不修改(goods.goods_name,goods.iorder代表不修改)
UPDATE goods set goods_name = case when id = ? then ? when id = ? then goods.goods_name end, 
iorder = case when id = ? then goods.iorder when id = ? then ? end 
WHERE id in ( ? , ? );

12、mysql中case when then end的含义和用法

case when then else end 语句

一是:用于查询满足多种条件的情况,类似java中的if…else;
二是:就是用于进行行转列的查询,这个是放在select 子句后面的,充当的是字段的作用。

一:具体用法分为两种,一种是简单的函数形式,另一种就是表达式的形式。

简单的函数形式:case 字段 when 值 then 结果 else 其他情况 end;

表达式的形式:case when 字段=值(这里写表达式,例如 score=80) then 结果 else 其他情况 end;

简单的函数形式
select (case score when 'a' then '优秀' else '良好' end) as score from student;

表达式形式
select (case when score between 85 and 90  then '优秀' else '良好' end) as score from student;

(case when then end)就是select后面需要查询出来的一个字段,在使用时可以用小括号括起来可读性更高,对其可以使用聚合函数,别名,去重,等操作。

二:行列转换

insert into `t_score`(name,course,score) values
    ('王海', '语文', 86),
    ('王海', '数学', 83),
    ('王海', '英语', 93),
    ('陶俊', '语文', 88),
    ('陶俊', '数学', 84),
    ('陶俊', '英语', 94),
    ('刘可', '语文', 80),
    ('刘可', '数学', 86),
    ('刘可', '英语', 88),
    ('李春', '语文', 89),
    ('李春', '数学', 80),
    ('李春', '英语', 87);

用case when then end 函数进行查询

SELECT name,
max(CASE course WHEN '语文' THEN score ELSE 0 END ) Chinese,
max(CASE course WHEN '数学' THEN score ELSE 0 END ) math,
max(CASE course WHEN '英语' THEN score ELSE 0 END ) English,
sum(score) score
FROM t_score 
GROUP BY name
union
select 'TOTAL',
sum(CASE course WHEN '语文' THEN score ELSE 0 END ),
sum(CASE course WHEN '数学' THEN score ELSE 0 END ),
sum(CASE course WHEN '英语' THEN score ELSE 0 END ),
sum(score) from t_score;

效果图:
在这里插入图片描述
另一种方法获取所需结果

利用with rollup 给结果集新添一行,再使用ifnull过程函数判断name的值是否为空

select ifnull(name,'TOTAL') name,
sum(CASE course WHEN '语文' THEN score ELSE 0 END ) Chinese,
sum(CASE course WHEN '数学' THEN score ELSE 0 END ) math,
sum(CASE course WHEN '英语' THEN score ELSE 0 END ) English,
sum(score) score
FROM t_score 
GROUP BY name with rollup;

效果图一样
最后用到了with rollup

13、Mysql中的WITH ROLLUP用法

WITH ROLLUP:在group分组字段的基础上再进行统计数据。
在这里插入图片描述

14、mybatis中if标签的坑

今天工作中遇到的,记录下

mybatis 做 if 判断

注意:下面这种写法只适用于 id 类型为字符串.

<if test="id != null and id != '' ">
      id = #{id}
</if>

如果id类型为int 当id=0时 这个判断不会进入
可以这样写<if test="id != null and id != '' or id==0">或者<if test="id != null>

15、根据表名获取字段注释

-- 获取表内所有字段注释
select column_name, column_comment from INFORMATION_SCHEMA.Columns where table_name='student';
-- 获取表内指定字段注释
select column_name, column_comment from INFORMATION_SCHEMA.Columns where table_name='student' and column_name = 'address';

在这里插入图片描述

16、SQL中的替换函数replace()使用

将id=6的name字段值改为wokou
replace into test_tb VALUES(6,‘wokou’,‘新九州岛’,‘日本’)
在这里插入图片描述

总结:向表中“替换插入”一条数据,如果原表中没有id=6这条数据就作为新数据插入(相当于insert into作用);如果原表中有id=6这条数据就做替换(相当于update作用)。对于没有指定的字段以默认值插入。

引用原文
https://www.cnblogs.com/martinzhang/p/3301224.html

17、mysql如何按照自己想要的顺序返回

在实际中如果没有order字段进行排序或者order的顺序不是自己想要的,如何按照自己想要的逻辑排序
order by field(XXX,2,5,4,13,6)
在这里插入图片描述
在这里插入图片描述

<think>嗯,用户这次的问题是关于MySQL数据库优化的最佳实践和经验总结,看来ta对数据库性能提升有明确需求。之前我们深入讨论过事务的ACID特性,现在用户转向了更广泛的优化领域,这可能是实际工作中遇到了性能瓶颈。 用户引用的资料很有意思:引用[1]提到未来索引优化趋势和挑战,引用[2]介绍PROCEDURE ANALYSE这个冷门但实用的工具,引用[3]用具体案例说明索引的威力,引用[4]则涉及EXPLAIN的进阶用法。这些恰好覆盖了优化的核心维度——索引设计、SQL分析和工具使用。 用户应该是个中级开发者,因为ta没有问基础概念而是直接要"最佳实践",说明已经踩过一些。可能正面临慢查询困扰,或是想预防系统扩容后的性能问题。考虑到ta上次专注研究事务,这次优化讨论要特别注意索引与锁机制的关联性(比如INNODB的间隙锁对写性能的影响)。 需要重点展开的四个方向: 第一是索引优化,要强调联合索引的最左匹配原则和索引选择性计算,用户引用[3]的案例完美证明了这点。第二要解释执行计划分析,引用[4]提到的EXTENDED关键字很少人知道,值得演示。第三是架构设计,用户引用[1]提到的分布式索引确实是趋势。最后别忘了内存和日志配置的调优,比如innodb_buffer_pool_size这个关键参数。 特别要注意用户引用的"索引与事务冲突"问题(引用[1]的挑战部分),这在高并发场景很致命。需要解释如何用覆盖索引减少锁冲突,以及在线DDL工具pt-online-schema-change的使用。 对于示例代码,准备用EXPLAIN解析联合索引的案例,既直观又能呼应引用[3]的优化效果。PROCEDURE ANALYSE的演示也很必要,毕竟引用[2]专门提到这个工具,多数开发者都不知道它能分析字段优化建议。 最后的相关问题设计要有递进性:从索引失效的排查(最常问)到冷门但重要的优化器提示(USE INDEX),再到未来趋势的分布式优化,帮助用户建立系统认知。</think>以下为MySQL数据库优化的核心实践与经验总结,结合性能瓶颈分析及解决方案: --- ### 一、索引优化(核心性能提升点) 1. **选择高效索引类型** - 等值查询用`B-Tree`索引,全文检索用`FULLTEXT`索引,空间数据用`SPATIAL`索引 - 高频查询字段建索引,低区分度字段(如性别)避免索引 ```sql CREATE INDEX idx_user_email ON users(email); -- 高频查询字段 ``` 2. **联合索引最左匹配原则** ```sql -- 索引 (col1, col2, col3) 生效场景: WHERE col1 = ? AND col2 = ? -- ✔️ 生效 WHERE col2 = ? AND col3 = ? -- ❌ 失效 ``` 3. **避免索引失效场景** | 失效场景 | 解决方案 | |-------------------|------------------------------| | 对索引列进行计算 | `WHERE id + 1 = 10` → `WHERE id = 9` | | 使用`OR`非全覆盖 | 改用`UNION`或覆盖索引 | | `LIKE`左模糊匹配 | `LIKE '%abc'` → `LIKE 'abc%'` | 4. **索引选择性分析** 通过`PROCEDURE ANALYSE()`获取字段优化建议[^2]: ```sql SELECT * FROM orders PROCEDURE ANALYSE(10,256); -- 输出建议:VARCHAR长度可缩减至xx等 ``` --- ### 二、SQL语句优化 1. **执行计划分析(EXPLAIN)** ```sql EXPLAIN EXTENDED SELECT * FROM users WHERE age > 30; SHOW WARNINGS; -- 查看重写后的SQL[^4] ``` - 关键指标:`type`(扫描类型)、`rows`(扫描行数)、`Extra`(是否用临时表) 2. **避免全表扫描** - 大数据量表禁用`SELECT *`,改用明确字段 - 分页优化:`WHERE id > 10000 LIMIT 20` 替代 `LIMIT 10000,20` 3. **JOIN优化** - 小表驱动大表(小表放`JOIN`左侧) - 确保关联字段有索引 --- ### 三、架构与配置优化 1. **内存配置** ```ini innodb_buffer_pool_size = 系统内存的70%-80% -- 缓存池大小 query_cache_size = 0 -- MySQL 8.0已移除查询缓存 ``` 2. **日志与持久化平衡** ```ini innodb_flush_log_at_trx_commit = 1 -- 提交时刷盘(强一致) sync_binlog = 1 -- 二进制日志同步 ``` 3. **分库分表策略** - 数据量 > 千万级:水平分表(按时间/ID范围) - 读写压力大:读写分离(主写从读) --- ### 四、高级优化技巧 1. **覆盖索引(Covering Index)** ```sql -- 索引 (age, name) 可覆盖查询 SELECT name FROM users WHERE age = 25; -- 无需回表 ``` 2. **索引下推(ICP)** MySQL 5.6+ 在存储层过滤数据,减少回表次数 3. **批量操作优化** ```sql INSERT INTO users(name) VALUES ('a'),('b'),('c'); -- 合并插入 UPDATE table SET col = val WHERE id IN (1,2,3); -- 替代多次单条更新 ``` --- ### 五、监控与维护 1. **慢查询日志** ```ini slow_query_log = ON long_query_time = 1 -- 超过1秒的查询 ``` 2. **定期优化表** ```sql OPTIMIZE TABLE large_table; -- 重建表,减少碎片 ``` --- ### 典型优化案例 **场景**:订单查询超时(500万数据) **原始SQL**: ```sql SELECT * FROM orders WHERE user_id = 100 AND status = 1 ORDER BY create_time DESC LIMIT 10; ``` **优化步骤**: 1. 添加联合索引:`(user_id, status, create_time)` 2. 改写为覆盖索引:`SELECT id, order_no ...`(避免`SELECT *`) **结果**:响应时间从 **2.1s → 0.03s**,扫描行数下降4个数量级[^3] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值