1

我想阅读 biiiig CSV-Files 并想将它们插入数据库。这已经有效:

if(($handleF = fopen($path."\\".$file, 'r')) !== false){
                        $i = 1;
                        // loop through the file line-by-line
                        while(($dataRow = fgetcsv($handleF,0,";")) !== false) {
                            // Only start at the startRow, otherwise skip the row.
                            if($i >= $startRow){
                                // Check if to use headers
                                if($lookAtHeaders == 1 && $i == $startRow){
                                    $this->createUberschriften( array_map(array($this, "convert"), $dataRow ) );
                                } else {
                                    $dataRow = array_map(array($this, "convert"), $dataRow );
                                    $data = $this->changeMapping($dataRow, $startCol);
                                    $this->executeInsert($data, $tableFields);
                                }
                                unset($dataRow);
                            }
                            $i++;
                        }
                        fclose($handleF);
                    }

我对这个解决方案的问题是,它非常慢。但是文件太大而无法直接放入内存中......所以我想问一下,是否有可能将例如 10 行读取到 $dataRow 数组中,而不仅仅是一个或全部。我想在内存和性能之间取得更好的平衡。

你明白我的意思吗?感谢帮助。

格雷茨

编辑: 好的,我仍然必须尝试使用​​ MSSQL 数据库找到解决方案。我的解决方案是堆叠数据,而不是制作一个多 MSSQL 插入:

 while(($dataRow = fgetcsv($handleF,0,";")) !== false) { 
                            // Only start at the startRow, otherwise skip the row.
                            if($i >= $startRow){ 
                                // Check if to use headers
                                if($lookAtHeaders == 1 && $i == $startRow){
                                    $this->createUberschriften( array_map(array($this, "convert"), $dataRow ) );
                                } else {

                                    $dataRow = array_map(array($this, "convert"), $dataRow );
                                    $data = $this->changeMapping($dataRow, $startCol);
                                    $this->setCurrentRow($i);
                                    if(count($dataStack) > 210){
                                        array_push($dataStack, $data);

                                        #echo '<pre>', print_r($dataStack), '</pre>';
                                        $this->executeInsert($dataStack, $tableFields, true);
                                        // reset the stack
                                        unset($dataStack);
                                        $dataStack = array();
                                    } else {
                                        array_push($dataStack, $data);
                                    }
                                    unset($data);
                                }
                                $i++;
                                unset($dataRow);
                            }
                        }

最后,我必须循环堆栈并在方法“executeInsert”中构建多个插入,以创建如下查询:

INSERT INTO [myTable] (field1, field2) VALUES ('data1', 'data2'),('data2', 'datta3')...

那效果要好得多。我仍然必须检查最佳平衡,但因此我只能更改上面代码中的值“210”。我希望对遇到类似问题的每个人都有帮助。注意:读取完整文件后不要忘记再次执行方法“executeInsert”,因为可能会发生堆栈中还有一些数据,并且只有在堆栈达到210大小时才会执行该方法。 ..

格雷茨

4

1 回答 1

0

我认为您的瓶颈不是读取文件。这是一个文本文件。您的瓶颈是 SQL 表中的 INSERT。

做一些事情,只需注释实际执行插入的行,您就会看到不同之处。

过去我也遇到过同样的问题,我完全按照你在做的事情。读取 5+ 百万行 CSV 并将其插入 Mysql 表中。执行时间是 60 小时,这是不现实的。

我的解决方案是切换到另一种数据库技术。我选择了 MongoDB,执行时间减少到 5 分钟。MongoDB在这种情况下执行得非常快,并且还有一个名为mongoimport的工具,可让您从命令行直接导入 csv 文件。

如果数据库技术不是您的限制,请尝试一下。

另一种解决方案是将巨大的 CSV 文件拆分为多个块,然后并行运行相同的 php 脚本多次,每个脚本都会处理文件名上带有特定前缀或后缀的块。

我不知道您使用的是哪个特定的操作系统,但在 Unix/Linux 中有一个名为 split 的命令行工具可以为您执行此操作,并且还会将您想要的任何前缀或后缀添加到块的文件名中。

于 2013-10-01T19:55:10.347 回答