2016-09-29 1 views
2

У меня есть запрос, который в основном является select *. В разработке эта таблица составляет всего 30000 строк, но в производстве она будет намного больше. Поэтому я хочу использовать этот запрос лениво. Почему запрос ниже не ленив? Я использую Postgres 9.5.4.1.clojure.java.jdbc ленивый запрос

(do 
    (def pg-uri {:connection-uri "jdbc:postgresql://localhost/..."}) 
    (def row (atom 0)) 
    (take 10 (clojure.java.jdbc/query 
      pg-uri 
      ["select * from mytable"] 
      {:fetch-size 10 
      :auto-commit false 
      :row-fn (fn [r] (swap! row inc))})) 
    @row) ;;=> 300000 
+0

Может быть, это старше С.О. вопрос и ответы будут актуальны, не уверены: http://stackoverflow.com/questions/19728538/clojure-java-jdbc-query-large-resultset-lazily – Josh

ответ

2

Во-первых, см. https://jdbc.postgresql.org/documentation/83/query.html#query-with-cursor.

Решил это вот так.

(jdbc/with-db-transaction [tx connection] 
    (jdbc/query tx 
    [(jdbc/prepare-statement (:connection tx) 
           "select * from mytable" 
           {:fetch-size 10})] 
    {:result-set-fn (fn [result-set] ...)})) 

где :result-set-fn это функция, которая потребляет ленивый набор результатов.

with-db-transaction позаботился о autoCommit установлен на false. :fetch-size не отправлено из query, поэтому вам нужно сделать prepare-statement.

0

Вам не нужны транзакции и подготовленные материалы выше. Это использование :result-set-fn, что приводит к потреблению ленивой последовательности. Возможно, вы использовали вместо этого :row-fn.

Для получения более подробной информации please see The Clojure Cookbook. Имеется также версия для печати, которую я рекомендую.

Функция jdbc/query имеет несколько необязательных параметров ключевого слова, которые определяют, как он создает возвращаемый результирующий набор. Параметр: result-set-fn указывает функцию, которая применяется ко всему результату set (ленивая последовательность) до его возврата. Аргумент по умолчанию является функция DOALL:

(defn hi-lo [rs] [(first rs) (last rs)]) 

;; Find the highest- and lowest-cost fruits 
(jdbc/query db-spec 
      ["select * from fruit order by cost desc"] 
      :result-set-fn hi-lo) 
;; -> [{:grade nil, :unit nil, :cost 77, :appearance nil, :name "Kumquat"} 
;;  {:grade 1.4, :unit nil, :cost 10, :appearance "rotten", :name "Tomato"}] 

: строка-Fn параметр определяет функцию, которая применяется к каждой строке результата, как результат построен. По умолчанию аргумент функция идентичности:

(defn add-tax [row] (assoc row :tax (* 0.08 (row :cost)))) 

(jdbc/query db-spec 
      ["select name,cost from fruit where cost = 12"] 
      :row-fn add-tax) 
;; -> ({:tax 0.96, :cost 12, :name "Plum"} {:tax 0.96, :cost 12, :name "Fig"}) 
+1

К сожалению, это не с Postgres. См. Ссылку в моем ответе. Вам необходимо установить autoCommit в false и fetchSize, чтобы сделать выборку с помощью работы курсора, иначе весь результирующий набор все равно будет находиться в памяти. Проверьте, включив журналы Postgres: LOG: выполнить выборку из S_1/C_2: выберите * from ... Слово «выборка» здесь - это то, что вы должны увидеть с помощью курсора. Еще одна проверка, которую вы можете сделать, это «выбрать все» на большой таблице и записать время по сравнению с ограниченным запросом. Вы увидите, что ограниченный запрос выполняется намного быстрее, но с его извлечением не имеет значения. –

+1

Вы увидите, что ограниченный запрос выполняется намного быстрее, но с использованием курсора не имеет значения => то есть, если вы только реализуете, например, первый элемент. –

+1

Так что, возможно, он ленив только на стороне Clojure, а не на стороне JDBC/Postgres, тогда ... Я не экспериментировал так далеко. Вы можете отправить этот вопрос в группу Google clojure, так как там хранитель clojure.java.jdbc. –

Смежные вопросы