База данных запросов для отдельных значений и агрегированных данных на основе условия - PullRequest
2 голосов
/ 07 октября 2019

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

CREATE TABLE users
(
  id SERIAL PRIMARY KEY,
  name VARCHAR(255)
);

CREATE TABLE photos
(
  id INT PRIMARY KEY,
  user_id INTEGER REFERENCES users(id),
  flag VARCHAR(255)
);

INSERT INTO users VALUES (1, 'Bob');
INSERT INTO users VALUES (2, 'Alice');
INSERT INTO users VALUES (3, 'John');

INSERT INTO photos VALUES (1001, 1, 'a');
INSERT INTO photos VALUES (1002, 1, 'b');
INSERT INTO photos VALUES (1003, 1, 'c');
INSERT INTO photos VALUES (1004, 2, 'a');
INSERT INTO photos VALUES (1004, 2, 'x');

Что мне нужно, это извлечь каждое имя пользователя, только один раз, и значение флага для каждого из них. Значение флага должно определять приоритет конкретного, скажем, b. Таким образом, результат должен выглядеть следующим образом:

Bob    b 
Alice  a

Где Bob имеет фотографию с флагом b, а Alice - нет, а John не имеет фотографий. Для Alice вывод значения флага не важен (a или x будет так же хорошо), пока у нее нет фотографии, помеченной b.

Ближайшая вещь, которую я нашелБыли некоторые запросы на самообъединение, где значение флага было бы агрегировано с использованием min() или max(), но я ищу конкретное значение, которое не является ни первым, ни последним. Более того, я обнаружил, что вы можете определять свои собственные агрегатные функции, но мне интересно, есть ли более простой способ обработки запроса для получения необходимых данных.

Спасибо!

1 Ответ

1 голос
/ 07 октября 2019

Вот метод с агрегацией:

select u.name,
       coalesce(max(flag) filter (where flag = 'b'),
                min(flag)
               ) as flag
from users u left join
     photos p
     on u.id = p.user_id
group by u.id, u.name;

Тем не менее, более типичным методом будет запрос расстановки приоритетов. Возможно:

select distinct on (u.id) u.name, p.flag
from users u left join
     photos p
     on u.id = p.user_id
order by u.id, (p.flag = 'b') desc;
...