2015-10-20 62 views
3

我有一个sql查询返回大量的行。我想将查询的结果保存到磁盘上的csv文件中。但是,因为有太多的行在内存用完之前,所有行都可以通过sql查询进行聚合。在clojure中写入大文件到磁盘

它看起来像这样,和查询部分时失败:

(-> query format-transformer csv-writer) 

回答

4

我想通了从#clojure IRC频道一些帮助。你需要两个库:

(ns myproj.example 
    (:require [[clojure.java.jdbc :as sql] 
      [clojure.data.csv :as csv]])) 

诀窍是处理每一行,因为它返回,然后丢弃它。 java jdbc库具有查询功能,该功能使用:row-fn:result-set-fn选项提供此功能。

(defn sql->csv [title] 
    (with-open [w (clojure.java.io/writer (str title ".csv") :append true)] 
    (sql/query ds (second query) 
      :row-fn (fn [row] 
         (csv/write-csv w [(mapv str (vals row))])) 
      :result-set-fn dorun))) 

有趣的部分是:row-fn:result-set-fn。每当从查询返回一行时,:row-fn被调度。每一行都是{:column1 "data" :column2 "data2"}形式的地图。我们将地图更改为write-csv可以使用的内容(嵌套向量):[(mapv str (:vals row)])。然后write-csv将它附加到您提供的文件中。

:result-set-fn对于不吹你的堆是至关重要的。通过将其设置为dorun,您可以告诉它在处理完成后丢弃头部。

这个特定的实现不提供列标题,这是留给读者的一个练习(我的太尴尬了,在公共论坛上发布)。

+0

我会假设你的':row-fn'现在打开并关闭每一行的文件。更糟糕的是,它必须扫描到您正在编写的每行文件的末尾。我没有对它进行测试,但我认为如果您将'with-open'调用放到'sql/query'之外就可以提高效率。根据最新的[docs](https://clojure.github.io/java.jdbc/),我也认为你实际上想设置':result-set-fn'而不是':row-set-fn' #clojure.java.jdbc /查询)。我猜''result-set-fn'的默认'doall'可能会发生相同的变化,或者您使用的是旧版本。 – schaueho

+0

@schaueho:好的,谢谢。我已经测试过这些变化,并且它在'with-open'内更快。我试着将'write-csv'函数放在':result-set-fn'中,但最终返回的行数和nils一样多。 – dan