加入另一个 table 仅匹配特定记录
Join to another table only matching specific records
我有 table 个端口:
drop table if exists ports;
create table ports(id int, name char(20));
insert into ports (id, name ) values
(1, 'Port hedland'),
(2, 'Kwinana');
以及与这些港口相关的 table 关税:
drop table if exists tariffs;
create table tariffs(id int, portId int, price decimal(12,2), expiry bigint(11));
insert into tariffs (id, portId, price, expiry ) values
(1, 2, 11.00, 1648408400),
(2, 2, 12.00, 1648508400),
(3, 2, 13.00, 1648594800),
(4, 2, 14.00, 1651273200),
(5, 2, 15.00, 2250000000 );
insert into tariffs (id, portId, price, expiry ) values
(1, 1, 21.00, 1648408400),
(2, 1, 22.00, 1648508400),
(3, 1, 23.00, 1648594800),
(4, 1, 24.00, 1651273200),
(5, 1, 25.00, 2250000000 );
每个关税都有有效期。
我可以很容易地进行查询,以找出每个港口特定日期的正确关税。例如在时间戳 1648594700
正确的关税是:
SELECT * FROM tariffs
WHERE 1648594700 < expiry AND portId = 2
ORDER BY expiry
LIMIT 1
结果:
id portId price expiry
3 2 13.00 1648594800
但是,在我的应用程序中,我希望能够从 ports
记录开始提取正确的关税。
对于一条条记录,我可以这样做:
SELECT * FROM ports
LEFT JOIN tariffs on tariffs.portId = ports.id
WHERE 1648594700 < tariffs.expiry AND ports.id = 2
LIMIT 1
结果:
id name id portId price expiry
2 Kwinana 3 2 13.00 1648594800
这感觉有点'dirty',尤其是因为我正在对一条记录进行查找,然后使用 LIMIT 强制只有一个结果。但是,好吧。
我不能做,也不知道怎么做的是一个查询,它将 return 一个 list 端口,并且 each 端口具有匹配上述约束的 price
字段(即,与每个端口的 1648594700
相比,具有最高 expiry
的记录)。
这显然行不通:
SELECT * FROM ports
left join tariffs on tariffs.portId = ports.id
where 1648594700 < tariffs.expiry
由于查询的结果,使用时间戳 1648594700
进行测试,将是:
id name id portId price expiry
2 Kwinana 3 2 13.00 1648594800
2 Kwinana 4 2 14.00 1651273200
2 Kwinana 5 2 15.00 2250000000
1 Port he 3 1 23.00 1648594800
1 Port he 4 1 24.00 1651273200
1 Port he 5 1 25.00 2250000000
相反,所有端口的结果(在进一步过滤之前)应该是:
id name id portId price expiry
2 Kwinana 3 2 13.00 1648594800
1 Port he 3 1 23.00 1648594800
是否有一种干净、非 hacky 的方式来获得这样的结果?
作为一个附加的约束,这是否可以在一个查询中完成,没有临时 tables 等?
在 MySQL 8+ 上,ROW_NUMBER
应该可以在这里工作:
WITH cte AS (
SELECT p.id, p.name, t.price, t.expiry,
ROW_NUMBER() OVER (PARTITION BY p.id ORDER BY t.expiry) rn
FROM ports p
LEFT JOIN tariffs t ON t.portId = p.id
WHERE t.expiry > 1648594700
)
SELECT id, name, price, expiry
FROM cte
WHERE rn = 1
ORDER BY id;
此逻辑会 return 每个端口都有最近到期的记录。
您可以 select 最低到期时间,进行连接并仅获取具有此最低到期时间的行:
SELECT p.id, p.name, t.id, t.portId, t.price, t.expiry
FROM ports p
LEFT JOIN tariffs t ON p.id = t.portId
WHERE expiry = (SELECT MIN(expiry) FROM tariffs WHERE 1648594700 < expiry)
ORDER BY p.id;
这将得到你想要的结果,请看这里:db<>fiddle
我有 table 个端口:
drop table if exists ports;
create table ports(id int, name char(20));
insert into ports (id, name ) values
(1, 'Port hedland'),
(2, 'Kwinana');
以及与这些港口相关的 table 关税:
drop table if exists tariffs;
create table tariffs(id int, portId int, price decimal(12,2), expiry bigint(11));
insert into tariffs (id, portId, price, expiry ) values
(1, 2, 11.00, 1648408400),
(2, 2, 12.00, 1648508400),
(3, 2, 13.00, 1648594800),
(4, 2, 14.00, 1651273200),
(5, 2, 15.00, 2250000000 );
insert into tariffs (id, portId, price, expiry ) values
(1, 1, 21.00, 1648408400),
(2, 1, 22.00, 1648508400),
(3, 1, 23.00, 1648594800),
(4, 1, 24.00, 1651273200),
(5, 1, 25.00, 2250000000 );
每个关税都有有效期。
我可以很容易地进行查询,以找出每个港口特定日期的正确关税。例如在时间戳 1648594700
正确的关税是:
SELECT * FROM tariffs
WHERE 1648594700 < expiry AND portId = 2
ORDER BY expiry
LIMIT 1
结果:
id portId price expiry
3 2 13.00 1648594800
但是,在我的应用程序中,我希望能够从 ports
记录开始提取正确的关税。
对于一条条记录,我可以这样做:
SELECT * FROM ports
LEFT JOIN tariffs on tariffs.portId = ports.id
WHERE 1648594700 < tariffs.expiry AND ports.id = 2
LIMIT 1
结果:
id name id portId price expiry
2 Kwinana 3 2 13.00 1648594800
这感觉有点'dirty',尤其是因为我正在对一条记录进行查找,然后使用 LIMIT 强制只有一个结果。但是,好吧。
我不能做,也不知道怎么做的是一个查询,它将 return 一个 list 端口,并且 each 端口具有匹配上述约束的 price
字段(即,与每个端口的 1648594700
相比,具有最高 expiry
的记录)。
这显然行不通:
SELECT * FROM ports
left join tariffs on tariffs.portId = ports.id
where 1648594700 < tariffs.expiry
由于查询的结果,使用时间戳 1648594700
进行测试,将是:
id name id portId price expiry
2 Kwinana 3 2 13.00 1648594800
2 Kwinana 4 2 14.00 1651273200
2 Kwinana 5 2 15.00 2250000000
1 Port he 3 1 23.00 1648594800
1 Port he 4 1 24.00 1651273200
1 Port he 5 1 25.00 2250000000
相反,所有端口的结果(在进一步过滤之前)应该是:
id name id portId price expiry
2 Kwinana 3 2 13.00 1648594800
1 Port he 3 1 23.00 1648594800
是否有一种干净、非 hacky 的方式来获得这样的结果? 作为一个附加的约束,这是否可以在一个查询中完成,没有临时 tables 等?
在 MySQL 8+ 上,ROW_NUMBER
应该可以在这里工作:
WITH cte AS (
SELECT p.id, p.name, t.price, t.expiry,
ROW_NUMBER() OVER (PARTITION BY p.id ORDER BY t.expiry) rn
FROM ports p
LEFT JOIN tariffs t ON t.portId = p.id
WHERE t.expiry > 1648594700
)
SELECT id, name, price, expiry
FROM cte
WHERE rn = 1
ORDER BY id;
此逻辑会 return 每个端口都有最近到期的记录。
您可以 select 最低到期时间,进行连接并仅获取具有此最低到期时间的行:
SELECT p.id, p.name, t.id, t.portId, t.price, t.expiry
FROM ports p
LEFT JOIN tariffs t ON p.id = t.portId
WHERE expiry = (SELECT MIN(expiry) FROM tariffs WHERE 1648594700 < expiry)
ORDER BY p.id;
这将得到你想要的结果,请看这里:db<>fiddle