powered by simpleCommunicator - 2.0.56     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / SQLite [игнор отключен] [закрыт для гостей] / Упростить сложный агрегат
11 сообщений из 11, страница 1 из 1
Упростить сложный агрегат
    #36311799
Anton Sergunov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
SQLite последней версии (в смысле могу использовать любую)

Код: plaintext
1.
2.
CREATE TABLE g (a INTEGER, b INTEGER); -- группы с параметрами
CREATE TABLE i (g INTEGER REFERENCES g, a INTEGER, b INTEGER); -- элементы с параметрами
Надо по сути слепить два запроса.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
SELECT 
 expr2(g.a, g.b) as e2
 SUM(g.a), -- агрегат по группам
 SUM(g.b), -- следующий итд...
FROM g
WHERE expr1(g.a, g.b) -- В смысле далеко не все группы задействованы
GROUP BY e2; -- группируется не просто по группам, а как-то еще компактнее

SELECT 
 expr2(g.a, g.b) as e2
 SUM(i.a) -- агрегат по элементам групп
 SUM(i.b) -- следующий итд...
FROM g INNER JOIN i ON i.g = g.rowid
WHERE expr1(g.a, g.b) -- В смысле далеко не все группы задействованы
GROUP BY e2; -- группируется не просто по группам, а как-то еще компактнее

1. Сейчас так
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
SELECT 
 SUM(g.a), -- агрегат по группам
 SUM(g.b), -- следующий итд...
 SUM( SELECT SUM(i.a) FROM i WHERE g.rowid = i.g ), -- агрегат по элементам групп
 SUM( SELECT SUM(i.b) FROM i WHERE g.rowid = i.g ) -- следующий итд...
FROM g
WHERE expr1(g.a, g.b) -- В смысле далеко не все группы задействованы
GROUP BY expr2(g.a, g.b); -- группируется не просто по группам, а как-то еще компактнее
Это самый быстрый вариант из всех. Объяснил для себя тем, что таблица g фильтруется быстро и уже на этом этапе большинство откидывается, а элементов в группах < 10, так что потеря на преборах незначительна.

Вобщем этих селектов в реале много и конструкции "FROM i WHERE g.rowid = i.g" у них одинаковые хочется как-то написать их в одном месте.

Пробовал:
2. Казалось-бы самая логичная запись
Код: plaintext
1.
2.
3.
4.
5.
6.
SELECT expr2(g.a, g.b) as e1, SUM(g.a), SUM(g.b), SUM(t.sum_a), SUM(t.sum_b)
FROM g LEFT OUTER JOIN ( -- Элементов в группе может и не быть, но агрегат группы должен присутствовать
 SELECT i.g as g, SUM(i.a) as sum_a, SUM(i.b) as sum_b
 FROM i
) as t ON t.g = g.rowid
WHERE expr1(g.a, g.b)
GROUP BY e2;

но работает очень-очень медленно. Как будто таблица подзапроса генерируется вся, а потом сопоставляется.

3. Минимальный размер таблицы подзапроса
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
SELECT expr2(g.a, g.b) as e2, SUM(g.a), SUM(g.b), t.sum_a, t.sum_b
FROM g LEFT OUTER JOIN ( -- Элементов в группе может и не быть, но агрегат группы должен присутствовать
 SELECT expr2(g.a, g.b) as e2, SUM(i.a) as sum_a, SUM(i.b) as sum_b
 FROM i INNER JOIN g ON i.g=g.rowid
 WHERE expr1(g.a, g.b)
) as t ON t.e2 = e2
WHERE expr1(g.a, g.b)
GROUP BY e2;

сильно быстрее 2-го, но все-равно медленнее 1-го. Да и опять дубли "WHERE expr1(g.a, g.b)"

на деле expr1, expr2 довольно простые, так что можно делать индексы и в первом варианте индексы работают.
В остальных — не очень. Надо прописывать руками через INDEXED BY.

ANALYZE не помогает. Скорее даже наоборот.

Как это правильно делать?
...
Рейтинг: 0 / 0
Упростить сложный агрегат
    #36312731
MBG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
MBG
Гость
Лучше сначала ограничить выборку (можно подзапрос использовать), а потом делать группировки. Посмотреть, что же реально получается, так:

explain query plan ...
explain ...

Если запрос выполняется дольше десятков миллисекунд (откуда мне знать, что для вас значит "медленно"), бывает полезно создавать временные виды и таблицы.

Можно ускорить все запросы за счет увеличения размера страницы.
...
Рейтинг: 0 / 0
Упростить сложный агрегат
    #36312896
Anton Sergunov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Как составить запрос, чтоб оптимизатор его максимально хорошо оптимизировал.
Сейчас он только хуже делает.
...
Рейтинг: 0 / 0
Упростить сложный агрегат
    #36313490
MBG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
MBG
Гость
Вы читаете, что вам отвечают? Вместо того, чтобы методом тыка ковыряться, исследуйте указанным образом поведение ваших запросов, чтобы узнать, где какие индексы используются и какие нужно создать.
...
Рейтинг: 0 / 0
Упростить сложный агрегат
    #36313870
Anton Sergunov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Читаю. Делаю.
Выяснил что дело вовсе не в структуре запроса, а в кривом оптимизаторе.
Например однострочный запрос
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
EXPLAIN QUERY PLAN
SELECT  SUM(a.p1),
        COUNT(*),
        AVG(a.p2)
FROM    a INNER JOIN
        b ON a.b = b.id INNER JOIN
        c ON b.c = c.id
WHERE (a.user IN (SELECT DISTINCT owner FROM file))
        AND (c.p3 = ? 1  OR ? 1 = 0 );


Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
sqlite> .read query.txt
0|0|TABLE a WITH INDEX idx_a__u_a
1|1|TABLE b USING PRIMARY KEY
2|2|TABLE c USING PRIMARY KEY
0|0|TABLE file WITH INDEX idx_file__owner ORDER BY
sqlite> analyze;
sqlite> .read query.txt
0|2|TABLE c
1|0|TABLE a WITH INDEX idx_a__u_a
2|1|TABLE b USING PRIMARY KEY
0|0|TABLE file WITH INDEX idx_file__owner ORDER BY
sqlite>

На деле падение скорости после
Код: plaintext
ANALYZE
в ~200 раз. И это все на довольно простом запросе.
...
Рейтинг: 0 / 0
Упростить сложный агрегат
    #36315953
MBG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
MBG
Гость
Все дело в кривых руках. У вас отсутствует индекс по полю c.p3, о чем и говорит анализатор.
...
Рейтинг: 0 / 0
Упростить сложный агрегат
    #36318401
Anton Sergunov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
MBGВсе дело в кривых руках. У вас отсутствует индекс по полю c.p3, о чем и говорит анализатор.

Что уж вы меня так... При ?1 = 0 этот индекс работать не будет - переберутся все строки с. Всего у c.p3 значений 2 штуки разных может быть. Таблица сама маленькая (77 элементов), перебрать ее не долго. На изменение значения ?1, как мне показалось, оптимизатор не среагировал, как и на удаление всего условия . Да и не должен, потому что они пишут не используют индекс на "OR".

Индекс не даст увеличение скорости в 200 раз.

Как я вижу ситуацию:
Код: plaintext
1.
2.
 0 | 0 |TABLE a WITH INDEX idx_a__u_a
 0 | 0 |TABLE file WITH INDEX idx_file__owner ORDER BY
По индексу file.owner находит всех возможных a.user
По индексу a.user пробегает все подходящие а
Код: plaintext
1.
2.
 1 | 1 |TABLE b USING PRIMARY KEY
 2 | 2 |TABLE c USING PRIMARY KEY
По основному ключу прицепляет b и c

После анализа
Код: plaintext
 0 | 0 |TABLE file WITH INDEX idx_file__owner ORDER BY
По индексу file.owner находит всех возможных a.user
Код: plaintext
 0 | 2 |TABLE c
Для каждого с. Отсюда и падение скорости в row_count(c)/ln(row_count(c)) раз
Код: plaintext
 1 | 0 |TABLE a WITH INDEX idx_a__u_a
по индексу a.user пробегает a
Код: plaintext
 2 | 1 |TABLE b USING PRIMARY KEY
По основному ключу цепляет b

Почему он решает что так быстрее? Ведь у него есть спектры.

Причем они сами не отрицают, что анализатор кривой. Я думаю, надо оптимизировать запросы руками и всех делов.
...
Рейтинг: 0 / 0
Упростить сложный агрегат
    #36321070
MBG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
MBG
Гость
Раз уж вы заговорили о "кривости", я решил уточнить ;-) Ниже приведена аргументация.

Что касается анализатора, то с ним в данном случае все ок. Как пример приведу таблицу и запрос из биллинговой системы:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
sqlite> .schema hw_telephony_port
CREATE TABLE hw_telephony_port ( -- множества физ. портов составляют логические порты
        id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
        save_date REAL NOT NULL DEFAULT (julianday('now')),
        delete_date REAL,
        ats_id integer not null, -- идентификатор АТС
        args text not null, -- список параметров порта, например, {0} {1} для cisco или {1} {5} {3} для m200
        is_active integer not null default  0  check (is_active =  0  or is_active =  1 ),
        is_external integer not null default  0  check (is_external =  0  or is_external =  1 ),
        group_name text collate russian not null -- понятное оператору название группы портов
);
CREATE INDEX hw_telephony_port_complex1_idx on hw_telephony_port (ats_id,args,delete_date);
CREATE INDEX hw_telephony_port_group_name_idx on hw_telephony_port(group_name,delete_date);
sqlite> explain query plan select * from hw_telephony_port where ats_id= 11  and (args='01 01 01' or '01 01 01'='');
 0 | 0 |TABLE hw_telephony_port WITH INDEX hw_telephony_port_complex1_idx

Условия вида (args='01 01 01' or '01 01 01'='') совершенно обычные и используются повсеместно. Никаких проблем у оптимизатора с ними нет, как вы можете видеть выше. В системе, выбранной в качестве примера, несколько тысяч портов в означенной таблице, принадлежащих десятку АТС. За месяц накапливается пара миллионов записей о звонках, которые биллингуются целиком менее 20-ти минут - при том, что каждый номер проверяется по таблице внутренних и определяется направление звонка по таблице из десятков тысяч направлений (при реалтайм биллинговании система и вовсе "прохлаждается"); итого за 500 секунд обрабатывается около 10 миллионов запросов различного вида (поиск порта, номера внутреннего, направления звонка, услуги пользователя по номеру и т.п.), не считая выполнения скриптового кода (на языке tcl).

P.S. Посмотрите опции компиляции SQLite, сравните с версией из моего debian-репозитория, - от этих опций многое зависит. Мною написан набор патчей, но вот как раз планировщик не трогаю, ибо нет в том необходимости.

Код: plaintext
1.
deb http://mobigroup.ru/debian/ lenny main contrib non-free
deb-src http://mobigroup.ru/debian/ lenny main contrib non-free
...
Рейтинг: 0 / 0
Упростить сложный агрегат
    #36321405
Серж
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MBGКак пример приведу таблицу и запрос из биллинговой системыsqlite используется только для первичного накопления и обработки и потом данные из нее куда-то сливаются или все так в ней и лежит?
...
Рейтинг: 0 / 0
Упростить сложный агрегат
    #36321634
MBG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
MBG
Гость
СержMBGКак пример приведу таблицу и запрос из биллинговой системыsqlite используется только для первичного накопления и обработки и потом данные из нее куда-то сливаются или все так в ней и лежит?

SQLite используется и как основная БД и для первичной обработки. Демоны сбора статистики с АТС хранят данные в in-memory БД, периодически сливая в базу на диск, притом сразу же ротируя данные по месяцам - таким образом, старые базы легко можно сбросить в архив, а размер каждой базы порядка гигабайта независимо от того, сколько лет работает система. Для управления разделенной базой написана свои функции. Схема баз разрешает их слияние (дубликаты игнорируются) - полезно, когда сбор статистики ведется на нескольких хостах для надежности. Основная база содержит данные о пользователях, атс, тарифах и проч. - она относительно небольшая. Таким образом, избавляемся от блокировок в многопользовательском доступе - основная база изменяется через веб-интерфейс независимо от биллингования баз с трафиком за месяц.
Сами тарифы реализованы в виде скриптов, имеющих доступ к БД - позволяют задавать совершенно произвольные правила обработки трафика (используются через веб-интерфейс и напрямую при биллинговании), функции интроспекции позволяют удобно управлять тарифами через веб-интерфейс, см.
Телефонный биллинг: тариф "Направление посекундно"
...
Рейтинг: 0 / 0
Упростить сложный агрегат
    #36324391
Серж
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MBG, очень интересно. Почитаю. Приходилось немного сталкиваться с биллингом.
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / SQLite [игнор отключен] [закрыт для гостей] / Упростить сложный агрегат
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]