SQL-запрос для выбора шкафчиков с местом для пакета - PullRequest
0 голосов
/ 17 октября 2018

Я создаю интеллектуальную систему резервирования шкафчиков со следующими таблицами Postgres:

CREATE TABLE lockers (
  id serial primary key
)

CREATE TABLE doors (
  id serial primary key,
  locker_id integer NOT NULL,
  size integer
);

CREATE TABLE packages (
  id serial primary key,
  locker_id integer NOT NULL,
  size integer
);

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

Все работает отлично, но теперь я хочу написать запрос, который может выбрать все шкафчики, в которых есть место для нового пакета с заданным размером.Моя проблема в том, что я не могу просто сделать JOIN, чтобы выбрать пустые двери, потому что пакеты знают только о шкафчике.Для каждого шкафчика мне нужно сделать что-то вроде Find the smallest possible door for each package, and see if the new package fits in any of the remaining doors.Я легко могу написать это на JavaScript так:

const canFitPackage = (reservations, doors, newPackage) => {
  const sortedReservations = reservations
    .slice()
    .sort((a, b) => a.size - b.size)
    .reverse();
  const sortedDoors = doors.slice().sort((a, b) => a.size - b.size);

  for (let i = 0; i < sortedReservations.length; i++) {
    const res = sortedReservations[i];
    for (let j = 0; j < sortedDoors.length; j++) {
      const door = sortedDoors[j];
      if (door.size >= res.size) {
        sortedDoors.splice(j, 1);
        break;
      }
    }
  }
  return sortedDoors.some(door => door.size >= newPackage.size);
};

Я потратил несколько дней, пытаясь понять, как это сделать в SQL, но пока мне не повезло.Мне интересно, действительно ли это можно сделать в вызове SQL или мне нужно написать функцию Postgres.Любая помощь приветствуется.

Обновление

После долгих попыток попробовать разные вещи, я пришел к этому SQL-запросу, который делает это для меня.Я уверен, что это медленно и неэффективно, поэтому любые отзывы и советы приветствуются.

WITH 
locker_doors AS (
  SELECT * FROM doors 
  --ADD INNER JOIN ON LOCKER 
  --ADD WHERE CLAUSE ON LOCKERS
),
all_combinations AS (
  SELECT locker_doors.id AS door_id, locker_doors.size AS door_size, locker_doors.locker_id, packages.id AS package_id, packages.size AS package_size
  FROM locker_doors 
  JOIN packages ON locker_doors.locker_id = packages.locker_id AND locker_doors.size >= packages.size
  ORDER BY packages.size DESC, locker_doors.size ASC
), 
distinct_doors AS (
  SELECT DISTINCT ON (door_id) * FROM all_combinations
), 
package_placements AS (
  SELECT DISTINCT ON (package_id) * FROM distinct_doors
)
SELECT DISTINCT ON (locker_id) locker_id 
FROM locker_doors 
WHERE id NOT IN (SELECT door_id FROM package_placements)

Ответы [ 2 ]

0 голосов
/ 26 октября 2018
locker_doors AS (
  SELECT * FROM doors 
  --ADD INNER JOIN ON LOCKER 
  --ADD WHERE CLAUSE ON LOCKERS
),
all_combinations AS (
  SELECT locker_doors.id AS door_id, locker_doors.size AS door_size, locker_doors.locker_id, packages.id AS package_id, packages.size AS package_size
  FROM locker_doors 
  JOIN packages ON locker_doors.locker_id = packages.locker_id AND locker_doors.size >= packages.size
  ORDER BY packages.size DESC, locker_doors.size ASC
), 
distinct_doors AS (
  SELECT DISTINCT ON (door_id) * FROM all_combinations
), 
package_placements AS (
  SELECT DISTINCT ON (package_id) * FROM distinct_doors
)
SELECT DISTINCT ON (locker_id) locker_id 
FROM locker_doors 
WHERE id NOT IN (SELECT door_id FROM package_placements)
0 голосов
/ 23 октября 2018

Вы можете использовать вычисляемый столбец для сортировки оставшегося пространства doors записей и получения наименьшего.

http://sqlfiddle.com/#!17/a8840/2

 WITH newPackage (packsize) as (
   values (1)--I used with clause to declare the new package. You can change the size of the new package from here to test different cases
)
select  d.*, 
(d.size - coalesce((select p.size from packages p where  p.locker_id = d.locker_id), 0)) remaining_size 
from 
doors d, 
newPackage
where 
d.size - coalesce((select p.size from packages p where  p.locker_id = d.locker_id), 0) >= newPackage.packsize
order by 
(d.size - coalesce((select p.size from packages p where  p.locker_id = d.locker_id), 0)) 
limit 1
...