mysql开发技巧

一、mysql开发技巧(join)

(本次操作数据库为 公司8.0开发库 java_test )

sql join的类型

内连接(join)

file

SELECT
    u.`user_name`,
    u.over,
    uf.over 
FROM
    `user` u
    JOIN user_friend uf ON u.`user_name` = uf.`user_name`

全外链接(full outer)

file

mysql不支持full join 使用左连接 union All 右链接

<-- full join -->
SELECT
    u.`user_name`,
    u.over,
    uf.over 
FROM
    `user` u
    LEFT JOIN user_friend uf ON u.`user_name` = uf.`user_name` 
UNION
SELECT
    uf.`user_name`,
    uf.over,
    u.over 
FROM
    `user` u
    RIGHT JOIN user_friend uf ON uf.`user_name` = u.`user_name` 

file

SELECT
    u.`user_name`,
    u.over,
    uf.over as over1 
FROM
    `user` u
    LEFT JOIN user_friend uf ON u.`user_name` = uf.`user_name` 
    where uf.over is null 
UNION ALL 
SELECT
    uf.`user_name`,
    uf.over,
    u.over as over1 
FROM
    `user` u
    RIGHT JOIN user_friend uf ON uf.`user_name` = u.`user_name`
where u.over is null
<-- 或者 -->
select * from
(SELECT
    u.`user_name`,
    u.over,
    uf.over as over1 
FROM
    `user` u
    LEFT JOIN user_friend uf ON u.`user_name` = uf.`user_name` 
UNION ALL 
SELECT
    uf.`user_name`,
    uf.over,
    u.over as over1 
FROM
    `user` u
    RIGHT JOIN user_friend uf ON uf.`user_name` = u.`user_name` ) t
where over1 is null

左外链接(left outer)

file

SELECT
    * 
FROM
    `user` u
    LEFT JOIN user_friend uf ON u.`user_name` = uf.`user_name`

不推荐使用 表B in 表a
file

<-- 不是悟空的结拜兄弟 -->
SELECT
    * 
FROM
    `user` u
    LEFT JOIN user_friend uf ON u.`user_name` = uf.`user_name` 
WHERE
    uf.user_name IS NULL

右外链接 (right out)

file

    SELECT
    * 
FROM
    `user` u
    RIGHT JOIN user_friend uf ON u.`user_name` = uf.`user_name` 

file

<--悟空的兄弟哪些没有去取经-->
    SELECT
    * 
FROM
    `user` u
    RIGHT JOIN user_friend uf ON u.`user_name` = uf.`user_name` 
WHERE
    u.user_name IS NULL

交叉链接 (cross)

file

笛卡尔积

SELECT
    * 
FROM
    `user`
    CROSS JOIN user_friend

使用join更新表

如何更新使用过滤条件中包括自身的表
请假:把同时存在于取经四人组和悟空结拜兄弟中的记录的人在取经四人组表中的over字段更新为“齐天大圣”

<-- 错误示范  不能更新在from从句中出现的表  (mysql不支持)-->
UPDATE `user` 
SET over = '齐天大圣' 
WHERE
    user_name IN ( SELECT u.`user_name` FROM `user` u JOIN user_friend uf ON u.`user_name` = uf.`user_name` )
<--优化2-->
update `user` u
    JOIN user_friend uf ON u.`user_name` = uf.`user_name`
set u.over = '齐天大圣'

使用join优化子查询

<--子查询-->
SELECT
    a.user_name,
    a.over,
    ( SELECT u.over FROM user_friend b WHERE a.user_name = b.user_name ) AS over1 
FROM
    `user` a;

<-- join优化 -->
SELECT
    a.user_name,
    a.over,
    b.over AS over1 
FROM
    `user` a
    LEFT JOIN user_friend b ON a.user_name = b.user_name;

使用join优化子查询聚合子查询

打怪最多的日期

<-- 教程 -->
SELECT
    a.user_name,
    b.timestr,
    b.kills 
FROM
    `user` a
    JOIN user_kills b ON a.id = b.user_id 
WHERE
    b.kills = ( SELECT MAX( c.kills ) FROM user_kills c WHERE c.user_id = b.user_id )
<-- 教材优化 -->
    SELECT
    a.user_name,
    b.timestr,
    b.kills
FROM
    `user` a
    JOIN user_kills b ON a.id = b.user_id 
  JOIN user_kills c ON c.user_id  = b.user_id
GROUP BY a.user_name,b.timestr,b.kills
HAVING b.kills = MAX(c.kills)
<-- 自己 (临时表优化子查询) -->
SELECT
    a.* 
FROM
    user_kills a
    INNER JOIN ( SELECT user_id, MAX( kills ) AS kills FROM user_kills GROUP BY user_id ) b ON a.user_id = b.user_id 
    AND a.kills = b.kills

如何实现分组数据的选择???

取经四人组每个人杀怪最多的前俩天

<-- 循坏查 不建议 -->

SELECT
    a.user_name,
    b.timestr,
    b.kills 
FROM
    `user` a
    JOIN user_kills b ON a.id = b.user_id 
WHERE
    a.user_name = "孙悟空" 
ORDER BY
    kills 
    LIMIT 2

file

<-- 优化 -->
SELECT
    d.user_name,
    timestr,
    kills,
    cnt 
FROM
    (
    SELECT
        user_id,
        timestr,
        kills,
        ( SELECT COUNT( * ) FROM user_kills b WHERE b.user_id = a.user_id AND a.kills < b.kills ) AS cnt 
    FROM
        user_kills a 
    GROUP BY
        user_id,
        timestr,
        kills 
    ) c
    JOIN `user` d ON c.user_id = d.id 
WHERE
    cnt <2

file
当前俩名有重复数据时会有问题?

SELECT
    u.user_name,
    a.timestr,
    a.kills
FROM
    `user` u
    INNER JOIN
    ( SELECT e.*, row_number ( ) OVER ( PARTITION BY user_id ORDER BY kills DESC ) AS row_num FROM user_kills e ) a 
    on u.id = a.user_id and a.row_num <= 2;
二、mysql开发技巧二(行列转换)

1.如何进行行列转换

行转列的场景:
2.1报表统计

file

2.2汇总显示

file

行转列
SELECT
    b.user_name,
    sum(a.kills)
FROM
    user_kills a INNER JOIN `user` b on a.user_id = b.id 
GROUP BY
    a.user_id
<--交叉链接  耗性能 -->
SELECT * FROM
    (SELECT sum( a.kills ) AS 沙僧 
    FROM user_kills a
        INNER JOIN `user` b ON a.user_id = b.id 
    WHERE
        b.user_name = "沙僧" 
    ) a
    CROSS JOIN (
    SELECT sum( a.kills ) AS 猪八戒 
    FROM user_kills a
        INNER JOIN `user` b ON a.user_id = b.id 
    WHERE
        b.user_name = "猪八戒" 
    ) b
    CROSS JOIN (
    SELECT sum( a.kills ) AS 孙悟空 
    FROM user_kills a
        INNER JOIN `user` b ON a.user_id = b.id 
    WHERE
    b.user_name = "孙悟空" 
    ) c
<--优化-->
SELECT
    sum( CASE WHEN user_name = '孙悟空' THEN kills END ) AS '孙悟空' ,
    sum( CASE WHEN user_name = '猪八戒' THEN kills END ) AS '猪八戒' ,
    sum( CASE WHEN user_name = '沙僧' THEN kills END ) AS '沙僧' 
FROM
    user_kills a
    INNER JOIN `user` b ON a.user_id = b.id
列转行

SELECT
user_name,
substring_index(substring_index(mobile, ',', a.id),',' ,- 1) AS mobile
FROM
tb_sequence a
CROSS JOIN (
SELECT user_name, mobile,
(length(mobile) - length(REPLACE(mobile, ',', '')) + 1) AS size
FROM
user
) b ON a.id <= b.size

file

<--耗性能-->
SELECT
    user_name,
    "arms" AS equipment,
    arms 
FROM
    `user` a
    JOIN `user_equipment` b ON a.id = b.user_id UNION ALL
SELECT
    user_name,
    "clothing" AS equipment,
    clothing 
FROM
    `user` a
    JOIN `user_equipment` b ON a.id = b.user_id UNION ALL
SELECT
    user_name,
    "shoe" AS equipment,
    shoe 
FROM
    `user` a
    JOIN `user_equipment` b ON a.id = b.user_id
ORDER BY user_name
<--序列化的方式-->
SELECT
    user_name,
CASE
        WHEN c.id = 1 THEN 'arms' 
        WHEN c.id = 2 THEN 'clothing' 
        WHEN c.id = 3 THEN 'shoe' 
    END AS equipment,
    COALESCE (
    CASE WHEN c.id = 1 THEN arms END,
    CASE WHEN c.id = 2 THEN clothing END,
    CASE WHEN c.id = 3 THEN shoe  END 
    ) AS eq_name 
    FROM
        user_equipment a
        JOIN `user` b ON a.user_id = b.id
        CROSS JOIN tb_sequence c 
WHERE
    c.id <= 3

    COALESCE是一个函数, (expression_1, expression_2, ...,expression_n)依次参考各参数表达式,遇到非null值即停止并返回该值。如果所有的表达式都是空值,最终将返回一个空值。使用COALESCE在于大部分包含空值的表达式最终将返回空值。

2.如何生成唯一序列号

file

file
一天之内不能重复

file

3.如何删除重复数据

file

<--分组查询重复数据 (group by + having)-->
SELECT user_name, `over`, count(*) 
FROM user1 
GROUP BY user_name, `over`
HAVING count(*) >1
删除重复数据保留id最大的
DELETE a 
FROM
    user1 a
    INNER JOIN (
    SELECT
        user_name,
        count( * ),
        MAX( id ) AS id 
    FROM
        user1 
    GROUP BY
        user_name 
    HAVING
        COUNT( * ) > 1 
    ) b ON a.user_name = b.user_name 
WHERE a.id < b.id
三、mysql开发技巧三

3.1 如何在子查询中匹配俩个值

==子查询:当一个查询是另一个查询的条件时,称之为子查询==

使用场景 :尽量使用连接查询代替子查询 可以避免在子查询表中重复出现的问题

更符合语意,更好了解。

单列:

<-- 子查询 自动去重-->
SELECT
    user_name 
FROM
    `user` 
WHERE
    id IN ( SELECT user_id FROM user_kills );

<-- 表链接 (一对多或者多对多的关系)DISTINCT去重-->
SELECT
    DISTINCT
    user_name 
FROM
    `user` u
    JOIN user_kills uk ON u.id = uk.user_id;

多列

file

<--连表的方式-->
SELECT
    a.user_name,
    b.timestr,
    b.kills 
FROM
    `user` a
    INNER JOIN user_kills b ON a.id = b.user_id
    INNER JOIN ( SELECT user_id, max( kills ) AS cnt FROM user_kills GROUP BY user_id ) c ON b.user_id = c.user_id 
    AND b.kills = c.cnt;

file

==多列join==

<-- 多列子查询-->
SELECT
    a.user_name,
    b.timestr,
    b.kills 
FROM
    `user` a
    JOIN user_kills b ON a.id = b.user_id 
WHERE
    ( b.user_id, b.kills ) IN ( SELECT user_id, max( kills ) FROM user_kills GROUP BY user_id )

3.2 解决同属性多值过滤问题

file

如何查询同时具有变化和念经这俩项技能的取经人???

    <--错误写法-->
    SELECT
    a.user_name,
    b.skill,
    b.skill_level 
FROM
    `user` a
    JOIN user_skill b ON a.user_name = b.user_name
 where skill in ("念经","变化") and skill_level>0

file

<--优化-->
SELECT
    user_name 
FROM
    (
    SELECT
        a.user_name,
        b.skill,
        b.skill_level 
    FROM
        `user` a
        JOIN user_skill b ON a.user_name = b.user_name 
    WHERE
        skill IN ( "念经", "变化" ) 
        AND skill_level > 0 
    ) t 
GROUP BY
    user_name 
HAVING
    count( user_name ) =2
 <--链接查询-->
SELECT
    a.user_name,
    b.skill,
    c.skill 
FROM
    `user` a
    JOIN user_skill b ON a.user_name = b.user_name
    JOIN user_skill c ON b.user_name = c.user_name 
WHERE
    b.skill = '念经' 
    AND c.skill = '变化' 
    AND b.skill_level > 0 
    AND c.skill_level > 0

满足几个技能中的任意俩个技能

<--group by-->
SELECT
    a.user_name
FROM
    `user` a
    JOIN user_skill b ON a.user_name = b.user_name 
WHERE
    skill IN ( "念经", "变化", "腾云", "浮水" ) 
    AND skill_level > 0 
GROUP BY
    b.user_name 
HAVING
    count( * ) >2

3.3 如何计算累进税类问题

file

file

file

SELECT
    user_name,sum( curmoney * rate ) 
FROM
    (SELECT a.user_name,a.money,b.low,b.high,
    LEAST( money - low, high - low ) AS curmoney,b.rate 
    FROM`user` a
        JOIN user_rate b ON a.money > b.low 
    ) t 
GROUP BY user_name

file

四、mysql开发技巧之虚拟列

作用适用于搜索
我们要模糊搜索用户表的用户昵称,与最终成就,按照我们以前的写法

select id from `user` where  user_name like "%aa%"
UNION
select id from `user` where  `over` like "%aa%"
<--创建虚拟列-->
alter table `user` add search varchar(50) generated always as(concat(`user_name`,`over`)) ;

虚拟列会自动将俩个值拼接起来
file

select id from `user` where search_test LIKE "%aa%"
讨论数量: 1

长篇大作啊,牛批~!

4年前

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!