我正在使用以下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)
答案 0 :(得分:0)
您可以使用计算列对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
答案 1 :(得分:0)
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)