Поведение базы данных HAVING-SUM против WHERE / DISTINCT против GROUP BY - PullRequest
1 голос
/ 17 декабря 2010

Предположим, у меня есть очень большая итоговая таблица, в которой мы храним сумму баллов активности, строку для каждого пользователя, для каждого дня и сумму активности - для каждого типа, отдельного столбца - который пользователь сделал в тот день.:

CREATE TABLE summry_data
(
    UserID INT NOT NULL,
    ActivityDate DATE,
    t1 INT NOT NULL,
    t2 INT NOT NULL,
    t3 INT NOT NULL,
    t4 INT NOT NULL,
    PRIMARY KEY(UserID, ActivityDate)
)

Каждое утро мы заполняем данные предыдущего дня.мы вставляем строку для каждого пользователя:

INSERT summery_data
SELECT UserID, '2010-12-16'
    , SUM(IF(TypeID = 1, Points, 0))
    , SUM(IF(TypeID = 2, Points, 0))
    , SUM(IF(TypeID = 3, Points, 0))
    , SUM(IF(TypeID = 4, Points, 0))
FROM activities
WHERE ActivityDate >= '2010-12-16' AND ActivityDate < '2010-12-17'
GROUP BY UserID

Данные таблицы выглядят примерно так:

UserID  ActivityDate   t1   t2  t3  t4
1       2010-01-01      0   82  0   0
1       2010-01-02      100 1   12  0
2       2010-01-01      0   0   0   41
2       2010-01-02      0   0   0   1
3       2010-01-02      0   0   0   106
3       2010-01-03      2   5   0   4

Таблица очень большая (10M + строк), если я хочу получитьсписок идентификаторов пользователей, у которых были какие-либо точки активности для t1, t2 или t3 (но мы не хотим считать t4) в любой день.мой конечный результат будет включать в себя UserID 1 и 3.

, какой из следующих запросов лучше:

SELECT DISTINCT UserID
FROM summery_data
WHERE t1 > 0 OR t2 > 0 OR t3 > 0

против

SELECT UserID
FROM summery_data
GROUP BY UserID
HAVING SUM(t1) > 0 OR SUM(t2) > 0 OR SUM(t3) > 0

, чтобы понять, что будетбыстрее, у меня есть вопрос о том, что происходит за кулисами:

  1. запрос DISTINCT, как база данных обеспечивает добавление только 1 идентификатора пользователя в набор результатов, проверяет ли онакаждый идентификатор пользователя, чтобы увидеть, если он уже существует в наборе?или поскольку таблица в любом случае кластеризована по UserID, просто сохраняйте переменную - при сканировании строк - последнего UserID, добавленного в набор результатов?

  2. в запросе DISTINCT, После того как база данныхнайти одну строку, которая соответствует критериям для текущего идентификатора пользователя, останавливает ли он проверку предиката в предложении where, пока не достигнет следующего идентификатора пользователя?

  3. в запросе GROUP BY при суммированиистолбец t1, когда база данных найдет запись о том, что столбец t1> 0, который будет соответствовать HAVING, прекратит ли суммировать другие строки t1 для текущего идентификатора пользователя (поскольку предикат> 0, что уже верно)?или, по крайней мере, он не суммирует другие столбцы (t2 и t3), поскольку в этом нет необходимости?или база данных сначала выполняет суммирование t1, t2 и t3 до , вычисляя предложение HAVING?

Примечание: я использую MySql в качестве сервера базы данных,Однако я хотел бы знать, если Sql Server или любые другие системы баз данных будут работать по-другому.

Любая помощь очень ценится.

Ответы [ 2 ]

2 голосов
/ 17 декабря 2010

Ваши запросы не идентичны, если вы разрешаете отрицательные числа в любом из (t1, t2, t3, t4).Рассмотрим следующие данные:

user_id   T1   T2   T3   T4
-------  ---  ---  ---  ---
   1      -2   0    0    0
   1       2   0    0    0
   2       1   0    0    0
   2       2   0    0    0

Ваш первый запрос (отдельный) будет включать в себя как пользователя 1, так и 2, поскольку для каждого пользователя имеется хотя бы одна строка со значением T1> 0.

Второй запрос (имеющий gby) исключит пользователя 1, так как сумма значений T1 равна 0 (даже если значения в группе> 0).Это также хороший пример разницы между наличием и местом.(ГДЕ работают с отдельными строками; HAVING работает с группой в целом).

Остальная часть ответа не только сильно зависит от поставщика, но и совершенно не имеет значения с точки зрения SQL, поскольку это база данных, котораяв конечном итоге делает выбор.Сказав, что, зная немного об этом, вы можете повлиять на оптимизатор, написав свои запросы определенным образом.

Вопрос 1

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

Сортировка .Сортировка результатов.Запустите отсортированный результат и отследите предыдущее значение.Это потенциально очень дорого (медленно), если не помещается в память.

Хеширование .Хеш-функция применяется ко всем строкам в наборе результатов.Результат сохраняется в промежуточной хеш-таблице.Это часто быстрее, чем сортировка.

Индексная прогулка .По сути, это тот же метод, что и для сортировки, но поскольку индекс уже отсортирован, этот шаг пропускается.

Вопрос 2

База данных, если она свободна, позволяет оценивать ваши предикаты в любом порядке.Вы не можете легко решить это самостоятельно.Оптимизатор может использовать эвристику или статистику, чтобы найти оптимальный порядок оценки.Он должен подчиняться тем же логическим принципам, что и остальные из нас.Когда любое из (t1 = 1 или t2 = 2 или t3 = 3) истинно, мы можем прекратить оценивать другие.

Вопрос 3

Нет.Это объясняется моим примером выше в отношении ГДЕ / ИМЕЮЩИХ.

2 голосов
/ 17 декабря 2010

Многие ваши конкретные вопросы зависят от реализации.

SQL-запросы являются декларативными.Они не указывают способы получения ответа, они просто указывают, что вы ищете.DMBS (система управления базами данных) определяет, как они применяются на практике.Большинство запросов SELECT содержат некоторый тип итерации сканирования таблицы (если только это не преодолевается индексом рассматриваемого поля), но вы не видите явного зацикливания в середине запроса.

Что я могуокончательно рекомендую, чтобы вы не использовали агрегатные функции, такие как суммы, если вас не интересуют фактические значения сумм.Используйте DISTINCT, если вы хотите получить те идентификаторы пользователя, которые имеют положительные значения в любом из этих трех полей в любой строке.Это, по крайней мере, дает DMBS шанс сделать правильную вещь и оптимизировать этот запрос.

Возможно, индекс мог бы помочь этому запросу, но не так существенно.Индексация действительно помогает в таких вещах, как объединение равенства в разных таблицах (это может потребовать m * n времени, когда вы объединяете таблицу с m строками в таблицу с n).Здесь все, что вы хотите сделать, это отфильтровать, если одно из этих 3 полей является положительным.В худшем случае вы посмотрите на каждый ряд один раз.Индекс UserId может помочь в сочетании с DISTNCT исключить проверку строк с пользователем, которого вы уже решили включить.

...