› 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 · Mar 25, 2022 · 4331 views
    This topic created in 1648 days ago, the information mentioned may be changed or developed.

    有一个表

    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 replies  •  2022-03-25 18:43:13 +08:00
    CyJaySong
        1
    CyJaySong  
    OP
       Mar 25, 2022
    在套一个 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  
       Mar 25, 2022
    检查一下 sql_mode 。
    westoy
        3
    westoy  
       Mar 25, 2022
    ORDER BY date => ORDER BY `date`试试?
    CyJaySong
        4
    CyJaySong  
    OP
       Mar 25, 2022
    @wps353 看了一下,阿里云和本地的一样
    CyJaySong
        5
    CyJaySong  
    OP
       Mar 25, 2022
    @westoy 还是一样
    CyJaySong
        6
    CyJaySong  
    OP
       Mar 25, 2022
    之前那种销量很低,子查询居然全表查询了,换成这种更好
    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  
       Mar 25, 2022
    搞不好是 RDS 的 bug 。阿里云数据库并不是简单地跑一个 mysql 实例给你用,为了利用好资源,底层有很多他们自己实现的东西,甚至可能查询引擎都和 mysql 默认的不一样。
    jtwor
        8
    jtwor  
       Mar 25, 2022
    是不是 DATE_FORMAT 转成的是字符串 而不是 date 类型报的?
    encro
        9
    encro  
       Mar 25, 2022
    这么简单的问题。。。。。
    错误提示这么明显了。。。

    PRIMARY KEY (`date`, `account_type`) 重复了。。。。。
    encro
        10
    encro  
       Mar 25, 2022
    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  
       Mar 25, 2022
    sorry 是我看错了。
    encro
        12
    encro  
       Mar 25, 2022
    DATE_FORMAT(CURRENT_TIMESTAMP, '%Y-%m-%d') 直接换成 CURRENT_DATE 试试?
    encro
        13
    encro  
       Mar 25, 2022
    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
       Mar 25, 2022
    @encro 确实有性能问题,EXPLAIN 显示全表查询了。用#6 那种就不会了
    CyJaySong
        15
    CyJaySong  
    OP
       Mar 25, 2022
    @encro 回复#13 ,不是为了替换,这个表主要是统计每个账户类型的每日总日结余额,每次资金变动时,更新总日结余额。
    encro
        16
    encro  
       Mar 25, 2022
    哈哈,你需要一个物化视图。
    果然 pg 才是最好选择。
    encro
        17
    encro  
       Mar 25, 2022
    replace into trade_account_total_daily_record set `balance`=balance+10 where date=CURRENT_DATE and account_type = 4

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

    明白了,你是想不存在查前一天的,再累加。
    encro
        22
    encro  
       Mar 25, 2022
    我是想每天都只加当天的,没必要去加前一天的。
    encro
        23
    encro  
       Mar 25, 2022
    @CyJaySong

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

    索引顺序导致用不了主键索引。。。
    CyJaySong
        27
    CyJaySong  
    OP
       Mar 25, 2022
    @encro 回复#26, 用#6 就很快哦
    encro
        28
    encro  
       Mar 25, 2022
    @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  
       Mar 25, 2022
    LIMIT 1 不是只有一行结果吗?还需要 sum?
    encro
        30
    encro  
       Mar 25, 2022
    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
       Mar 25, 2022
    @encro 回复 #29 如果执行的时候数据库没有 account_type = 4 的数据呢,你看看结果会怎样
    encro
        32
    encro  
       Mar 25, 2022
    @CyJaySong

    不知道,前面也加了 isnull 。
    encro
        33
    encro  
       Mar 25, 2022
    就是看同表更新好像是需要建立临时表。不知道 8.0 了。
    CyJaySong
        34
    CyJaySong  
    OP
       Mar 25, 2022
    @encro 回复#32 在没有复合 account_type = 4 的数据的情况下,光有 isnull 没用
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   5342 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 06:15 · PVG 14:15 · LAX 23:15 · JFK 02:15
    ♥ Do have faith in what you're doing.