V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
MySQL 5.5 Community Server
MySQL 5.6 Community Server
Percona Configuration Wizard
XtraBackup 搭建主从复制
Great Sites on MySQL
Percona
MySQL Performance Blog
Severalnines
推荐管理工具
Sequel Pro
phpMyAdmin
推荐书目
MySQL Cookbook
MySQL 相关项目
MariaDB
Drizzle
参考文档
http://mysql-python.sourceforge.net/MySQLdb.html
CyJaySong
V2EX  ›  MySQL

同样是 MySQL 8,这个我问题我硬是没搞明白会这样,希望大家帮我分析一下

  •  1
     
  •   CyJaySong · 2022-03-25 14:31:10 +08:00 · 3150 次点击
    这是一个创建于 1000 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有一个表

    CREATE TABLE `trade_account_total_daily_record`
    (
        `date`         date           NOT NULL COMMENT '日期',
        `account_type` int            NOT NULL COMMENT '账户类型',
        `balance`      decimal(20, 2) NOT NULL DEFAULT 0.00 COMMENT '账户余额',
        `created_at`   datetime       NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
        `updated_at`   datetime       NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
        PRIMARY KEY (`date`, `account_type`)
    ) ;
    

    我执行

    SELECT DATE_FORMAT(CURRENT_TIMESTAMP, '%Y-%m-%d'), 1, IFNULL(SUM(balance),0) + 10
    FROM trade_account_total_daily_record WHERE account_type = 1 ORDER BY date DESC LIMIT 1 
    

    能正确给出结果, 但是执行

    INSERT INTO trade_account_total_daily_record (`date`, `account_type`, `balance`)
    SELECT DATE_FORMAT(CURRENT_TIMESTAMP, '%Y-%m-%d'), 1, IFNULL(SUM(balance),0) + 10
    FROM trade_account_total_daily_record WHERE account_type = 4 ORDER BY date DESC LIMIT 1
    ON DUPLICATE KEY UPDATE `balance`=VALUES(`balance`) 
    

    就报 Column 'date' cannot be null ,奇怪的是阿里云 RDS 数据库(mysql-8.0.25)会这样,我本地的 8.0.28 没问题

    34 条回复    2022-03-25 18:43:13 +08:00
    CyJaySong
        1
    CyJaySong  
    OP
       2022-03-25 15:21:04 +08:00
    在套一个 SELECT 就可以了。。。好奇怪
    ```
    INSERT INTO trade_account_total_daily_record (`date`, `account_type`, `balance`)
    SELECT tmp.* FROM(SELECT DATE_FORMAT(CURRENT_TIMESTAMP, '%Y-%m-%d'), 4, IFNULL(SUM(balance),0) + 10
    FROM trade_account_total_daily_record AS tmp WHERE account_type = 4 ORDER BY date DESC LIMIT 1) AS tmp
    ON DUPLICATE KEY UPDATE `balance`=VALUES(`balance`)
    ```
    wps353
        2
    wps353  
       2022-03-25 15:30:26 +08:00
    检查一下 sql_mode 。
    westoy
        3
    westoy  
       2022-03-25 15:32:51 +08:00
    ORDER BY date => ORDER BY `date`试试?
    CyJaySong
        4
    CyJaySong  
    OP
       2022-03-25 16:00:44 +08:00
    @wps353 看了一下,阿里云和本地的一样
    CyJaySong
        5
    CyJaySong  
    OP
       2022-03-25 16:01:24 +08:00
    @westoy 还是一样
    CyJaySong
        6
    CyJaySong  
    OP
       2022-03-25 16:06:58 +08:00
    之前那种销量很低,子查询居然全表查询了,换成这种更好
    INSERT INTO trade_account_total_daily_record (`date`, `account_type`, `balance`)
    SELECT DATE_FORMAT(CURRENT_TIMESTAMP, '%Y-%m-%d'), 4, IFNULL(SUM(tmp.balance),0) + 10,FROM (SELECT balance FROM trade_account_total_daily_record WHERE account_type = 4 ORDER BY `date` DESC LIMIT 1) AS tmp
    ON DUPLICATE KEY UPDATE `balance`=VALUES(`balance`)
    northernlights0
        7
    northernlights0  
       2022-03-25 16:22:23 +08:00
    搞不好是 RDS 的 bug 。阿里云数据库并不是简单地跑一个 mysql 实例给你用,为了利用好资源,底层有很多他们自己实现的东西,甚至可能查询引擎都和 mysql 默认的不一样。
    jtwor
        8
    jtwor  
       2022-03-25 16:37:28 +08:00
    是不是 DATE_FORMAT 转成的是字符串 而不是 date 类型报的?
    encro
        9
    encro  
       2022-03-25 16:40:26 +08:00
    这么简单的问题。。。。。
    错误提示这么明显了。。。

    PRIMARY KEY (`date`, `account_type`) 重复了。。。。。
    encro
        10
    encro  
       2022-03-25 16:40:49 +08:00
    SELECT DATE_FORMAT(CURRENT_TIMESTAMP, '%Y-%m-%d'), 1, IFNULL(SUM(balance),0) + 10
    FROM trade_account_total_daily_record WHERE account_type = 1 ORDER BY date DESC LIMIT 1


    这条语句写的莫名其妙。
    encro
        11
    encro  
       2022-03-25 16:43:10 +08:00
    sorry 是我看错了。
    encro
        12
    encro  
       2022-03-25 16:45:58 +08:00
    DATE_FORMAT(CURRENT_TIMESTAMP, '%Y-%m-%d') 直接换成 CURRENT_DATE 试试?
    encro
        13
    encro  
       2022-03-25 16:52:07 +08:00
    mysql replace into 应该比你这个更好用。
    SUM(balance) + LIMIT 1 我看不出这是想干啥?

    看起来希望:

    replace into trade_account_total_daily_record set `balance`=balance+10 where date=CURRENT_DATE and account_type = 4 ORDER BY date DESC LIMIT 1
    CyJaySong
        14
    CyJaySong  
    OP
       2022-03-25 16:53:17 +08:00
    @encro 确实有性能问题,EXPLAIN 显示全表查询了。用#6 那种就不会了
    CyJaySong
        15
    CyJaySong  
    OP
       2022-03-25 16:59:48 +08:00
    @encro 回复#13 ,不是为了替换,这个表主要是统计每个账户类型的每日总日结余额,每次资金变动时,更新总日结余额。
    encro
        16
    encro  
       2022-03-25 17:07:42 +08:00
    哈哈,你需要一个物化视图。
    果然 pg 才是最好选择。
    encro
        17
    encro  
       2022-03-25 17:08:47 +08:00
    replace into trade_account_total_daily_record set `balance`=balance+10 where date=CURRENT_DATE and account_type = 4

    更新日结用我这个就可以了。
    CyJaySong
        18
    CyJaySong  
    OP
       2022-03-25 17:23:43 +08:00
    @encro 回复#17 : REPLACE INTO 效率没得 ON DUPLICATE KEY UPDATE 高,而且你这句没达到我要的目的🤣
    CyJaySong
        19
    CyJaySong  
    OP
       2022-03-25 17:25:19 +08:00
    @northernlights0 回复#7 大概是吧,不过通过多套一个子查询解决了,反而提高了性能
    CyJaySong
        20
    CyJaySong  
    OP
       2022-03-25 17:26:18 +08:00
    @jtwor 回复#8 问题不在这儿,大概如#7 说的那样
    encro
        21
    encro  
       2022-03-25 17:56:20 +08:00
    @CyJaySong

    明白了,你是想不存在查前一天的,再累加。
    encro
        22
    encro  
       2022-03-25 17:57:30 +08:00
    我是想每天都只加当天的,没必要去加前一天的。
    encro
        23
    encro  
       2022-03-25 18:07:43 +08:00
    @CyJaySong

    ORDER BY date DESC LIMIT 1 ,一条语句主键,谈什么性能?
    CyJaySong
        24
    CyJaySong  
    OP
       2022-03-25 18:07:44 +08:00
    @encro #回复 21,22 是的,这样可以生成各账户类型的每日总结余额
    CyJaySong
        25
    CyJaySong  
    OP
       2022-03-25 18:09:30 +08:00
    @encro 回复#23 期初我也这样么认为的,但是即便是 LIMIT 1 情况下,SUM 函数会导致全表扫描,就很皮
    encro
        26
    encro  
       2022-03-25 18:09:36 +08:00
    where account_type = 4 ORDER BY date DESC LIMIT 1

    索引顺序导致用不了主键索引。。。
    CyJaySong
        27
    CyJaySong  
    OP
       2022-03-25 18:11:32 +08:00
    @encro 回复#26, 用#6 就很快哦
    encro
        28
    encro  
       2022-03-25 18:20:30 +08:00
    @CyJaySong

    你删掉当天存在的值再试试,说不定没跑查询或者命中 cache 呢。

    INSERT INTO trade_account_total_daily_record (`date`, `account_type`, `balance`)
    SELECT DATE_FORMAT(CURRENT_TIMESTAMP, '%Y-%m-%d'), 4, IFNULL(SUM(tmp.balance),0) + 10,FROM (SELECT balance FROM trade_account_total_daily_record WHERE account_type = 4 ORDER BY `date` DESC LIMIT 1) AS tmp
    ON DUPLICATE KEY UPDATE `balance`=VALUES(`balance`)


    我还没琢磨透 SUM(tmp.balance) 和 WHERE account_type = 4 ORDER BY `date` DESC LIMIT 1 的用意。。。。
    encro
        29
    encro  
       2022-03-25 18:21:52 +08:00
    LIMIT 1 不是只有一行结果吗?还需要 sum?
    encro
        30
    encro  
       2022-03-25 18:34:41 +08:00
    INSERT INTO trade_account_total_daily_record (`date`, `account_type`, `balance`)
    SELECT CURRENT_DATE , 4, IFNULL((SELECT balance FROM trade_account_total_daily_record WHERE account_type = 4 ORDER BY `date` DESC LIMIT 1),0) + 10
    ON DUPLICATE KEY UPDATE `balance`=`balance`

    ?
    CyJaySong
        31
    CyJaySong  
    OP
       2022-03-25 18:38:40 +08:00
    @encro 回复 #29 如果执行的时候数据库没有 account_type = 4 的数据呢,你看看结果会怎样
    encro
        32
    encro  
       2022-03-25 18:40:10 +08:00
    @CyJaySong

    不知道,前面也加了 isnull 。
    encro
        33
    encro  
       2022-03-25 18:40:40 +08:00
    就是看同表更新好像是需要建立临时表。不知道 8.0 了。
    CyJaySong
        34
    CyJaySong  
    OP
       2022-03-25 18:43:13 +08:00
    @encro 回复#32 在没有复合 account_type = 4 的数据的情况下,光有 isnull 没用
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1039 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 20:31 · PVG 04:31 · LAX 12:31 · JFK 15:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.