我有一个SQL查询,返回了大量的行。我想将查询结果保存到磁盘上的CSV文件中。然而,由于有太多的行,所以在SQL查询聚合所有行之前就会用完内存。
它看起来像这样,并且在查询部分失败:
(-> query format-transformer csv-writer)
我在 #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"}
的映射。我们使用 [(mapv str (:vals row)])
将映射转换为 write-csv 可以使用的东西(嵌套向量)。然后 write-csv 将其附加到您提供的文件中。
:result-set-fn
对于不使堆栈溢出至关重要。通过将其设置为 dorun
,您告诉它在处理时丢弃头部。
:row-fn
现在为每一行打开和关闭文件。更糟糕的是,它必须扫描整个文件以便写入每一行。虽然我没有测试过,但我认为如果你将对with-open
的调用移到sql/query
之外,这可能会更有效率。我还认为你实际上想设置:result-set-fn
而不是:row-set-fn
,根据最新的文档。我猜测默认的:result-set-fn
的doall
函数会起到同样的作用,或者你正在使用旧版本。 - schauehowith-open
内部速度更快。我尝试将write-csv
函数放入:result-set-fn
中,但最终返回的nil与行数相同。 - detran