Есть одна вещь, которую вы можете сделать ужасно неправильно при работе с RDBMS. И эта вещь не запускает ваши вычисления в базе данных, когда вы должны.
Мы не выступаем за то, чтобы слепо переносить всю бизнес-логику в базу данных, но когда я вижу такой вопрос переполнения стека , я чувствую побуждение мягко напомнить вам о втором пункте в наших популярных 10 распространенных ошибках, которые делают Java-разработчики при написании SQL ,
Вопрос переполнения стека по сути сводится к следующему (в широком смысле):
Из следующей таблицы среднего размера я хочу посчитать количество документов со статусом 0 или 1 для каждого идентификатора приложения:
1234567AppID | 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
Это самый тривиальный, но он может не возвращать результат именно так, как вы хотели, то есть разные результаты агрегации находятся в разных строках:
1
2
3
|
SELECT [AppID], [DocStatus], count (*) FROM [MyTable] GROUP BY [AppID], [DocStatus] |
Пример на SQLFiddle , возвращающий что-то вроде
1
2
3
4
5
6
|
| APPID | DOCSTATUS | COLUMN_2 | |-------|-----------|----------| | 1 | 0 | 2 | | 2 | 0 | 3 | | 1 | 1 | 3 | | 2 | 1 | 2 | |
Использование вложенных выборок
Это, вероятно, решение, которое искал именно этот пользователь. Они, вероятно, хотят, чтобы каждая агрегация была в отдельном столбце, и один очень общий способ добиться этого — использовать вложенные селекторы. Обратите внимание, что это решение может оказаться немного медленным в некоторых базах данных, которым трудно оптимизировать эти вещи
1
2
3
4
5
6
7
8
9
|
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 , возвращающий что-то вроде
1
2
3
4
|
| APPID | STATUS_0 | STATUS_1 | |-------|----------|----------| | 1 | 2 | 3 | | 2 | 3 | 2 | |
Использование SUM ()
Это решение, вероятно, является оптимальным. Он эквивалентен предыдущему с вложенными выборами, хотя он работает только для простых запросов, тогда как версия с вложенными выборами более универсальна.
1
2
3
4
5
|
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
!
01
02
03
04
05
06
07
08
09
10
|
SELECT [AppID], [0], [1] FROM ( SELECT [AppID], [DocStatus] FROM [MyTable] ) [t] PIVOT ( count ([DocStatus]) FOR [DocStatus] IN ([0], [1]) ) [pvt] |
Поклонники SQL используют PIVOT
Пример на SQLFiddle , тот же результат, что и раньше
Вывод
Вы можете свободно выбирать свое оружие среди вышеупомянутых предложений, и я уверен, что есть больше альтернатив. Все они превзойдут любую реализацию агрегации на основе Java на порядки, даже для тривиально небольших наборов данных. Мы скажем это снова и снова, и мы снова и снова процитируем Гэвина Кинга за одно и то же:
То, что вы используете Hibernate, не означает, что вы должны использовать его для всего . Я делаю это уже около десяти лет.
И в наших словах:
Используйте SQL, когда это уместно! И это гораздо чаще, чем вы думаете!