powered by simpleCommunicator - 2.0.52     © 2025 Programmizd 02
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Left join медленный запрос
25 сообщений из 32, страница 1 из 2
Left join медленный запрос
    #39997913
Bekass
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Тестирую переход с Mysql на PostgreSQL
Столкнулся с проблемой
есть простая таблица
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
CREATE TABLE "public"."geo" (
  "id" int8 NOT NULL DEFAULT nextval('geo_id_seq'::regclass),
  "parent_id" int8,
  "name" varchar(1000) COLLATE "pg_catalog"."default"
)
;

-- ----------------------------
-- Indexes structure for table geo
-- ----------------------------
CREATE INDEX "pkParent" ON "public"."geo" USING btree (
  "parent_id" "pg_catalog"."int8_ops" ASC NULLS LAST
);

-- ----------------------------
-- Primary Key structure for table geo
-- ----------------------------
ALTER TABLE "public"."geo" ADD CONSTRAINT "geo_pkey" PRIMARY KEY ("id");

-- ----------------------------
-- Foreign Keys structure for table geo
-- ----------------------------
ALTER TABLE "public"."geo" ADD CONSTRAINT "geo_parent_id_fkey" FOREIGN KEY ("parent_id") REFERENCES "public"."geo" ("id") ON DELETE CASCADE ON UPDATE NO ACTION;



В ней около 12 миллионов записей , задача вывести строки у который нет потомков (древовидная структура )
Делаю запрос
Код: sql
1.
select geo.* from geo LEFT JOIN geo as p ON geo.id = p.parent_id where p.id ISNULL; 



который длиться около 20 секунд , тот же запрос на Mysql занимает 0.21 секунды

Подскажите пожалуйста, что не так с таблицей ?

Eplain: Gather (cost=320462.73..792817.22 rows=1 width=49)
Workers Planned: 2
-> Parallel Hash Left Join (cost=319462.73..791817.12 rows=1 width=49)
Hash Cond: (geo.id = p.parent_id)
Filter: (p.id IS NULL)
-> Parallel Seq Scan on geo (cost=0.00..212933.33 rows=6128432 width=49)
-> Parallel Hash (cost=212933.33..212933.33 rows=6128432 width=16)
-> Parallel Seq Scan on geo p (cost=0.00..212933.33 rows=6128432 width=16)
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39997934
Guzya
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вы уверены, что

Код: sql
1.
p.id IS NULL;



а не

Код: sql
1.
p.parent_id IS NULL;



у Вас же

Код: sql
1.
"id" int8 NOT NULL
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39997937
Bekass
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Уверен, left join проверка на потомков которых нет , т.е. where p.id ISNULL
Проблема я думаю, не в запросе а в таблице, либо в тонких настройках БД
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39997956
и сколько же у вас среди 12 млн записей с NULL ?
и зачем вы хотите их ВСЕ и сразу? LIMIT надо бы юзать.
Код: plsql
1.
 NOT NULL DEFAULT nextval('geo_id_seq'::regclass)


это не нужно вообще, у вас же PK там
и вопрос на миллион: а зачем вам вообще LEFT JOIN тут?
Bekass
проверка на потомков, которых нет

для этого WHERE хватит.
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39997995
Bekass
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Для того чтобы найти записи у которые НЕТ потомков, нужно выбрать по LEFT JOIN записи которых не существует, это как бы классический SQL запрос.
nextval('geo_id_seq'::regclass) - ЭТО авто инкремет ключевого поля
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39997998
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Bekass,

я не вижу каким образом он 0.21s может занимать на mysql
и никакими настройками базы вы такой скорости не получите.
тут как ни делай а надо все строки проверять и никаким индексом это не вылечить.

ps: explain analyze покажите - может получится ускорить раз в несколько но точно не до 200ms
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998002
Bekass
Для того чтобы найти записи у которые НЕТ потомков, нужно выбрать по LEFT JOIN записи которых не существует, это как бы классический SQL запрос.
nextval('geo_id_seq'::regclass) - ЭТО авто инкремет ключевого поля

а почему вы тут
Код: sql
1.
2.
3.
4.
SELECT geo.*
FROM geo
LEFT JOIN geo p ON geo.id = p.parent_id
WHERE p.id IS NULL;


не можете просто написать:
Код: sql
1.
2.
WHERE geo.id IS NULL
    AND geo.parent_id IS NULL


?
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998003
Bekass
nextval('geo_id_seq'::regclass) - ЭТО авто инкремет ключевого поля

так сделайте тип bigserial вместо int8
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998021
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Алексей Роза
Bekass
nextval('geo_id_seq'::regclass) - ЭТО авто инкремет ключевого поля

так сделайте тип bigserial вместо int8


Такого типа нет, почитайте документацию и посмотрите во что превращается псевдотип serial/bigserial после создания и как он в plain text dump выглядит.

--
Maxim Boguk
лучшая поддержка PostgreSQL: dataegret.ru
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998022
где нет, в SQL-стандарте? Там дохрена чего нет, это нормально, он стааарый
пофигу что там макрос разворачивает в команду "создать последовательность", половина PG на таких макросах
это путь ООП и он правильный.
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998023
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Алексей Роза
Bekass
Для того чтобы найти записи у которые НЕТ потомков, нужно выбрать по LEFT JOIN записи которых не существует, это как бы классический SQL запрос.
nextval('geo_id_seq'::regclass) - ЭТО авто инкремет ключевого поля

а почему вы тут
Код: sql
1.
2.
3.
4.
SELECT geo.*
FROM geo
LEFT JOIN geo p ON geo.id = p.parent_id
WHERE p.id IS NULL;


не можете просто написать:
Код: sql
1.
2.
WHERE geo.id IS NULL
    AND geo.parent_id IS NULL


?


потому что это будет решение другой задачи... включите голову наконец и кончайте ересь советовать...

чтобы выбрать все записи у которых нет потомков надо выбрать все записи вообще и далее убрать те из них которые упомянуты в parent_id в той же таблице (может в той же строке а может и в других каких то).
Так что авторское решение вполне логичное (другой вопрос что ВОЗМОЖНО можно сделать быстрее а для этого надо понимать сколько уникальных parent_id в таблице).
select count(distinct parent_id) from "public"."geo";

--
Maxim Boguk
лучшая поддержка PostgreSQL: dataegret.ru
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998025
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Алексей Роза
где нет, в SQL-стандарте? Там дохрена чего нет, это нормально, он стааарый
пофигу что там макрос разворачивает в команду "создать последовательность", половина PG на таких макросах
это путь ООП и он правильный.


при этом это макрос для ленивых людей... по факту в pg_dump и в отображении структуры таблицы внутри базы bigserial отображается именно как DEFAULT nextval('geo_id_seq'::regclass) (и чем меньше его использовать этот макрос тем лучше потому что он важные детали реализации скрывает, например тот факт что serial тип вполне может за пределы int поля выйти и об этом надо помнить).
Вы хоть раз то в psql \d+ имя таблицы то делали с serial полем?

--
Maxim Boguk
лучшая поддержка PostgreSQL: dataegret.ru
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998026
А я и не советую, вы почитайте внимательно, там вопрос.
Это очень странная задача - лефтджойнить 12 лямов строк и все разом выводить...
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998027
Guzya
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Может я опять что-то не доглядел, но объясните, как поле объявленное "id" int8 NOT NULL (т.е. не допускает вставки NULL) да еще и являющееся первичным ключем, может дать TRUE в условии id IS NULL?
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998028
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Алексей Роза
А я и не советую, вы почитайте внимательно, там вопрос.
Это очень странная задача - лефтджойнить 12 лямов строк и все разом выводить...


потому что найти 'вывести строки у который нет потомков' по другому не решается... а вот ваши
WHERE geo.id IS NULL
AND geo.parent_id IS NULL
решают совсем другую задачу (причем непонятно какую)

Задачу можно обсуждать конечно, но сначала надо рабочее решение предложить а не явно неправильные варианты.

--
Maxim Boguk
лучшая поддержка PostgreSQL: dataegret.ru
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998029
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Guzya
Может я опять что-то не доглядел, но объясните, как поле объявленное "id" int8 NOT NULL (т.е. не допускает вставки NULL) да еще и являющееся первичным ключем, может дать TRUE в условии id IS NULL?


Так left join же, это значит что нет записей с таким parent_id у строки... что и соответствует решению...
это стандартный с момента появления sql альтернативный метод записи запроса
Код: plsql
1.
select geo.* from geo where not exists (select from geo as p where geo.id = p.parent_id);



или вы про неверную идею
Код: plsql
1.
2.
WHERE geo.id IS NULL
    AND geo.parent_id IS NULL


- так это к ее автору )).

--
Maxim Boguk
лучшая поддержка PostgreSQL: dataegret.ru
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998031
Maxim Boguk
например тот факт что serial тип вполне может за пределы int поля выйти и об этом надо помнить

поэтому все советуют делать сразу bigserial и не париться. Но когда есть 200% уверенность, что не вылезет, я таки ставлю serial.
Maxim Boguk
и чем меньше его использовать этот макрос тем лучше

ну оставим это на полке "на вкус и цвет все фломастеры разные"
мне в листинге нравится лаконичная запись "bigserial", там нужно быстренько глазами пробежать и всё понять
если всё подряд разворачивать - это не дело.
А то что \d+ показывает всё целиком никак не поможет эту последовательность ручками создать/удалить, всё равно в документацию лезть.
Maxim Boguk
Вы хоть раз то в psql \d+ имя таблицы то делали с serial полем?

да написал пару инфосистем на пг.
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998032
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Алексей Роза,

- Почему у вас так много свободного времени, вы такой добродушный и веселый? Как вам это удаётся?
- Я никогда ни с кем не спорю, особенно в интернете (и особенно если мне за это не платят почасовую ставку).
- Но это-же невозможно!
- Да, вы совершенно правы.

--
Maxim Boguk
лучшая поддержка PostgreSQL: dataegret.ru
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998107
Bekass
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
[quot Maxim Boguk#22196035]
Guzya

select geo.* from geo where not exists (select from geo as p where geo.id = p.parent_id);

--
Maxim Boguk
лучшая поддержка PostgreSQL: dataegret.ru


Результат не быстрее Left join

В общем судя по экспертам с этого форума, с Mysql на PostgreSQL переходит , увы, не стоит
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998124
Guzya
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Покажите план запроса с mysql.
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998134
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
[quot Bekass#22196196]
Maxim Boguk
пропущено...


Результат не быстрее Left join

В общем судя по экспертам с этого форума, с Mysql на PostgreSQL переходит , увы, не стоит


1)чтобы посоветовать что то внятное я запросил результаты explain analyze которые вы не прислали
2)я запросил результаты select count(distinct parent_id) from "public"."geo"; которые вы тоже не прислали
3)запросили план на mysql который вы тоже не прислали (и 0.21s для обработки 12M таблицы выглядит подозрительно мало как его не делай)
4)я бы еще посмотрел на explain (analyze, costs, buffers, timing) запроса с exists при включенном track_io_timing

--
Maxim Boguk
лучшая поддержка PostgreSQL: dataegret.ru
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998191
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Maxim Boguk

чтобы выбрать все записи у которых нет потомков надо выбрать все записи вообще и далее убрать те из них которые упомянуты в parent_id в той же таблице (может в той же строке а может и в других каких то).

вообще-то надо выбрать все значения индекса ид для которых нет соответственного в индексе парент_ид. и для разницы зачитать остатние строки. для каких-нибудь структур типа сортированных списков алгоритмом типа мерджа( только анти), вполне может решаться "невероятно бистро", особо если "списки" уже в памяти, а проколов (исключений) для поднятия с диска записей "очень мало" относительно массы в 12 лямов,. которые душка пж так у порно вычитывать собирается по плану . а надо всего-то сравнить мощности множеств инд.....
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998255
Bekass
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Maxim Boguk,

1) explain analyze select geo.* from geo LEFT JOIN geo as p ON geo.id = p.parent_id where p.id ISNULL LIMIT 100;

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
Limit  (cost=320462.73..792817.22 rows=1 width=49) (actual time=10291.636..12699.384 rows=100 loops=1)
  ->  Gather  (cost=320462.73..792817.22 rows=1 width=49) (actual time=10291.632..12699.371 rows=100 loops=1)
        Workers Planned: 2
        Workers Launched: 2
        ->  Parallel Hash Left Join  (cost=319462.73..791817.12 rows=1 width=49) (actual time=10251.338..10251.521 rows=34 loops=3)
              Hash Cond: (geo.id = p.parent_id)
              Filter: (p.id IS NULL)
              Rows Removed by Filter: 53
              ->  Parallel Seq Scan on geo  (cost=0.00..212933.33 rows=6128432 width=49) (actual time=0.019..773.833 rows=4902746 loops=3)
              ->  Parallel Hash  (cost=212933.33..212933.33 rows=6128432 width=16) (actual time=6822.487..6822.487 rows=4902746 loops=3)
                    Buckets: 131072  Batches: 256  Memory Usage: 3808kB
                    ->  Parallel Seq Scan on geo p  (cost=0.00..212933.33 rows=6128432 width=16) (actual time=0.021..5358.471 rows=4902746 loops=3)
Planning Time: 0.636 ms
Execution Time: 12699.580 ms



2) select count(distinct parent_id) from "public"."geo";
COUNT 2939789 https://prnt.sc/ugwwap

3) MYSQL
Explain https://prnt.sc/ugwtwy
Запрос https://prnt.sc/ugwuij
COUNT: https://prnt.sc/ugwyrh

4) "я бы еще посмотрел на explain (analyze, costs, buffers, timing) запроса с exists при включенном track_io_timing" тут не совсем понял как делать
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998425
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Bekass
Maxim Boguk,


3) MYSQL
Explain https://prnt.sc/ugwtwy
Запрос https://prnt.sc/ugwuij
COUNT: https://prnt.sc/ugwyrh

4) "я бы еще посмотрел на explain (analyze, costs, buffers, timing) запроса с exists при включенном track_io_timing" тут не совсем понял как делать


По mysql - а попробуйте выполнить и замерять время выполнения (это важно) для
select count(*) from geo LEFT JOIN geo as p ON geo.id = p.parent_id where p.id ISNULL;
потому что есть у меня подозрение почему вы такую скорость получаете на mysql

по 4)
set track_io_timing to on;
explain (analyze, costs, buffers, timing) select geo.* from geo where not exists (select from geo as p where geo.id = p.parent_id);
...
Рейтинг: 0 / 0
Left join медленный запрос
    #39998565
Bekass
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Спасибо за помощь, Maxim

[quot Maxim Boguk#22196748]
Bekass
Maxim Boguk,


3)
По mysql - а попробуйте выполнить и замерять время выполнения (это важно) для
select count(*) from geo LEFT JOIN geo as p ON geo.id = p.parent_id where p.id ISNULL;
потому что есть у меня подозрение почему вы такую скорость получаете на mysql


Дело в том что mysql как порядочная БД сортирует индексы, в выборке первые 1000 значений, понятное дело если в запрос ORDER BY добавить , то время увеличится,
например
Код: sql
1.
SELECT   geo.* from geo LEFT JOIN geo as p ON geo.id = p.parent_id where p.id IS NULL ORDER BY geo.name;


Выдаст 37, что не плохо https://prnt.sc/uh8s3v
тот же запрос на POSTGRE https://prnt.sc/uh8w76 на меньшем количестве записей занимает 200с !!!


4) запрос выполнен :
Код: sql
1.
2.
set track_io_timing to on;
explain (analyze, costs, buffers, timing) select geo.* from geo where not exists (select from geo as p where geo.id = p.parent_id); 


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
Merge Anti Join  (cost=8.86..1288015.58 rows=13994306 width=49) (actual time=0.075..40279.078 rows=11768450 loops=1)
  Merge Cond: (geo.id = p.parent_id)
  Buffers: shared hit=718468 read=219043
  I/O Timings: read=29869.283
  ->  Index Scan using geo_pkey on geo  (cost=0.43..533612.01 rows=14708238 width=49) (actual time=0.008..2733.825 rows=14708238 loops=1)
        Buffers: shared hit=151637 read=40188
        I/O Timings: read=576.221
  ->  Index Only Scan using "pkParent" on geo p  (cost=0.43..533780.01 rows=14708238 width=8) (actual time=0.058..33983.615 rows=14708231 loops=1)
        Heap Fetches: 14708231
        Buffers: shared hit=566831 read=178855
        I/O Timings: read=29293.062
Planning Time: 0.936 ms
Execution Time: 40668.958 ms
...
Рейтинг: 0 / 0
25 сообщений из 32, страница 1 из 2
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Left join медленный запрос
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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