Оптимизация предложения SQL «Где» для запросов с подзапросами - PullRequest
4 голосов
/ 28 октября 2011

Допустим, у меня есть следующая гипотетическая структура данных:

create table "country"
(
  country_id integer,  
  country_name varchar(50),
  continent varchar(50),
  constraint country_pkey primary key (country_id)
);

create table "person"
(
  person_id integer,
  person_name varchar(100),
  country_id integer,
  constraint person_pkey primary key (person_id)
);

create table "event"
(
  event_id integer,
  event_desc varchar(100),
  country_id integer,
  constraint event_pkey primary key (event_id)
);

Я хочу запросить количество строк людей и событий в каждой стране.Я решил использовать подзапрос.

select c.country_name, sum(sub1.person_count) as person_count, sum(sub2.event_count) as event_count
from
  "country" c
  left join (select country_id, count(*) as person_count from "person" group by country_id) sub1
    on (c.country_id=sub1.country_id)
  left join (select country_id, count(*) as event_count from "event" group by country_id) sub2
    on (c.country_id=sub2.country_id)
group by c.country_name

Я знаю, что вы можете сделать это, используя операторы select в списке полей, но преимущество использования подзапросов состоит в том, что я более гибок в изменении SQL, чтобы сделать его обобщенным и использовать другое поле.Допустим, если я изменю запрос для отображения его по континентам, это будет так же просто, как заменить поле «c.country_name» на «c.continent».

Моя проблема связана с фильтрацией.Если мы добавим предложение where следующим образом:

select c.country_name, 
  sum(sub1.person_count) as person_count, 
  sum(sub2.event_count) as event_count
from
  "country" c
  left join (select country_id, count(*) as person_count from "person" group by country_id) sub1
    on (c.country_id=sub1.country_id)
  left join (select country_id, count(*) as event_count from "event" group by country_id) sub2
    on (c.country_id=sub2.country_id)
where c.country_name='UNITED STATES'
group by c.country_name

Кажется, что подзапросы все еще выполняют подсчет для всех стран.Предположим, что таблицы person и event огромны, и у меня уже есть индексы country_id для всех таблиц.Это действительно медленно.Разве база данных не должна выполнять только подзапросы для страны, которая была отфильтрована?Нужно ли заново создавать фильтр страны для каждого подзапроса (это очень утомительно и код не легко изменить)?Кстати, я использую PostgreSQL 8.3 и 9.0, но я думаю, что то же самое происходит и в других базах данных.

Ответы [ 2 ]

2 голосов
/ 28 октября 2011

Разве база данных не должна выполнять только подзапросы для страны что было отфильтровано?

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

Представьте, как бы вы сделали это, если бы sub1 и sub2 были базовыми таблицами вместо подвыборов. У них обоих будет два столбца, и у каждого будет один ряд для каждого country_id. И если вы хотите присоединиться ко всем строкам, вы бы написали это так.

from
  "country" c
  left join sub1 on (c.country_id=sub1.country_id)
  left join sub2 on (c.country_id=sub2.country_id)

Но если вы хотите присоединиться к одной строке, вы бы написали что-то эквивалентное этому.

from
  "country" c
  left join (select * from sub1 where country_id = ?)
    on (c.country_id=sub1.country_id)
  left join (select * from sub2 where country_id = ?)
    on (c.country_id=sub2.country_id)

Джо Селко, который помогал разрабатывать ранние стандарты SQL, часто писал о , как порядок оценки SQL выглядит в Usenet.

0 голосов
/ 28 октября 2011
  • Можете ли вы фильтровать / группировать строки, используя country_id, а не country_name?Я полагаю, у вас нет индекса по имени.
  • Подзапросы не используют никакого индекса, это нормально, потому что вы просматриваете всю таблицу.Если вы хотите уменьшить количество сканирований, вам следует отфильтровать данные.
...