累积和作为从属子查询

7xllpg7q  于 2021-06-15  发布在  Mysql
关注(0)|答案(3)|浏览(308)

我有一张table叫 transactions 它包含了卖家和他们的交易:销售,浪费,以及他们何时收到产品。基本结构如下:

seller_id    transaction_date    quantity    reason    product    unit_price
---------    ----------------    --------    ------    -------    ----------
1            2018-01-01                10    import          1         100.0
1            2018-01-01                -5    sale            1         100.0
1            2018-01-01                -1    waste           1         100.0
2            2018-01-01                -3    sale            4          95.5

我需要每个卖家的每日总结,包括他们的销售价值,浪费和开始库存。问题是,起始库存是截至给定日期的累计数量总和(也包括给定日期的进口量)。我有以下疑问:

SELECT
    t.seller_id,
    t.transaction_date,
    t.SUM(quantity * unit_price) as amount,
    t.reason as reason,
    (
        SELECT SUM(unit_price * quantity) FROM transactions
        WHERE seller_id = t.seller_id
            AND (transaction_date <= t.transaction_date)
            AND (
                transaction_date < t.transaction_date
                OR reason = 'import'
            ) 
    ) as opening_balance
FROM transactions t
GROUP BY 
    t.transaction_date,
    t.seller_id
    t.reason

查询工作正常,我得到了所需的结果。但是,即使在为外部查询和子查询创建索引之后,也需要花费太多的时间(大约30秒),因为 opening_balance 查询是一个从属子查询,它为每一行反复计算。
如何优化或重写此查询?
编辑:子查询有一个小错误,缺少where条件,我修复了它,但问题的本质是一样的。我创建了一个摆弄示例数据的工具:
https://www.db-fiddle.com/f/ma7mhufsehxexlfxhctgbz/2

xxe27gdn

xxe27gdn1#

下面使用用户定义变量的方法比使用相关子查询的方法性能更好。对你来说 temp 变量被用来解释计算逻辑,它也被输出。你可以忽略这一点。
您可以尝试以下查询(如果需要,可以添加更多解释):
查询

SELECT dt.reason,
       dt.amount,
       @bal := CASE
                 WHEN dt.reason = 'import'
                      AND @sid <> dt.seller_id THEN dt.amount
                 WHEN dt.reason = 'import' THEN @bal + @temp + dt.amount
                 WHEN @sid = 0
                       OR @sid = dt.seller_id THEN @bal
                 ELSE 0
               end                AS opening_balance,
       @temp := CASE
                  WHEN dt.reason <> 'import'
                       AND @sid = dt.seller_id
                       AND @td = dt.transaction_date THEN @temp + dt.amount
                  ELSE 0
                end               AS temp,
       @sid := dt.seller_id       AS seller_id,
       @td := dt.transaction_date AS transaction_date
FROM   (SELECT seller_id,
               transaction_date,
               reason,
               Sum(quantity * unit_price) AS amount
        FROM   transactions
        WHERE  seller_id IS NOT NULL
        GROUP  BY seller_id,
                  transaction_date,
                  reason
        ORDER  BY seller_id,
                  transaction_date,
                  Field(reason, 'import', 'sale', 'waste')) AS dt
       CROSS JOIN (SELECT @sid := 0,
                          @td := '',
                          @bal := 0,
                          @temp := 0) AS user_vars;

结果(请注意,我已订购 seller_id 先是然后 transaction_date )

| reason | amount | opening_balance | temp  | seller_id | transaction_date |
| ------ | ------ | --------------- | ----- | --------- | ---------------- |
| import | 1250   | 1250            | 0     | 1         | 2018-12-01       |
| sale   | -850   | 1250            | -850  | 1         | 2018-12-01       |
| waste  | -100   | 1250            | -950  | 1         | 2018-12-01       |
| import | 950    | 1250            | 0     | 1         | 2018-12-02       |
| sale   | -650   | 1250            | -650  | 1         | 2018-12-02       |
| waste  | -450   | 1250            | -1100 | 1         | 2018-12-02       |
| import | 2000   | 2000            | 0     | 2         | 2018-12-01       |
| sale   | -1200  | 2000            | -1200 | 2         | 2018-12-01       |
| waste  | -250   | 2000            | -1450 | 2         | 2018-12-01       |
| import | 750    | 1300            | 0     | 2         | 2018-12-02       |
| sale   | -600   | 1300            | -600  | 2         | 2018-12-02       |
| waste  | -450   | 1300            | -1050 | 2         | 2018-12-02       |

db fiddle视图

pxq42qpu

pxq42qpu2#

SELECT
    t.seller_id,
    t.transaction_date,
    SUM(quantity) as amount,
    t.reason as reason,
    quantityImport
FROM transaction t
inner join
(
 select sum(ifnull(quantityImport,0)) quantityImport,p.transaction_date,p.seller_id from 
 ( /* subquery get all the date and seller distinct row */
  select transaction_date  ,seller_id ,reason
  from transaction 
  group by seller_id, transaction_date
 )
 as p
 left join 
 (  /* subquery get all the date and seller and the import quantity */
   select sum(quantity) quantityImport,transaction_date  ,seller_id
   from transaction
   where reason='Import'
   group by seller_id, transaction_date
 ) as n
 on
   p.seller_id=n.seller_id
 and
   p.transaction_date>=n.transaction_date
 group by
   p.seller_id,p.transaction_date
) as q
where
  t.seller_id=q.seller_id
and
  t.transaction_date=q.transaction_date
GROUP BY 
    t.transaction_date,
    t.seller_id,
    t.reason;
nxowjjhe

nxowjjhe3#

做这样的事吗?

SELECT s.* ,@balance:=@balance+(s.quantity*s.unit_price) AS opening_balance FROM (
    SELECT t.* FROM transactions t
    ORDER BY t.seller_id,t.transaction_date,t.reason
) s
CROSS JOIN ( SELECT @balance:=0) AS INIT
GROUP BY s.transaction_date, s.seller_id, s.reason;

样品

MariaDB [test]> select * from transactions;
+----+-----------+------------------+----------+------------+--------+
| id | seller_id | transaction_date | quantity | unit_price | reason |
+----+-----------+------------------+----------+------------+--------+
|  1 |         1 | 2018-01-01       |       10 |        100 | import |
|  2 |         1 | 2018-01-01       |       -5 |        100 | sale   |
|  3 |         1 | 2018-01-01       |       -1 |        100 | waste  |
|  4 |         2 | 2018-01-01       |       -3 |       99.5 | sale   |
+----+-----------+------------------+----------+------------+--------+
4 rows in set (0.000 sec)

MariaDB [test]> SELECT s.* ,@balance:=@balance+(s.quantity*s.unit_price) AS opening_balance FROM (
    ->     SELECT t.* FROM transactions t
    ->     ORDER BY t.seller_id,t.transaction_date,t.reason
    -> ) s
    -> CROSS JOIN ( SELECT @balance:=0) AS INIT
    -> GROUP BY s.transaction_date, s.seller_id, s.reason;
+----+-----------+------------------+----------+------------+--------+-----------------+
| id | seller_id | transaction_date | quantity | unit_price | reason | opening_balance |
+----+-----------+------------------+----------+------------+--------+-----------------+
|  1 |         1 | 2018-01-01       |       10 |        100 | import |            1000 |
|  2 |         1 | 2018-01-01       |       -5 |        100 | sale   |             500 |
|  3 |         1 | 2018-01-01       |       -1 |        100 | waste  |             400 |
|  4 |         2 | 2018-01-01       |       -3 |       99.5 | sale   |           101.5 |
+----+-----------+------------------+----------+------------+--------+-----------------+
4 rows in set (0.001 sec)

MariaDB [test]>

相关问题