按非主键属性分组 min(Value) 并从同一行加入附加属性(使用原始 SQL 或 SQLalchemy)

Posted

技术标签:

【中文标题】按非主键属性分组 min(Value) 并从同一行加入附加属性(使用原始 SQL 或 SQLalchemy)【英文标题】:Group min(Value) by non-primary key-attribute and join additional attribute from the same row (with raw SQL or SQLalchemy) 【发布时间】:2018-05-12 23:54:02 【问题描述】:

鉴于我有下表“PriceRecord”:

| PriceRecord_ID | Company_ID | Price | Tarif_type | Tarif_Model | ... attributes |
|:--------------:|:----------:|:-----:|:----------:|:----------:|:--------------:|
|        1       |      A     |   10  |   tarif_1  |   Model_1   |       ...      |
|        2       |      A     |   20  |   tarif_2  |   Model_1   |       ...      |
|        3       |      A     |   10  |   tarif_3  |   Model_2   |       ...      |
|        4       |      B     |   11  |   tarif_1  |   Model_2   |       ...      |
|        5       |      B     |   15  |   tarif_2  |   Model_3   |       ...      |
|        6       |      C     |   10  |   tarif_1  |   Model_4   |       ...      |

我的目标是获得每家公司的最低(价格)。对于每个 min(Price),我还需要其他属性(例如 Tarif_type、Tarif_name)。

预期结果:

| PriceRecord_ID | Company_ID | Price | Tarif_type | Tarif_Model | ... attributes |
|:--------------:|:----------:|:-----:|:----------:|:----------:|:--------------:|
|        1       |      A     |   10  |   tarif_1  |   Model_1   |       ...      |
|        4       |      B     |   11  |   tarif_1  |   Model_1   |       ...      |
|        6       |      C     |   10  |   tarif_1  |   Model_2   |       

我知道如何按公司获取最低(价格)和分组。我的问题是,我无法加入其他属性(因为公司名称不是唯一键。

我尝试了这个查询(如果两个条件都满足,则打算加入该表 --> 不幸的是,它为每个条件加入了两次,并且我的结果表中每个公司都有多个记录)

    subquery = db.session.query(PriceRecord.company_id, db.func.min(PriceRecord.Price).label("minPrice")) \
        .group_by(PriceRecord.company_id) \
        .subquery()
    result = db.session.query(subquery.c.company_id, subquery.c.minPrice, PriceRecord.tarif_type, PriceRecord.tarif_model) \
    .join(PriceRecord, subquery.c.insurance_company==PriceRecord.company_id and subquery.c.minPrice==PriceRecord.Price) \

我找到了 this 类似的解决方案,但无法针对我的场景实施。

非常感谢任何帮助!提前谢谢你

【问题讨论】:

您没有清楚地使用“公司”。您似乎在说公司列实际上是公司名称。如果公司名称不是唯一的,您怎么可能获得每个公司的信息?您只能获取每个公司名称的信息。您需要在名称旁边附上公司 ID。 感谢您的意见。我澄清了上面的描述。公司是另一个表的外键。但我的问题是,一家公司可以有多个具有相同最小值(价格)的价格记录。在这种情况下,只希望一个记录显示在我的结果表中(哪个记录并不重要,例如找到的第一个或最容易实现的记录)。该表有 250k 的价格记录,否则它会很快变得混乱。谢谢你的时间 【参考方案1】:
SELECT b.*
FROM (
  SELECT Company, MIN(Price) Price FROM PriceRecord GROUP BY Company
  ) a
JOIN PriceRecord b on a.Company = b.Company and a.Price = b.Price

一个警告 - 如果 A 公司有两行价格为 10,则两者都将返回。

【讨论】:

您的解决方案是正确的,但不幸的是,大多数公司都有多个价格相同的价格记录 -> 这就是我的结果表中有多个条目的原因。是否有可能只获得每家公司的 min(Price) 第一行? @Simon “第一”是什么意思?首先是什么时候订购的?无论如何,这里没有每个公司,只有每个公司。 找到的 min(Prices) 中的哪一个以及相应的属性显示在结果表中并不重要。以最容易实现的为准。非常感谢您的帮助 将 SELECT 更改为:SELECT DISTINCT b.Company, b.Price 这将使您每家公司只有一行,并且该公司的最低价格。【参考方案2】:

经过一番研究,我能够使用以下 sql 代码得到预期的结果:

select * 
from
 (
  select
    row_number () over (partition by company_ID order by premium asc) as rownumber, 
    min(Price) over (partition by company_ID) as minimalPrice, *
  FROM price_table
 ) subquery
where subquery.rownumber = 1

使用所谓的窗口函数,您可以在没有 group_by 语句的情况下使用聚合函数。 rownumber 函数为每个价格记录分配一个“动态”创建的编号(由于分区函数,每个公司的 rownumber 再次从 1 开始)。因为我需要按 rownumber = 1 过滤的每家公司的确切结果。

这里是我的内部选择的示例(没有过滤 rownumber 等于 = 1)

rownumber minprice company_id  price
   1       408.9      8         408.9
   2       408.9      8         436.1
   3       408.9      8         439.7
   4       408.9      8         463.1
   5       408.9      8         468.9
   6       408.9      8         490.3
   7       408.9      8         498
   8       408.9      8         517.5
   9       408.9      8         527.2
   10      408.9      8         528.2
   11      408.9      8         556.4
   12      408.9      8         568
   1       364.4      32        364.4
   2       364.4      32        387.6
   3       364.4      32        391.8
   4       364.4      32        416.8
   5       364.4      32        419.3
   6       364.4      32        446
   7       364.4      32        446.6
   8       364.4      32        474.1
   9       364.4      32        475.1
   10      364.4      32        485
   11      364.4      32        504.3
   12      364.4      32        515.9
   1       412        57        412
   2       412        57        433.7
   3       412        57        439.7

我希望这会有所帮助,我花了一些时间来研究那些 sql 函数。

【讨论】:

以上是关于按非主键属性分组 min(Value) 并从同一行加入附加属性(使用原始 SQL 或 SQLalchemy)的主要内容,如果未能解决你的问题,请参考以下文章

从同一行电源查询中获取列

非主键之间的休眠关系OneToMany

数据库范式

数据库三范式 无重复列 完全依赖主键 属性不依赖非主属性

JpaRepository 注解 如何根据某非主键属性删除一个(或一批)记录?

Mysql