MySQL оптимизировать левое соединение с подзапросом - PullRequest
1 голос
/ 17 октября 2019

Я беспокоюсь о производительности.

Возможно ли оптимизировать следующий запрос mysql?

SELECT u.name, t2.transactions, o2.orders FROM users AS u

LEFT JOIN (
    SELECT t.aid AS tuid, SUM( IF( t.status = 1, t.amount, 0 ) ) AS transactions
    FROM transactions AS t 
    WHERE ( t.datetime BETWEEN ('2018-01-01 00:00:00') AND ( '2020-01-01 23:59:59' ) ) GROUP BY t.aid
) AS t2 ON tuid = u.id 

LEFT JOIN (
    SELECT o.aid AS ouid, SUM(o.net) AS orders FROM orders AS o 
    WHERE ( o.date BETWEEN ('2018-01-01 00:00:00') AND ( '2020-01-01 23:59:59' ) ) GROUP BY o.aid 
) AS o2 ON ouid = u.id

WHERE u.status = 1
ORDER BY t2.transactions DESC

В основном мне нужно суммировать данные пользователей из нескольких таблиц (и иметь возможностьзаказать их)

Ответы [ 2 ]

2 голосов
/ 17 октября 2019

В вашем запросе нет очевидного антипаттерна производительности запроса. Производительность в значительной степени зависит от производительности двух подзапросов с предложениями group by.

Давайте посмотрим на один из них, чтобы найти некоторые улучшения.

SELECT t.aid AS tuid, 
       SUM( IF( t.status = 1, t.amount, 0 ) ) AS transactions
  FROM afs_transactions AS t 
 WHERE t.datetime BETWEEN '2018-01-01 00:00:00' AND '2020-01-01 23:59:59'
 GROUP BY t.aid

Это будет нормально, если выиметь индекс на afs_transactions.datetime.

Но весь подзапрос можно переписать

SELECT t.aid AS tuid, 
       SUM( t.amount ) AS transactions
  FROM afs_transactions AS t 
 WHERE t.datetime BETWEEN '2018-01-01 00:00:00' AND '2020-01-01 23:59:59'
   AND t.status = 1
 GROUP BY t.aid

В этом запросе будет использован составной индекс для (status, datetime). Если у вас много строк со значениями status, не равными 1, и у вас есть составной индекс, переписанный запрос будет быстрее.

Pro tip : BETWEEN дляЗначения datetime, как правило, плохой выбор, потому что, ну, 59:59. Попробуйте использовать < вместо BETWEEN <= для конца диапазона.

 WHERE t.datetime >= '2018-01-01'
   AND t.datetime <  '2020-01-02'   /* notice, it's the day after the range */
0 голосов
/ 19 октября 2019

Несколько JOIN ( SELECT ... ) раньше были убийцами производительности (до 5.6). Теперь может быть проблемой производительности.

Альтернативой является

SELECT u.name,
       ( SELECT ... WHERE ...=u.id ) AS transactions,
       ( SELECT ... WHERE ...=u.id ) AS orders
    FROM users AS u
    WHERE  u.status = 1
    ORDER BY  transactions DESC

Первый подзапрос является коррелированным подзапросом и выглядит как

       ( SELECT SUM( IF(status = 1, amount, 0)
            FROM  transactions
            WHERE  aid = u.id
              AND  datetime >= '2018-01-01'
              AND  datetime  < '2018-01-01' + INTERVAL 2 YEAR`
       ) AS transactions

(другой похож.)

Индексы:

users:         INDEX(status, name, id)   -- "covering"
transactions:  INDEX(aid, datetime)
orders:        INDEX(aid, date)  or  INDEX(aid, date, net)
...