2008-09-18 36 views
57

Làm cách nào để tạo chỉ mục vào phần ngày của trường DATETIME?Làm cách nào để tạo chỉ mục vào phần ngày của trường DATETIME trong MySql

mysql> SHOW COLUMNS FROM transactionlist; 
+-------------------+------------------+------+-----+---------+----------------+ 
| Field    | Type    | Null | Key | Default | Extra   | 
+-------------------+------------------+------+-----+---------+----------------+ 
| TransactionNumber | int(10) unsigned | NO | PRI | NULL | auto_increment | 
| WagerId   | int(11)   | YES | MUL | 0  |    | 
| TranNum   | int(11)   | YES | MUL | 0  |    | 
| TranDateTime  | datetime   | NO |  | NULL |    | 
| Amount   | double   | YES |  | 0  |    | 
| Action   | smallint(6)  | YES |  | 0  |    | 
| Uid    | int(11)   | YES |  | 1  |    | 
| AuthId   | int(11)   | YES |  | 1  |    | 
+-------------------+------------------+------+-----+---------+----------------+ 
8 rows in set (0.00 sec) 

TranDateTime được sử dụng để lưu các ngày và thời gian của một giao dịch khi nó xảy ra

Bảng My có hơn 1.000.000 hồ sơ trong đó và báo cáo kết quả

SELECT * FROM transactionlist where date(TranDateTime) = '2008-08-17' 

mất một thời gian dài.

EDIT:

Hãy nhìn vào bài viết trên blog này trên "Why MySQL’s DATETIME can and should be avoided"

+3

chú thích cảnh báo cho liên kết mà bạn đề xuất một giao diện và cơn thịnh nộ mà nó gần như biên giới ở điểm trẻ con. Và nhà văn không đánh bại bất kỳ lời chỉ trích nào, trong khi vẫn đề cập rằng anh ta đứng đằng sau những gì anh ta nói, nhưng quan điểm của anh ấy đang giảm dần với mỗi người. Nhưng vẫn không lãng phí thời gian, nếu bạn đọc nhận xét. – kommradHomer

Trả lời

50

Nếu tôi nhớ chính xác, điều đó sẽ chạy toàn bộ bảng quét vì bạn đang chuyển cột qua một hàm. MySQL sẽ ngoan ngoãn chạy hàm cho mỗi và mỗi cột, bỏ qua chỉ mục vì trình tối ưu hóa truy vấn không thể thực sự biết kết quả của hàm.

gì tôi sẽ làm là một cái gì đó như:

SELECT * FROM transactionlist 
WHERE TranDateTime BETWEEN '2008-08-17 00:00:00' AND '2008-08-18 23:59:59'; 

Điều đó sẽ cung cấp cho bạn tất cả những gì đã xảy ra trên 2008-08-17, và tất cả những gì đã xảy ra tại chính xác 2008-08-18 00:00:00. Nếu đó là vấn đề, bạn có thể thay đổi từ thứ hai thành '2008-08-17 23:59:59' và chỉ nhận được 2008-08-17.

+0

thực sự thực sự hiệu quả ... – Arfeen

+1

tôi từng nghĩ về cách sử dụng này chỉ để cắt ngắn cho 'YYYY-MM-DD 00:00:00' – kommradHomer

+3

Tôi biết đây là câu trả lời cũ, nhưng tôi cảm thấy bắt buộc phải chỉ ra rằng khi MySQL sử dụng so sánh chuỗi cho 'DATETIME'; truy vấn của bạn trả lại kết quả chính xác và không bao gồm các hàng có 'TranDateTime = 2008-08-18 00: 00: 00'. – Arth

0

gì 'giải thích' nói gì? (Chạy GIẢI THÍCH SELECT * FROM transactionlist nơi ngày (TranDateTime) = '2008-08-17')

Nếu nó không sử dụng chỉ số của bạn bởi vì kể từ ngày() chức năng, một truy vấn nhiều nên chạy nhanh:

CHỌN * TỪ danh sách giao dịch trong đó TranDateTime> = '2008-08-17' VÀ TranDateTime < '2008-08-18'

+1

Nếu bạn sử dụng ngày() bạn sẽ không đạt được chỉ mục. Mysql không thể sử dụng các chỉ mục bên trong các cuộc gọi hàm như thế. – JBB

3

Tôi không biết về các chi tiết cụ thể của mySql, nhưng những gì gây hại trong chỉ lập chỉ mục trường ngày toàn bộ?

Sau đó chỉ cần tìm kiếm:

select * from translist 
    where TranDateTime > '2008-08-16 23:59:59' 
     and TranDateTime < '2008-08-18 00:00:00' 

Nếu các chỉ số là b-cây hay cái gì khác đó là hợp lý, những cần được tìm thấy một cách nhanh chóng.

9

Tôi không có nghĩa là âm thanh dễ thương, nhưng một cách đơn giản sẽ là thêm một cột mới chỉ chứa phần ngày và chỉ mục trên đó.

+0

Yup - và thêm cột chỉ với phần thời gian và loại bỏ toàn bộ DATETIME. – JBB

+0

giải pháp hiện tại của tôi là thêm một trường khác gọi ‘date’ và khi tôi cập nhật TranDateTime ngày cũng được cập nhật. Bây giờ tôi có chỉ mục về ‘ngày’ và truy vấn nhanh hơn rất nhiều bởi bảng của tôi đã tăng kích thước lên +5% –

0

Thay vì tạo chỉ mục dựa trên hàm (nếu điều đó thậm chí có thể trong mysql), hãy đặt mệnh đề where của bạn làm so sánh phạm vi. Một cái gì đó như:

đâu TranDateTime> '2008-08-17 00:00:00 ' và TranDateTime < '2008-08-17 11:59:59')

này cho phép DB sử dụng chỉ mục trên TranDateTime (có một, phải không?) để thực hiện lựa chọn.

2

Valeriy Kravchuk về yêu cầu tính năng cho vấn đề này trên trang web MySQL được cho là sử dụng phương pháp này.

"Trong thời gian chờ đợi, bạn có thể sử dụng các cột ký tự để lưu trữ giá trị DATETIME dưới dạng chuỗi, chỉ với các ký tự đầu tiên được lập chỉ mục. Với một số cách sử dụng cẩn thận trong MySQL 5, bạn có thể tạo một giải pháp hợp lý dựa trên ý tưởng này."

Bạn có thể viết thường trình khá dễ dàng để thêm cột này và sau đó với trình kích hoạt, hãy giữ cột này được đồng bộ hóa. Chỉ số trên cột chuỗi này sẽ khá nhanh.

8

Bạn không thể tạo chỉ mục chỉ trên phần ngày tháng. Có lý do nào bạn phải làm không?

Thậm chí nếu bạn có thể tạo chỉ mục chỉ trong phần ngày, trình tối ưu hóa có thể vẫn không sử dụng nó cho truy vấn trên.

Tôi nghĩ rằng bạn sẽ thấy rằng

SELECT * FROM transactionlist WHERE TranDateTime BETWEEN '2008-08-17' AND '2008-08-18' 

là hiệu quả và làm những gì bạn muốn.

0

Tôi không biết về các chi tiết cụ thể của mySQL, nhưng tác hại của việc chỉ lập chỉ mục trường ngày toàn bộ là gì?

Nếu bạn sử dụng ma thuật chức năng cho * cây, băm, ... đã biến mất, vì để lấy giá trị, bạn phải gọi hàm. Nhưng, bởi vì bạn không biết kết quả trước, bạn phải quét toàn bộ bảng.

Không có gì để thêm.

Có thể bạn có ý nghĩa giống như chỉ mục được tính (được tính toán?) ... nhưng cho đến nay, tôi chỉ thấy điều này trong bộ đệm ẩn của Intersystems. Tôi không nghĩ rằng có một trường hợp trong cơ sở dữ liệu quan hệ (AFAIK).

Một giải pháp tốt, theo ý kiến ​​của tôi, là như sau (cập nhật clintp ví dụ):

SELECT * FROM translist 
WHERE TranDateTime >= '2008-08-17 00:00:00.0000' 
    AND TranDateTime < '2008-08-18 00:00:00.0000' 

Cho dù bạn sử dụng 00:00:00.0000 hoặc 00:00 theo ý kiến ​​của tôi làm cho không có sự khác biệt (tôi đã thường được sử dụng nó ở định dạng này).

0

Tạo một trường mới chỉ với các ngày convert(datetime, left(date_field,10)) và sau đó chỉ mục đó.

1

Giải pháp tốt nhất và tốt là sử dụng dấu thời gian làm thời gian, thay vì ngày giờ. Nó được lưu trữ dưới dạng INT và được lập chỉ mục đủ tốt. Cá nhân tôi gặp phải vấn đề như vậy trên bảng giao dịch, có khoảng triệu hồ sơ và làm chậm lại khó khăn, cuối cùng tôi chỉ ra rằng điều này gây ra bởi lĩnh vực chỉ mục xấu (datetime). Bây giờ nó chạy rất nhanh.

-2

Tại sao không có ai đề xuất sử dụng LIKE? Điều đó không thực hiện công việc sao? Nó sẽ nhanh như BETWEEN?

SELECT * FROM transactionlist where TranDateTime LIKE '2008-08-17%' 
1

datetime LIKE something% cũng sẽ không nắm bắt chỉ mục.

Sử dụng tính năng này: WHERE datetime_field> = curdate();
Điều đó sẽ nắm bắt những chỉ số,
và che hôm nay: 00: 00: 00 đến ngày hôm nay: 23: 59: 59
Xong.

5

Tùy chọn khác (liên quan đến ver 7.5.3 trở lên) là tạo cột được tạo/ảo dựa trên cột ngày giờ, sau đó lập chỉ mục.

CREATE TABLE `table` (
`my_datetime` datetime NOT NULL, 
`my_date` varchar(12) GENERATED ALWAYS AS (DATE(`my_daetime`)) STORED, 
KEY `my_idx` (`my_date`) 
) ENGINE=InnoDB; 
+0

lý do tại sao được lưu trữ chứ không phải ảo? – 0x13a

+0

Nếu bạn muốn chỉ mục nó phải được lưu trữ .. Nếu không có chỉ mục nó có thể là ảo –

+0

thx, tôi tưởng tượng, tho tôi đã nhầm lẫn với bài viết này https://www.percona.com/blog/2016/03/04/virtual -columns-in-mysql-and-mariadb/ – 0x13a

Các vấn đề liên quan