簡體   English   中英

MYSQL:查詢一段時間不使用索引

[英]MYSQL: query doesn't use the index some time

測試數據庫:

SET NAMES utf8;
SET foreign_key_checks = 0;
SET time_zone = '+02:00';
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';

CREATE TABLE `account` (
  `idAccount` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  PRIMARY KEY (`idAccount`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

CREATE TABLE `users` (
  `idUser` int(11) NOT NULL AUTO_INCREMENT,
  `idAccount` int(11) NOT NULL,
  `firstName` varchar(128) NOT NULL,
  PRIMARY KEY (`idUser`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


DROP TABLE IF EXISTS `transactions`;
CREATE TABLE `transactions` (
  `idTransactions` int(11) NOT NULL AUTO_INCREMENT,
  `idUser` int(11) NOT NULL,
  `dateTransaction` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`idTransactions`),
  KEY `index_dateTransaction` (`dateTransaction`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


INSERT INTO `transactions` (`idTransactions`, `idUser`, `dateTransaction`) VALUES
(1, 1,  '2012-12-16 15:52:32'),
(2, 1,  '2012-12-20 15:52:37'),
(3, 1,  '2013-02-01 15:52:37'),
(4, 2,  '2013-03-16 15:52:37'),
(5, 2,  '2013-03-18 15:52:37'),
(6, 3,  '2014-04-19 15:52:37'),
(7, 3,  '2014-05-20 15:52:37'),
(8, 4,  '2014-06-21 15:58:46');

INSERT INTO `account` (`idAccount`, `name`) VALUES
(1, 'Burger & Burger');

INSERT INTO `users` (`idUser`, `idAccount`, `firstName` ) VALUES
(1, 1,  'Roberto'),
(2, 1,  'Alessandro');

取決於日期,有時MYSQL不使用INDEX。

我知道我需要添加/編輯INDEX,請您能幫我很好地執行此查詢嗎?

此查詢使用INDEX:

SELECT 
    users.firstName,
    ts1.*,
    COUNT(transactions.dateTransaction) AS num_transactions
FROM users
    INNER JOIN transactions ON transactions.idUser = users.idUser
    INNER JOIN ( 
        SELECT 
            users.idUser,
            MIN(transactions.dateTransaction) AS first_transaction,
            MAX(transactions.dateTransaction) AS last_transaction
        FROM transactions
            INNER JOIN users ON transactions.idUser = users.idUser
        WHERE (users.idAccount = 1) 
        GROUP BY users.idUser 
    ) AS ts1 ON users.idUser = ts1.idUser
WHERE 
    transactions.dateTransaction BETWEEN ('2012-01-01') AND ('2013-12-31')
AND users.idAccount = 1
GROUP BY users.idUser

解釋鏈接: http ://sqlfiddle.com/#!2/059d8/7/0

此查詢使用它:

SELECT 
    users.firstName,
    ts1.*,
    COUNT(transactions.dateTransaction) AS num_transactions
FROM users
        INNER JOIN transactions ON transactions.idUser = users.idUser
        INNER JOIN ( 
            SELECT 
                users.idUser,
                MIN(transactions.dateTransaction) AS first_transaction,
                MAX(transactions.dateTransaction) AS last_transaction
            FROM transactions
                INNER JOIN users ON transactions.idUser = users.idUser
            WHERE users.idAccount = 1
            GROUP BY users.idUser
        ) AS ts1 ON users.idUser = ts1.idUser
WHERE 
    transactions.dateTransaction BETWEEN ('2012-01-01') AND ('2012-12-31')
AND users.idAccount = 1
GROUP BY users.idUser

僅更改年份。

但是最大的問題是,在生產環境中,有大約65.000行事務,查詢掛起超過60秒(!)。

我創建了一個sqlfiddle,這是鏈接: http ://sqlfiddle.com/#!2/059d8/1/0

非常感謝你!

添加以下兩個索引:

ALTER TABLE `users` ADD KEY `bk1_account_user` (idAccount, idUser);

ALTER TABLE `transactions` KEY `bk2_user_datetrans` (idUser, dateTransaction);

這允許通過覆蓋索引來訪問所有表,並消除了一些ALL類型表。 有關詳細信息,請參見SQLfiddle: http ://sqlfiddle.com/#!2/b11bb/4

另外,考慮升級到5.6,以擺脫“使用連接緩沖區”。

這是有趣的。 我使用日期,如果過濾器明顯關閉(例如,使用2001年),mysql將使用其CONST表來計算查詢:

Impossible WHERE noticed after reading const tables

我懷疑日期列有一個強大的優化,我認為這會干擾索引計算。 但是我不確定...

盡管如此,您的查詢還是可以改進的。

看一下這個:

SELECT 
    users.firstName,
    ts1.*
FROM users
    JOIN ( 
        SELECT 
            users.idUser,
            MIN(transactions.dateTransaction) AS first_transaction,
            MAX(transactions.dateTransaction) AS last_transaction,
            COUNT(transactions.dateTransaction) AS num_transactions
        FROM transactions
            JOIN users ON transactions.idUser = users.idUser AND users.idAccount = 1
        WHERE 
            transactions.dateTransaction BETWEEN ('2011-01-01') AND ('2011-07-31')
        GROUP BY users.idUser
    ) AS ts1 ON users.idUser = ts1.idUser
WHERE 
   users.idAccount = 1
GROUP BY users.idUser;

我在子查詢中移動了COUNTWHERE子句,因此您只需要使用事務表一次。 但這意味着查詢的含義已更改,您必須檢查它是否是您想要的。 現在,該計數將僅計算這兩個日期之間的交易,而之前,該計數通常針對給定用戶對它們進行計數,而與日期無關。 如果您認為它不適合您的需求,請忽略我的更改。

從DDL的角度來看,我認為您可以像這樣改善它:

  1. 如果只有IF,您就有許多不同的用戶帳戶(idAccount的基數> 20-30),或多或少地平均分配:

user表上的鍵index_idAccountidAccount )。

2.更改現有索引index_dateTransaction以也使用idUser:

KEY index_dateTransactionidUserdateTransaction

最終結果如下:

在此處輸入圖片說明

您應該在transactions.idUser,users.idUser和transactions.dateTransaction上具有索引

如果我理解正確,那么您需要每個帳戶= 1的用戶的第一次和最后一次交易的日期,以及該用戶在一定時期內的交易總數。

最好這樣做:

SELECT  u.*,
        (
        SELECT  MIN(dateTransaction)
        FROM    transactions t
        WHERE   t.idUser = u.idUser
        ) minDate,
        (
        SELECT  MAX(dateTransaction)
        FROM    transactions t
        WHERE   t.idUser = u.idUser
        ) maxDate,
        (
        SELECT  COUNT(*)
        FROM    transactions t
        WHERE   t.idUser = u.idUser
                AND t.dateTransaction BETWEEN '2012-01-01' AND '2012-02-02'
        ) cnt
FROM    users u
WHERE   u.idAccount = 1

創建以下索引:

users (idAccount)
transactions (idUser, dateTransaction)

我沒有將主鍵包含在我應該在MyISAM表上完成的索引中,但是,除非有特殊原因(我認為您沒有),否則不應使用MyISAM。 將引擎更改為InnoDB。

看到這個小提琴: http ://sqlfiddle.com/#!2/ d92e6/3

附帶說明一下,如果此查詢很頻繁,則應考慮實現其某些結果。 如果將每個用戶的每日或每月交易計數保存在單獨的表中,該表將通過觸發器進行更新,則查詢中最昂貴的部分COUNT將消失,這將大大改善查詢。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM