Есть одна вещь, которую вы можете сделать ужасно неправильно при работе с RDBMS. И эта вещь не запускает ваши вычисления в базе данных, когда вы должны.
Мы не выступаем за то, чтобы слепо переносить всю бизнес-логику в базу данных, но когда я вижу такой вопрос переполнения стека , я чувствую побуждение мягко напомнить вам о втором пункте в наших популярных 10 распространенных ошибках, которые делают Java-разработчики при написании SQL,
Нежно напоминаю вам о втором пункте.
Немного предыстории этой иллюстрации здесь (на немецком языке) .
Вопрос переполнения стека по сути сводится к следующему (в широком смысле):
Из следующей таблицы среднего размера я хочу посчитать количество документов со статусом 0 или 1 для каждого идентификатора приложения:
AppID | DocID | DocStatus ------+-------+---------- 1 | 100 | 0 1 | 101 | 1 2 | 200 | 0 2 | 300 | 1 ... | ... | ...Должен ли я использовать Hibernate для этого?
И ответ: НЕТ! Не используйте Hibernate для этого (если вы не имеете в виду нативные запросы). Вы должны использовать SQL для этого . Es-Queue-Эл! У вас так много тривиальных опций, чтобы ваш SQL Server помог вам выполнить этот запрос за долю времени, которое потребуется, если вы загрузите все эти данные в память Java перед агрегированием!
Например (с использованием SQL Server):
Использование GROUP BY
Это самый тривиальный, но он может не возвращать результат именно так, как вы хотели, то есть разные результаты агрегации находятся в разных строках:
SELECT [AppID], [DocStatus], count(*) FROM [MyTable] GROUP BY [AppID], [DocStatus]
Пример на SQLFiddle , возвращающий что-то вроде
| APPID | DOCSTATUS | COLUMN_2 | |-------|-----------|----------| | 1 | 0 | 2 | | 2 | 0 | 3 | | 1 | 1 | 3 | | 2 | 1 | 2 |
Использование вложенных выборок
Это, вероятно, решение, которое искал именно этот пользователь. Они, вероятно, хотят, чтобы каждая агрегация была в отдельном столбце, и один очень общий способ добиться этого — использовать вложенные селекторы. Обратите внимание, что это решение может оказаться немного медленным в некоторых базах данных, которым трудно оптимизировать эти вещи.
SELECT [AppID], (SELECT count(*) FROM [MyTable] [t2] WHERE [t1].[AppID] = [t2].[AppID] AND [DocStatus] = 0) [Status_0], (SELECT count(*) FROM [MyTable] [t2] WHERE [t1].[AppID] = [t2].[AppID] AND [DocStatus] = 1) [Status_1] FROM [MyTable] [t1] GROUP BY [AppID]
Пример на SQLFiddle , возвращающий что-то вроде
| APPID | STATUS_0 | STATUS_1 | |-------|----------|----------| | 1 | 2 | 3 | | 2 | 3 | 2 |
Использование SUM ()
Это решение, вероятно, является оптимальным. Он эквивалентен предыдущему с вложенными выборами, хотя работает только для простых запросов, тогда как версия с вложенными выборами более универсальна.
SELECT [AppID], SUM(IIF([DocStatus] = 0, 1, 0)) [Status_0], SUM(IIF([DocStatus] = 1, 1, 0)) [Status_1] FROM [MyTable] [t1] GROUP BY [AppID]
Пример на SQLFiddle , тот же результат, что и раньше
Использование PIVOT
Это решение для любителей SQL среди вас. Он использует предложение T-SQL PIVOT
!
SELECT [AppID], [0], [1] FROM ( SELECT [AppID], [DocStatus] FROM [MyTable] ) [t] PIVOT ( count([DocStatus]) FOR [DocStatus] IN ([0], [1]) ) [pvt]
Пример на SQLFiddle , тот же результат, что и раньше
Вывод
Вы можете свободно выбирать свое оружие среди вышеупомянутых предложений, и я уверен, что есть больше альтернатив. Все они превзойдут любую реализацию агрегации на основе Java на порядки, даже для тривиально небольших наборов данных. Мы скажем это снова и снова, и мы снова и снова процитируем Гэвина Кинга за одно и то же:
То, что вы используете Hibernate, не означает, что вы должны использовать его для всего . Я делаю это уже около десяти лет.
И в наших словах:
Используйте SQL, когда это уместно! И это гораздо чаще, чем вы думаете!