mysql 如何优化大型查询?

bpzcxfmw  于 2023-06-28  发布在  Mysql
关注(0)|答案(1)|浏览(107)

我有一个CPU killing查询,需要10秒以上的时间来执行:

select 
  sum(t.total) payment_amount, 
  sum(
    IFNULL(
      t.credit, group_wallets_transactions.credit
    )
  ) cashbacked_amount, 
  case when group_wallets_transactions.type = "daily_cashback"
         or group_wallets_transactions.type = "payment_consume" then "cashback"
       else group_wallets_transactions.type end type, 
  t.business_id as b_id, 
  business_sub_categories.name sub_category, 
  business_sub_categories.selected_img, 
  group_wallets_transactions.created_at as date, 
  businesses.shop_name 
from 
  `group_wallets_transactions` 
  left join (
    (
      select 
        'cash' as pay_type, 
        sum(amount) as total, 
        round(
          sum(daily_percentage * amount / 100), 
          2
        ) as credit, 
        DATE_ADD(
          Date(created_at), 
          INTERVAL 1 DAY
        ) as created_at, 
        `user_id`, 
        `business_id` 
      from 
        `pos_transactions` 
      where 
        (`user_id` = 408670) 
        and `rewarding_model` = 'cash' 
      group by 
        DATE_ADD(
          Date(created_at), 
          INTERVAL 1 DAY
        ), 
        `user_id`, 
        `business_id`
    ) 
    union all 
      (
        select 
          'credit' as pay_type, 
          sum(amount) as total, 
          round(
            sum(daily_percentage * amount / 100), 
            2
          ) as credit, 
          DATE_ADD(
            Date(created_at), 
            INTERVAL 1 DAY
          ) as created_at, 
          `user_id`, 
          `business_id` 
        from 
          `club_transactions` 
        where 
          (`user_id` = 408670) 
          and `customer_cashback_status` = 1 
          and `club_transactions`.`had_payment_reward` = 1 
          and `club_transactions`.`rewarding_model` = 'cash' 
          and `status` = 'paid' 
        group by 
          DATE_ADD(
            Date(created_at), 
            INTERVAL 1 DAY
          ), 
          `user_id`, 
          `business_id`
      )
  ) as `t` on date(
    group_wallets_transactions.created_at
  ) = date(t.created_at) 
  and `group_wallets_transactions`.`type` in ('daily_cashback') 
  left join `businesses` on `businesses`.`id` = `t`.`business_id` 
  left join `business_sub_categories` on `businesses`.`sub_category_id` = `business_sub_categories`.`id` 
where 
  (
    `group_wallets_transactions`.`user_id` = 408670
  ) 
group by 
  `type`, 
  `business_id`, 
  `sub_category`, 
  `selected_img`, 
  `date`, 
  `shop_name` 
order by 
  `group_wallets_transactions`.`created_at` desc

它工作得很好,给了我预期的结果。但是由于表pos_transactionsclub_transactions已经变得很大(每个表都有大约100万行),现在上面的查询的执行时间超过了10秒。下面是EXPLAIN的结果:

此外,group_wallets_transactions不是table,它是view,大约6秒的执行时间只是用于运行它,它的逻辑是:

CREATE ALGORITHM = UNDEFINED DEFINER = `administrator` @`%` SQL SECURITY DEFINER VIEW `group_wallets_transactions` AS 
select 
  `wallets_transactions`.`user_id` AS `user_id`, 
  `wallets_transactions`.`type` AS `TYPE`, 
  cast(
    `wallets_transactions`.`created_at` as date
  ) AS `created_at`, 
  sum(
    `wallets_transactions`.`credit`
  ) AS `credit` 
from 
  `wallets_transactions` 
group by 
  `wallets_transactions`.`user_id`, 
  `wallets_transactions`.`type`, 
  cast(
    `wallets_transactions`.`created_at` as date
  )

有什么方法可以优化它吗?

我已经尝试在需要的列上放置一些复合索引,并使性能更好,但仍然需要10秒才能执行,表现得像一个瓶颈。下面是group_wallets_transactions视图的EXPLAIN结果:

nimxete2

nimxete21#

created_atDATE吗?还是DATETIME?(如果是DATE,我可以建议一种简化方法来加快查询速度。)
一些索引可能会有所帮助:

group_wallets_transactions:  INDEX(user_id, type, created_at)
club_transactions:  INDEX(user_id)
pos_transactions:  INDEX(user_id, rewarding_model)

请不要使用

SELECT DATE_ADD(...created_at...) AS created_at
    ... GROUP BY DATE_ADD(...created_at...) ...

很难知道使用的是哪一个created_at。也许这就是你的意图?

SELECT DATE_ADD(...created_at...) AS date_created
    ... GROUP BY date_created ...

请用它所来自的表(或别名)限定每个列名;我可能遗漏了一些分析,因为没有遵循什么来自哪里。
看着

SELECT  ....
        businesses.shop_name ...
    FROM ... (... GROUP BY ...) AS t
    LEFT JOIN  `businesses` AS b  ON b.`id` = `t`.`businesses_id`
    GROUP BY ...

我建议改写为

SELECT  ...
        ( SELECT b.shot_name FROM businesses AS b
                  WHERE b.id = t.businesses_id ) AS show_name
    FROM (SELECT ...
        FROM ... (... GROUP BY ...) AS t
        GROUP BY ...
         ) AS t2
    ORDER BY ...

前者在进行GROUPing之前会反复查找businesses.shop_name;后者每次只做一次。这可以提供显著的加速。
注意第二个GROUP BYbusiness_name的相对位置。

相关问题