Таблица построения SQL с использованием результатов count (*) из другой таблицы - PullRequest
0 голосов
/ 07 ноября 2018

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

create table pokedex(
    name varchar(20) not null,
    weigth int not null,
    height int not null,
    primary key(name)
);
create table trainer(
    name varchar(20) not null,
    location varchar(20) not null,
    gender varchar(10) not null,
    birth_year int not null,
    primary key(name)
);

create table trainer_pokemon(
    trainer_name varchar(20) not null,
    pokemon_name varchar(20) not null,
    level int not null,
    year_obtained int not null,
    primary key(trainer_name, pokemon_name, level, year_obtained),
    foreign key(trainer_name) references trainer(name),
    foreign key(pokemon_name) references pokedex(name)
);
create table type(
    name varchar(20) not null,
    primary key(name)
);
create table poke_type(
    pokemon_name varchar(20) not null,
    type_name varchar(20) not null,
    primary key(pokemon_name, type_name),
    foreign key(pokemon_name) references pokedex(name),
    foreign key(type_name) references type(name)
);

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

with psychics as (
    select trainer_name, count(type_name) psychic from trainer_pokemon as tp
    inner join poke_type as pt on pt.pokemon_name = tp.pokemon_name
    group by tp.trainer_name, pt.type_name
    having pt.type_name = 'Psychic'
),
waters as (
    select trainer_name, count(type_name) water from trainer_pokemon as tp
    inner join poke_type as pt on pt.pokemon_name = tp.pokemon_name
    group by tp.trainer_name, pt.type_name
    having pt.type_name = 'Water'
),
select tp.trainer_name, w.water, p.psychic from trainer_pokemon as tp
inner join waters as w on w.trainer_name = tp.trainer_name
inner join psychics as p on p.trainer_name = tp.trainer_name
group by tp.trainer_name, w.water, p.psychic

Однако это не приводит к тому, что у тренеров нет покемонов определенного типа (в данном примере это вода / экстрасенс).

Может ли кто-нибудь указать мне правильное направление для составления таблицы дрессировщиков, сколько раз определенный тип находится в их коллекции покемонов?

1 Ответ

0 голосов
/ 07 ноября 2018

при условии, что вы используете относительно обновленную версию Postgresql (вы пометили и Mysql, и Postgres, которые являются довольно разными базами данных)

SELECT tp.trainer_name,
  COUNT(*) FILTER (WHERE pt.type_name = 'Psychic') as psychic,
  COUNT(*) FILTER (WHERE pt.type_name = 'Water')   as water
FROM trainer_pokemon tp, poke_type pt
WHERE tp.pokemon_name = pt.pokemon_name
GROUP BY 1

запрос, размещенный в вашем вопросе, вероятно, будет выполнен как многократное сканирование таблицы, но если вы действительно хотите это сделать, вы хотите использовать "LEFT OUTER JOIN" (ключевое слово OUTER необязательно) do:

with psychics as (
    select trainer_name, count(type_name) AS psychic
    from trainer_pokemon as tp
      inner join poke_type as pt on pt.pokemon_name = tp.pokemon_name
    group by tp.trainer_name, pt.type_name
    having pt.type_name = 'Psychic'
), waters as (
    select trainer_name, count(type_name) AS water
    from trainer_pokemon as tp
      inner join poke_type as pt on pt.pokemon_name = tp.pokemon_name
    group by tp.trainer_name, pt.type_name
    having pt.type_name = 'Water'
)
select tp.trainer_name, w.water, p.psychic
from trainer_pokemon as tp
  left join waters as w on w.trainer_name = tp.trainer_name
  left join psychics as p on p.trainer_name = tp.trainer_name

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

with trained as (
    select trainer_name, pt.type_name, count(*) as num
    from trainer_pokemon as tp
      inner join poke_type as pt on pt.pokemon_name = tp.pokemon_name
    group by tp.trainer_name, pt.type_name
)
select tp.trainer_name, w.num as water, p.num as psychic
from trainer_pokemon as tp
  left join trained as w on tp.trainer_name = w.trainer_name and w.type_name = 'Water'
  left join trained as p on tp.trainer_name = p.trainer_name and p.type_name = 'Psychic'
...