Статьи

Пожалуйста, запустите этот расчет в вашей РСУБД

Есть одна вещь, которую вы можете сделать ужасно неправильно при работе с 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]

Поклонники SQL используют PIVOT чирикать это

Пример на SQLFiddle , тот же результат, что и раньше

Вывод

Вы можете свободно выбирать свое оружие среди вышеупомянутых предложений, и я уверен, что есть больше альтернатив. Все они превзойдут любую реализацию агрегации на основе Java на порядки, даже для тривиально небольших наборов данных. Мы скажем это снова и снова, и мы снова и снова процитируем Гэвина Кинга за одно и то же:

То, что вы используете Hibernate, не означает, что вы должны использовать его для всего . Я делаю это уже около десяти лет.

И в наших словах:

Используйте SQL, когда это уместно! И это гораздо чаще, чем вы думаете!