mysql 如何优化关系求和查询?

ttvkxqim  于 2023-02-11  发布在  Mysql
关注(0)|答案(1)|浏览(100)

我有三张简单的table
1.发票(约50万条记录)
1.发票项,与发票的一对多关系(约1000万条记录)
1.发票付款,与发票的一对多关系(约70万条记录)
现在,听起来很简单,我需要查询未付发票。
下面是我正在使用的查询:

select * from invoices
    LEFT JOIN  (SELECT invoice_id, SUM(price) as totalAmount 
                FROM invoice_items 
                GROUP BY invoice_id) AS t1 
        ON t1.invoice_id = invoices.id
    LEFT JOIN  (SELECT invoice_id, SUM(payed_amount) as totalPaid 
                FROM invoice_payment_transactions 
                GROUP BY invoice_id) AS t2 
        ON t2.invoice_id = invoices.id
WHERE totalAmount > totalPaid

不幸的是,这个查询大约需要30秒,所以速度很慢。当然,我在付款和项目上都为"invoice_id"设置了索引。
当我"解释"查询时,我可以看到mysql必须进行全表扫描。我也尝试了其他几种查询方法,在子查询中使用"EXISTS"或"IN",但我从来没有绕过全表扫描。
很肯定这里没有什么可以做的(除了使用一些缓存方法),但也许有人知道如何优化它?我需要这个查询运行在+/-2秒最大。

    • 编辑:**

感谢大家的尝试,请记住,我绝对知道如何在这里采用不同的缓存策略,但这个问题纯粹是关于优化这个查询!
以下是(简化的)表定义

CREATE TABLE `invoices`
(
    `id`          bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `created_at`  timestamp           NOT NULL DEFAULT current_timestamp(),
    `date`        date                NOT NULL,
    `title`       enum ('M','F','Other')       DEFAULT NULL,
    `first_name`  varchar(191)                 DEFAULT NULL,
    `family_name` varchar(191)                 DEFAULT NULL,
    `street`      varchar(191)        NOT NULL,
    `postal_code` varchar(10)         NOT NULL,
    `city`        varchar(191)        NOT NULL,
    `country`     varchar(2)          NOT NULL,
    PRIMARY KEY (`id`),
    KEY `date` (`date`)
) ENGINE = InnoDB

CREATE TABLE `invoice_items`
(
    `id`          bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `invoice_id`  bigint(20) unsigned NOT NULL,
    `created_at`  timestamp           NOT NULL DEFAULT current_timestamp(),
    `name`        varchar(191)                 DEFAULT NULL,
    `description` text                         DEFAULT NULL,
    `reference`   varchar(191)                 DEFAULT NULL,
    `quantity`    smallint(6)         NOT NULL,
    `price`       int(11)             NOT NULL,
    PRIMARY KEY (`id`),
    KEY `invoice_items_invoice_id_index` (`invoice_id`),
) ENGINE = InnoDB

CREATE TABLE `invoice_payment_transactions`
(
    `id`                     bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `invoice_id`             bigint(20) unsigned NOT NULL,
    `created_at`             timestamp           NOT NULL DEFAULT current_timestamp(),
    `transaction_identifier` varchar(191)        NOT NULL,
    `payed_amount`           mediumint(9)                 DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `invoice_payment_transactions_invoice_id_index` (`invoice_id`),
) ENGINE = InnoDB
wj8zmpe1

wj8zmpe11#

A计划:
按invoice_id和 day 列出的汇总表。(按Bill建议)Summary Tables
B计划:
将设计更改为“当前”和“历史”。这是“付款”是货币易手的“历史”。同时,“发票”将是“当前”的,因为它包含“余额_欠款”列。这是一个哲学上的变化;它可以(应该)封装在客户子例程和/或数据库存储过程中。
计划C:如果“大部分”发票都付清了,这可能会有用。
在发票表中设置一个标记来表示已付清,这将防止“大多数”JOIN发生(添加该列和执行计划B一样困难)。

相关问题