我有以下两个MySQL / MariaDB表:
CREATE TABLE requests (
request_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
unix_timestamp DOUBLE NOT NULL,
[...]
INDEX unix_timestamp_index (unix_timestamp)
);
CREATE TABLE served_objects (
request_id BIGINT UNSIGNED NOT NULL,
object_name VARCHAR(255) NOT NULL,
[...]
FOREIGN KEY (request_id) REFERENCES requests (request_id)
);
每张表中有数百万列。每个请求有零个或多个serving_objects。我有一个视图,通过连接这两个表提供完整的serve_objects视图:
CREATE VIEW served_objects_view AS
SELECT
r.request_id AS request_id,
unix_timestamp,
object_name
FROM requests r
RIGHT JOIN served_objects so ON r.request_id=so.request_id;
到目前为止,这一切似乎都很简单。但是,当我像这样做一个简单的SELECT:
SELECT * FROM served_objects_view ORDER BY unix_timestamp LIMIT 5;
需要一分钟或更长时间。显然没有使用索引。我尝试了很多不同的方法,包括翻转东西并使用LEFT或INNER连接,但无济于事。
这是此SELECT的EXPLAIN的输出:
+------+-------------+-------+--------+---------------+---------+---------+------------------+---------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+---------------+---------+---------+------------------+---------+---------------------------------+
| 1 | SIMPLE | so | ALL | NULL | NULL | NULL | NULL | 5196526 | Using temporary; Using filesort |
| 1 | SIMPLE | r | eq_ref | PRIMARY | PRIMARY | 8 | db.so.request_id | 1 | |
+------+-------------+-------+--------+---------------+---------+---------+------------------+---------+---------------------------------+
这里有什么根本可以防止使用索引吗?我知道它需要使用临时表来满足视图,并且这会干扰使用索引的能力。但我希望存在一些技巧,允许我从视图中选择SELECT,同时尊重请求表中的索引。
答案 0 :(得分:2)
您正在使用臭名昭着的反对模式。
main()
您已经告诉查询规划人员复制整个视图(在RAM或临时存储中),对其进行排序,并抛出除五行之外的所有视图。所以,它服从了。它真的没关心它需要多长时间。
import csv
import os
import PyPDF2
os.chdir('C:/Users/User/Desktop')
def getText(happy_file):
pdf_file_obj = open(happy_file, 'rb')
pdf_reader = PyPDF2.PdfFileReader(pdf_file_obj)
pdf_reader.numPages #optional
page_obj = pdf_reader.getPage(0)
return page_obj.extractText()
def writeToCSV(happy_file):
output_file = open('myfinalfile.csv', 'w', newline ='')
output_writer = csv.writer(output_file)
output_writer.writerow([str(getText(happy_file))])
output_file.close()
def main():
writeToCSV("anyfile.pdf")
if __name__ == "__main__":
main()
通常被认为对查询效果有害,这就是为什么这是真的。
尝试此延迟加入优化
SELECT * FROM served_objects_view ORDER BY unix_timestamp LIMIT 5;
这会对较小的数据子集(只是request_id和timestamp值)进行排序。然后它获取视图行的一小部分。
如果它仍然太慢,请尝试在SELECT *
上创建复合索引。但这可能是不必要的。如果有必要,请专注于优化子查询。
备注:SELECT a.*
FROM served_objects_view a
JOIN (
SELECT request_id
FROM served_objects_view
ORDER BY unix_timestamp
LIMIT 5
) b ON a.request_id = b.request_id
?真?你不想要request (unix_timestamp, request_id)
吗?
答案 1 :(得分:0)
VIEWs
并不总是优化得很好。使用SELECT
时查询是否运行缓慢?您是否添加了建议的索引?
您使用的是什么版本的MySQL / MariaDB?新版本可能有优化改进,升级可能帮助。
我的观点是,你可能不得不放弃VIEW
。
答案 2 :(得分:0)
O. Jones提供的答案是正确的方法;谢谢!这里的大救星是,如果内部SELECT仅引用来自请求表的列(例如,仅选择request_id时的情况),优化器可以在不执行连接的情况下满足视图,使其变得非常分裂。
但是,我必须进行两次调整,以使其产生与原始SELECT相同的结果。首先,如果内部SELECT返回非唯一的request_ids,则外部JOIN会创建这些非唯一条目的叉积。通过将外部SELECT更改为SELECT DISTINCT,可以有效地丢弃这些重复的行。
其次,如果ORDER BY列可以包含非唯一值,则结果可以包含不相关的行。通过选择orderByCol并将AND a.orderByCol = b.orderByCol添加到JOIN规则,可以有效地丢弃这些。
所以我的最终解决方案,如果orderByCol来自请求表,效果很好,如下:
SELECT DISTINCT a.*
FROM served_objects_view a
JOIN (
SELECT request_id, <orderByCol> FROM served_objects_view
<whereClause>
ORDER BY <orderByCol> LIMIT <startRow>,<nRows>
) b ON a.request_id = b.request_id AND a.<orderByCol> = b.<orderByCol>
ORDER BY <orderByCol>;
这是一个比我希望的更复杂的解决方案,但它有效,所以我很高兴。
最后一条评论。 INNER JOIN和RIGHT JOIN在这里实际上是相同的,所以我最初是根据RIGHT JOIN来制定的,因为这就是我对其进行概念化的方式。但是,经过一些实验(在你的挑战之后),我发现INNER连接效率更高。 (如果内部SELECT仅引用请求表中的列,那么优化器可以在不执行连接的情况下满足视图。)再次感谢!