2012-08-22 46 views
6
SELECT r.*, u.username 
FROM `reservation` AS r JOIN 
    `users` AS u 
WHERE u.id = r.user_id 
AND DATE(r.bx_date) >= DATE('2012-08-22') 
AND DATE(r.bx_date) <= DATE('2012-08-22') 
AND r.status='1' 
ORDER BY r.id desc 

bx_date - 預約日期。如何優化以下查詢?

運行此查詢需要8秒多的時間。我在預訂表中有超過500,000條記錄,在用戶表中有40,000條記錄。

我還沒有對我的數據庫表進行任何優化。一點都沒有。只是PK。我該如何優化這個查詢?有什麼選擇來提高這個數據庫的性能。

感謝

表預約:

CREATE TABLE `reservation` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`user_id` int(10) unsigned NOT NULL COMMENT 'User ID', 
`tx_id` varchar(15) NOT NULL COMMENT 'Transaction ID', 
`tx_date` datetime NOT NULL COMMENT 'Transaction Date', 
`bx_date` date NOT NULL COMMENT 'Booking Date', 
`theater_id` int(10) unsigned NOT NULL COMMENT 'Theater ID', 
`movie_id` int(10) unsigned NOT NULL COMMENT 'Movie ID', 
`showtime_id` int(10) unsigned NOT NULL COMMENT 'Show Time ID', 
`category_id` int(10) unsigned NOT NULL COMMENT 'Category ID', 
`full_tickets` tinyint(2) unsigned NOT NULL COMMENT 'Number of Full Tickets', 
`half_tickets` tinyint(2) unsigned NOT NULL COMMENT 'Number of Half Tickets', 
`no_seats` tinyint(3) unsigned NOT NULL COMMENT 'No of Seats', 
`full_ticket_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT 'Full Ticket Price', 
`half_ticket_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT 'Half Ticket Price', 
`amount` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT 'Total Amount', 
`method` tinyint(1) unsigned NOT NULL COMMENT 'Payment Method 1=web 2=mobile 3=theater', 
`paymentgateway_id` int(10) unsigned NOT NULL COMMENT 'Payment Gateway ID', 
`payment_type` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0 = Cash 1=Credit Card', 
`status` tinyint(1) unsigned NOT NULL COMMENT 'Status 0=provisional 1=booked 2=canceled 3=auto canceld', 
`comment` text, 
`reservation_type` tinyint(1) unsigned NOT NULL COMMENT 'Reservation Type 0=complimentary 1=advamce booking 2=Mobile Booking, 3 - Theater Offline Booking', 
`complimentary_type` tinyint(1) unsigned NOT NULL COMMENT '0=none 1=loyalty rewards 2=marketing 3=promotional 4=show blocking 5=vip', 
`description` mediumtext NOT NULL COMMENT 'Complimentary Description', 
`title` varchar(10) DEFAULT NULL COMMENT 'Title', 
`fname` varchar(255) DEFAULT NULL COMMENT 'First Name', 
`lname` varchar(255) DEFAULT NULL COMMENT 'Last Name', 
`gender` tinyint(1) unsigned DEFAULT NULL COMMENT 'Gender 0=male 1=female', 
`dob` date DEFAULT '0000-00-00' COMMENT 'Date of Birth', 
`nic` varchar(10) DEFAULT NULL COMMENT 'NIC no', 
`address` text COMMENT 'Address Line 1', 
`city` varchar(255) DEFAULT NULL COMMENT 'City', 
`district` varchar(50) DEFAULT NULL COMMENT 'District ', 
`country` varchar(255) DEFAULT NULL COMMENT 'Country', 
`mobile` varchar(15) DEFAULT NULL COMMENT 'Mobile No', 
`contact_phone` varchar(15) DEFAULT NULL COMMENT 'Fixed Land Phone Number', 
`email` varchar(255) DEFAULT NULL COMMENT 'Email Address', 
`timer` int(4) unsigned NOT NULL, 
PRIMARY KEY (`id`), 
KEY `user_id_index` (`user_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=555706 DEFAULT CHARSET=utf8 

表用戶:

CREATE TABLE users (id int(11) NOT NULL AUTO_INCREMENT, 
title varchar(100) NOT NULL, 
name varchar(255) NOT NULL DEFAULT '', 
last_name varchar(255) NOT NULL, 
username varchar(150) NOT NULL DEFAULT '', 
email varchar(100) NOT NULL DEFAULT '', 
password varchar(100) NOT NULL DEFAULT '', 
usertype varchar(25) NOT NULL DEFAULT '', 
block tinyint(4) NOT NULL DEFAULT '0', 
sendEmail tinyint(4) DEFAULT '0', 
registerDate datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 
lastvisitDate datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 
activation varchar(100) NOT NULL DEFAULT '', 
params text NOT NULL, 
gender varchar(6) NOT NULL, 
date_of_birth date NOT NULL, 
nic_no varchar(10) NOT NULL, 
address varchar(255) NOT NULL, 
city varchar(100) NOT NULL, 
district varchar(100) NOT NULL, 
mobile varchar(15) NOT NULL, 
subscribe_sms tinyint(1) NOT NULL, 
contact_phone varchar(15) NOT NULL, 
newsletter_subscribe tinyint(1) NOT NULL, 
PRIMARY KEY (id), 
UNIQUE KEY username (username), 
KEY usertype (usertype), 
KEY idx_name (name), 
KEY idx_block (block), 
KEY email (email)) 
ENGINE=InnoDB AUTO_INCREMENT=34265 DEFAULT CHARSET=utf8 
+2

你能告訴我們從輸出'SHOW CREATE TABLE reservation','SHOW CREATE TABLE users'和你張貼在這裏選擇的解釋? (只需執行選擇,但前面有「EXPLAIN」一詞)。 – Konerak

+2

-1,因爲你甚至沒有嘗試優化它。至少包括'EXPLAIN'的輸出並說明你嘗試過的來自https://dev.mysql.com/doc/refman/5.6/en/optimization.html的內容。 –

+1

什麼數據類型是'bx_date'? – Bridge

回答

3

首先,我會改寫這樣的查詢:

SELECT r.*, u.username 
FROM 
`reservation` AS r 
INNER JOIN `users` AS u ON u.id = r.user_id 
WHERE 
r.bx_date = '2012-08-22' /* or use BETWEEN when you really have a range */ 
AND r.status='1' 
ORDER BY r.id desc 

的INNER JOIN只是一個di不同於你的語法,但它也是這樣。在我看來,這個不太容易出錯並且更加友善,因爲你不必在WHERE子句中分離出什麼是連接,什麼不連接。

由於bx_date列的類型爲DATE,因此您不需要函數DATE()就可以再次創建日期。通常,在列上使用函數可防止使用索引(如果該列存在)。

爲了在開始時保持簡單,只需將索引添加到經常用於連接的列或在WHERE子句中使用的列。

在你的情況下,用於JOIN的列已經是主鍵,所以它們隱含地已經有一個索引。

您可以添加一個索引是這樣的:

ALTER TABLE reservation ADD INDEX idx_reservation_bx_date (bx_date); /*if I remember correctly :) */ 

欲瞭解更多信息閱讀here

您也可以使用複合索引,這是多列索引。

UPDATE由於ypercube在意見建議:

ALTER TABLE reservation ADD INDEX idx_reservation_bx_date_status (bx_date, status); /*if I remember correctly :) */ 

然後,您可以檢查是否或什麼指數是通過將一個EXPLAIN在查詢前面使用。當然,如果指數是好的,你必須衡量,衡量和衡量。過度使用也不是一個好主意,因爲插入和更新變得更慢。要查看是否索引扣緊你的查詢,你必須確保它不是在緩存中的某個地方,這樣只是爲了測試與SQL_NO_CACHE這樣執行查詢:

SELECT SQL_NO_CACHE r.*, u.username 
FROM 
`reservation` AS r 
INNER JOIN `users` AS u ON u.id = r.user_id 
WHERE 
r.bx_date = '2012-08-22' /* or use BETWEEN when you really have a range */ 
AND r.status='1' 
ORDER BY r.id desc; 

-

</UPDATE> 

另一條規則是,當表格中有很多行時,索引非常少數不同值的列並不是一個好主意。您預訂的狀態欄只有4個不同的值。在此列上添加索引會使索引無用。它甚至會變得更糟,因爲MySQL可能會使用索引,然後必須進行額外的查找才能找到與索引位置相對應的實際數據。

一個很好的瞭解它是Use-the-index-Luke

+0

雖然在這裏有一個複合索引可能是一個好主意,無論是在'(bx_date,status)'還是'(status,bx_date)' –

+0

@ ypercube是的,好主意,補充說。 – fancyPants

+0

感謝您的支持 – Techie