MySQL-按ID选择第二低的值

MySQL-按ID选择第二低的值,mysql,groupwise-maximum,Mysql,Groupwise Maximum,我有一个相当大的数据集,名为offers,包含大约700万行 该表有30列,但我只使用了其中的两列,cap_id(车辆的唯一标识符)和price(租赁车辆的每月成本) 我想写一个查询,返回每个cap_id的最佳最低价格和次优价格,以及最佳价格与次优价格相比节省的百分比 我使用的是5.7.12版 这是我的建议 创建表查询: CREATE TABLE `offers` ( `id` mediumint(8) unsigned NOT NULL auto_increment, `cap_id`

我有一个相当大的数据集,名为offers,包含大约700万行

该表有30列,但我只使用了其中的两列,cap_id(车辆的唯一标识符)和price(租赁车辆的每月成本)

我想写一个查询,返回每个cap_id的最佳最低价格和次优价格,以及最佳价格与次优价格相比节省的百分比

我使用的是5.7.12版

这是我的建议

创建表查询:

CREATE TABLE `offers` (
  `id` mediumint(8) unsigned NOT NULL auto_increment,
  `cap_id` varchar(255) default NULL,
  `price` mediumint default NULL,
  PRIMARY KEY (`id`)
) AUTO_INCREMENT=1;

INSERT INTO `offers` (`cap_id`,`price`) VALUES 
(18452,1007),(18452,884),(18452,276),(90019,328),(73353,539),(64854,249),(26684,257),(37452,966),(90019,980),(73353,1241),
(73353,1056),(37452,1043),(26684,829),(37452,260),(64854,358),(26684,288),(26684,678),(26684,905),(37452,1140),(94826,901),
(90019,745),(37452,1156),(37452,191),(64854,324),(73353,1110),(87725,624),(87725,973),(90019,1203),(90019,709),(18452,1133),
(18452,1019),(37452,639),(37452,1021),(87725,485),(94826,964),(37452,1066),(94826,823),(73353,1056),(18452,621),(37452,272),
(90019,223),(26684,412),(87725,310),(37452,948),(37452,826),(18452,1078),(90019,737),(18452,1166),(73353,150),(73353,1115),
(94826,957),(87725,242),(94826,715),(73353,1190),(94826,320),(94826,869),(64854,574),(94826,505),(26684,322),(90019,949),
(64854,1188),(37452,368),(90019,796),(87725,514),(37452,146),(94826,1216),(18452,625),(64854,1165),(18452,712),(37452,947),
(64854,616),(73353,1065),(26684,1167),(18452,935),(87725,1192),(26684,519),(64854,939),(90019,367),(26684,145),(64854,1076),
(26684,1016),(90019,606),(37452,1066),(73353,609),(94826,343),(94826,236),(94826,1059),(26684,681),(37452,779),(94826,259),
(87725,1080),(37452,914),(90019,826),(37452,597),(26684,879),(87725,471),(94826,680),(18452,906),(87725,860),(94826,1009);
这就是我迄今为止所尝试的:

SELECT 
 o1.cap_id,
 o2.price AS best_price,
 o1.price AS next_best,
 (o1.price / o2.price) * 100 AS '%_diff'
FROM
 offers o1
     JOIN
 offers o2 ON o1.cap_id = o2.cap_id
     AND o1.price > o2.price
GROUP BY o1.cap_id
HAVING COUNT(o1.price) = 2
它返回0行,当我在数据库中运行它时,运行速度非常慢

这是EXPLAIN的输出:

身份证件 选择类型 桌子 分割 类型 可能的\u键 钥匙 基伦 裁判 排 过滤 额外的 字段13 字段14 1. 易于理解的 x 指数 卡普 idx_配置文件_分组 国际直拨电话每月付款 国际直拨电话每月付款 9 7220930 100 使用指数;使用临时设备;使用文件排序 1. 易于理解的 Y 裁判 卡普 idx_配置文件_分组 国际直拨电话每月付款 国际直拨电话每月付款 4. moneyshake.x.cap_id 871 33.33 在何处使用;使用索引
这个过程可以在较新版本的MySQL中进行优化,但正如5.6中的小提琴一样,我的答案也是如此:

SELECT x.*
     , COUNT(*) running 
  FROM offers x 
  JOIN offers y 
    ON y.cap_id = x.cap_id 
   AND y.price < x.price 
 GROUP 
    BY x.id
 ORDER
    BY x.cap_id 
     , x.price;
扩展这一理念:

SELECT a.*
     , b.price
     , 1-(a.price/b.price) saving
  FROM 
     ( SELECT cap_id
            , MIN(price) price
         FROM offers 
        GROUP  
           BY cap_id
     ) a -- lowest price per cap_id
  JOIN 
     ( SELECT x.cap_id
            , x.price
         FROM offers x
         JOIN offers y
           ON y.cap_id = x.cap_id 
          AND y.price < x.price 
        GROUP 
           BY x.id
       HAVING COUNT(*)= 2
    ) b -- 2nd lowest price per cap_id (other methods are available)
   ON b.cap_id = a.cap_id
ORDER 
   BY a.cap_id;

这个过程可以在较新版本的MySQL中进行优化,但正如5.6中的小提琴一样,我的答案也是如此:

SELECT x.*
     , COUNT(*) running 
  FROM offers x 
  JOIN offers y 
    ON y.cap_id = x.cap_id 
   AND y.price < x.price 
 GROUP 
    BY x.id
 ORDER
    BY x.cap_id 
     , x.price;
扩展这一理念:

SELECT a.*
     , b.price
     , 1-(a.price/b.price) saving
  FROM 
     ( SELECT cap_id
            , MIN(price) price
         FROM offers 
        GROUP  
           BY cap_id
     ) a -- lowest price per cap_id
  JOIN 
     ( SELECT x.cap_id
            , x.price
         FROM offers x
         JOIN offers y
           ON y.cap_id = x.cap_id 
          AND y.price < x.price 
        GROUP 
           BY x.id
       HAVING COUNT(*)= 2
    ) b -- 2nd lowest price per cap_id (other methods are available)
   ON b.cap_id = a.cap_id
ORDER 
   BY a.cap_id;

在MySQL 8.x中,您可以执行以下操作:

with
p as (
  select
    id, cap_id, price,
    row_number() over(partition by cap_id order by price) as rn
  from offers
)
select 
  a.id as lowest_id, a.cap_id as lowest_cap_id, a.price as lowest_price,
  b.id as second_id, b.cap_id as second_cap_id, b.price as second_price,
  case when b.price is not null then 
    (b.price - a.price) / b.price
  end as percentage_saving
from p a
left join p b on a.cap_id = b.cap_id and b.rn = 2
where a.rn = 1

在MySQL 8.x中,您可以执行以下操作:

with
p as (
  select
    id, cap_id, price,
    row_number() over(partition by cap_id order by price) as rn
  from offers
)
select 
  a.id as lowest_id, a.cap_id as lowest_cap_id, a.price as lowest_price,
  b.id as second_id, b.cap_id as second_cap_id, b.price as second_price,
  case when b.price is not null then 
    (b.price - a.price) / b.price
  end as percentage_saving
from p a
left join p b on a.cap_id = b.cap_id and b.rn = 2
where a.rn = 1


您可以链接到5.6版。这就是你正在使用的版本吗?MySQL 8很简单。在旧版本中,这会有点尴尬。所以,请回答草莓的问题。您使用的是哪个版本?@草莓刚刚编辑-使用5.7.12,谢谢如果cap_id的最佳价格出现两次,那么最佳价格是否等于次优价格?还是你在寻找下一个更低的价格呢?5.7是一个相当旧的版本。你不能升级到MySQL 8吗?它提供了MySQL一直缺少的许多功能,如窗口函数、递归和非递归CTE。这就是你正在使用的版本吗?MySQL 8很简单。在旧版本中,这会有点尴尬。所以,请回答草莓的问题。您使用的是哪个版本?@草莓刚刚编辑-使用5.7.12,谢谢如果cap_id的最佳价格出现两次,那么最佳价格是否等于次优价格?还是你在寻找下一个更低的价格呢?5.7是一个相当旧的版本。你不能升级到MySQL 8吗?它提供了许多MySQL一直缺少的窗口函数、递归和非递归CTE功能。我不确定您指的是哪一个“最后一块拼图”——我试着运行您的代码,如下所示:选择x.cap_id,y.price作为最佳价格,x.价格作为下一个最佳报价x在y.cap\u id上加入报价y=x.cap\u id和y.price