eclipselink jpa генерирует количество запросов, используя COUNT (id) вместо COUNT (*)

1

Я использую Eclipselink, Spring Data и Postgresql. В моем проекте я заметил, что при использовании постраничных результатов, предоставляемых репозиториями SpringData, есть такие запросы, как:

SELECT COUNT(id) 
FROM table 
WHERE [part generated according to specification]

где "id" является первичным ключом "таблицы". Копаясь с объяснением, я заметил, что COUNT (id) примерно в 10 раз медленнее, чем COUNT() для очень большой таблицы (count (id) ищет ненулевые значения в столбце "id", а count() просто возвращает количество строк, совпадающих критерии), также count (*) может использовать индексы, в то время как count (id) - not.

Я проследил класс базового репозитория SpringData, и, похоже, для генерации этого запроса отвечает только реализация JPA.

  • В чем причина использования count (id) вместо более быстрого COUNT (*)?
  • Могу ли я изменить это поведение (в любом случае - даже увеличивая существующие компоненты)?

любая помощь оценена

- [edit] -

есть таблица:

\d ord_order
                                       Table "public.ord_order"
         Column          |           Type            |                       Modificators
-------------------------+--------------------------+----------------------------------------------------------
 id                      | integer                  | NOT NULL DEFAULT nextval('ord_order_id_seq'::regclass)
 test_order              | boolean                  | DEFAULT false
...
Indexes:
    "pk_order" PRIMARY KEY, btree (id)
    "idx_test_order" btree (test_order)



# explain SELECT COUNT(*) FROM ord_order WHERE (test_order = false);
                                QUERY PLAN
--------------------------------------------------------------------------
 Aggregate  (cost=89898.79..89898.80 rows=1 width=0)
   ->  Index Only Scan using idx_test_order on ord_order  (cost=0.43..85375.37 rows=1809366 width=0)
         Index Cond: (test_order = false)
         Filter: (NOT test_order)
(4 wiersze)



# explain SELECT COUNT(id) FROM ord_order WHERE (test_order = false);
                                QUERY PLAN
--------------------------------------------------------------------------
 Aggregate  (cost=712924.52..712924.53 rows=1 width=4)
   ->  Seq Scan on ord_order  (cost=0.00..708401.10 rows=1809366 width=4)
         Filter: (NOT test_order)
(3 wiersze)

теперь разница составляет ~ 90k против ~ 713k и индексное сканирование против полного сканирования

  • 0
    Пожалуйста, опубликуйте планы выполнения - если id действительно является первичным ключом таблицы, в это трудно поверить. Обычный миф состоит в том, что count(id) быстрее, чем count(*) поэтому, возможно, разработчики EclipseLink поверили в этот миф (я никогда не видел, чтобы это было правдой)
  • 0
    добавлены планы запросов в основном описании. Как count (id) может быть быстрее, чем count ( ), в то время как (согласно w3schools: w3schools.com/sql/sql_func_count.asp ) count ( ) просто возвращает количество критериев соответствия строк, в то время как count (id) возвращает количество критериев соответствия строк И имеющий ненулевое значение. Он делает что-то дополнительно, поэтому он не может быть быстрее ... может быть, какой-то конкретный индекс мог бы ускорить его, но не выше скорости счета (*) (в аренду я так думаю)
Показать ещё 3 комментария
Теги:
hibernate
jpa
spring-data-jpa

2 ответа

0
Лучший ответ

Мне удалось выполнить обычную реализацию базового класса репозитория Spring и factory с использованием этой реализации. В результате сгенерированные запросы счетчика теперь имеют форму:

SELECT COUNT(1) FROM table

который имеет тот же план, что и COUNT (*). Это кажется прекрасным решением и работает по всему миру для всех определенных хранилищ в приложении.

Я не знал, как генерировать COUNT (*), COUNT (1) было намного проще, поскольку функция COUNT ожидает некоторые выражения в качестве параметров, и я могу поставить статическое значение - 1

0

count(*) может использовать индекс, потому что в запросе указывается только один столбец (test_order). count(id) ссылается на два столбца, и для публикации результата Postgres должен выбрать столбец id столбца и test_order.

Как я уже говорил, некоторые люди считают, что count(id) быстрее, чем count(*) - когда ограничений на запрос нет. Миф, который никогда не был прав для любой СУБД с достойным оптимизатором. Я предполагаю, что ваш слой обфускации использует count(id) вместо count(*).

Предполагая, что вы не хотите избавляться от ORM (чтобы снова получить контроль над SQL, используемым вашим приложением), единственным обходным решением, которое я вижу, является создание частичного индекса, который может использовать Postgres:

create index on ord_order (id)
where test_order = false;
  • 0
    это обходной путь, который я уже рассмотрел и, вероятно, последую в краткосрочной перспективе. Проблема в том, что это «локальное» решение - работает только для этой единственной таблицы и этого единственного запроса. Как я уже упоминал, запросы строятся динамически, поэтому условия могут меняться (наиболее часто встречающийся вариант, который я здесь разместил) ... Я бы лучше нашел способ изменить компонент QueryBuilder, чтобы он всегда использовал count (*) ...
  • 0
    @redguy: "достоинства" слоя запутывания ....
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню