我有一个非常大的数据库表(超过700k记录),我需要导出到.csv文件。在导出之前,我需要检查一些选项(由用户通过GUI提供)并过滤记录。不幸的是,这个过滤操作无法通过SQL代码实现(例如,一列包含序列化数据,因此我需要反序列化,然后检查记录是否通过"过滤规则。
立即执行所有记录会导致内存限制问题,因此我决定以50k记录块的形式中断该过程。因此,我不是一次加载700k记录,而是加载50k记录,应用过滤器,保存到.csv文件,然后加载其他50k记录并继续(直到达到700k记录)。这样我就可以避免内存问题,但需要大约3分钟(如果记录数增加,这个时间会增加)。
在不改变数据库结构的情况下,还有其他方法可以完成此过程(在时间方面更好)吗?
提前致谢!
答案 0 :(得分:1)
最好的办法就是尽可能地让PHP脱离混合。始终是加载CSV或导出CSV的情况。
在下面,我有2600万行学生表。我将导出200K行。当然,学生表中的列数很小。主要用于测试我为学生提供校园信息的其他事项。但是你会得到我希望的想法。问题是您需要多长时间:
...然后检查记录"是否通过"过滤规则。
在没有PHP的情况下理论上可以通过db引擎自然发生。没有PHP应该是口头禅。但这还有待确定。关键是,让PHP处理脱离等式。 PHP有很多东西。数据库处理中的合适伙伴不是。
select count(*) from students;
-- 26.2 million
select * from students limit 1;
+----+-------+-------+
| id | thing | camId |
+----+-------+-------+
| 1 | 1 | 14 |
+----+-------+-------+
drop table if exists xOnesToExport;
create table xOnesToExport
( id int not null
);
insert xOnesToExport (id) select id from students where id>1000000 limit 200000;
-- 200K rows, 5.1 seconds
alter table xOnesToExport ADD PRIMARY KEY(id);
-- 4.2 seconds
SELECT s.id,s.thing,s.camId INTO OUTFILE 'outStudents_20160720_0100.txt'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
FROM students s
join xOnesToExport x
on x.id=s.id;
-- 1.1 seconds
以上带有200K行的1AM带时间戳的文件通过连接导出为CSV。花了1秒钟。
LOAD DATA INFILE
和SELECT INTO OUTFILE
是伴侣功能,对于一件事情来说,不能超过原始表格移动的速度。其次,人们似乎很少使用后者。如果人们可以通过用例和技巧了解所有这些内容,那么它们也很灵活。
对于Linux,使用LINES TERMINATED BY '\n'
...我现在使用上面的代码块在Windows机器上。唯一的区别往往是文件的路径和行终止符。
答案 1 :(得分:0)
除非你告诉它不这样做,否则php会立即将你的整个结果集淹没到RAM中。它被称为缓冲查询。当您的结果集包含超过几百行时,它不起作用,如您所发现的那样。
php的设计师让它使用缓冲查询,让需要读取几行数据并显示它们的网站开发人员的生活更简单。
您需要unbuffered query来做您正在做的事情。您的php程序将一次读取并处理一行。但是要小心使程序读取该无缓冲结果集的所有行;如果你在MySQL和你的php程序之间留下一个悬空的部分结果集,你真的可以搞砸了。
您没有说明您是使用mysqli
还是PDO
。它们都提供模式设置,使您的查询无缓冲。如果你使用的是旧式的mysql_
界面,那么你可能会运气不好。