【问题标题】:How to use SELECT DISTINCT ON with MySQL and Rails如何在 MySQL 和 Rails 中使用 SELECT DISTINCT ON
【发布时间】:2014-11-12 19:22:06
【问题描述】:

我有一个相当复杂的查询,基本上是选择每个送货服务最便宜的送货服务价格。

为了获得每个交付服务的唯一记录,我使用了 SQL 中的DISTINCT 函数。此查询提供了正确的结果:

DeliveryServicePrice.active.select('DISTINCT ON (delivery_service_id) *').order('delivery_service_id, price ASC')

(仅查询的一部分)

然而,这个查询似乎只适用于 PostgreSQL(我认为这很奇怪,因为 PostgreSQL 对 SQL 标准更加严格);它不适用于 MySQL 和 SQLite。我收到以下错误:

Mysql2::Error: 你的 SQL 语法有错误;检查手册 对应于您的 MySQL 服务器版本以获得正确的语法 在 'ON (delivery_service_id) * FROM `delivery_service_prices` 附近使用 WHERE `delivery_servi' 在第 1 行:SELECT DISTINCT ON (delivery_service_id) * FROM `delivery_service_prices` WHERE `delivery_service_prices`.`active` = 1 AND (2808.0 >= min_weight AND 2808.0 = min_length AND 104.0 = min_thickness AND 104.0

我正在构建的应用程序是开源的,因此需要支持所有 3 种数据库类型。 如何在 Rails 框架语法中为 MySQL 和 SQLite 创建DISTINCT ON 查询?

我使用的是 Rails 4.1。

资源

我之前的问题供参考:
How to select unique records based on foreign key column in Rails?

File and line number for where the query is being used.

回答完毕

DeliveryServicePrice.select('delivery_service_prices.id').active.joins('LEFT OUTER JOIN delivery_service_prices t2 ON (delivery_service_prices.delivery_service_id = t2.delivery_service_id AND delivery_service_prices.price > t2.price)').where('t2.delivery_service_id IS NULL')

【问题讨论】:

  • DISTINCT * FROM delivery_service_id?
  • 你试过了吗:DeliveryServicePrice.active.distinct(:delivery_service_id).order('delivery_service_id, price ASC')?
  • 这个查询没有返回正确的结果,它只是返回所有的记录,因为没有 DISTINCT 条件。转换为:SELECT DISTINCT 'delivery_service_prices'.* FROM 'delivery_service_prices' WHERE 'delivery_service_prices'.'active' = 1 ORDER BY delivery_service_id, price ASC

标签: mysql ruby-on-rails sqlite postgresql greatest-n-per-group


【解决方案1】:

DISTINCT ON 是标准 SQL DISTINCT 的 Postgres 特定扩展。它们都不是“函数”,都是 SQL 关键字——尽管DISTINCT ON 之后需要的括号使它看起来 像一个函数。

有几种技术可以用标准 SQL 重写,不过,它们都比较冗长。由于 MySQL 不支持窗口函数row_number() 已淘汰。

详细信息和更多可能的查询技术:

用NOT EXISTS重写:

SELECT *
FROM   delivery_service_prices d1
WHERE  active = 1
AND    2808.0 BETWEEN min_weight AND max_weight
AND    104.0  BETWEEN min_length AND max_length
AND    104.0  BETWEEN min_thickness AND max_thickness
AND NOT EXISTS (
   SELECT 1
   FROM   delivery_service_prices d2
   WHERE  active = 1
   AND    2808.0 BETWEEN min_weight AND max_weight
   AND    104.0  BETWEEN min_length AND max_length
   AND    104.0  BETWEEN min_thickness AND max_thickness
   AND    d2.delivery_service_id = d1.delivery_service_id
   AND    d2.price < d1.price 
   AND    d2.<some_unique_id> < d1.<some_unique_id>  -- tiebreaker!
   )
ORDER  BY delivery_service_id
  • 如果同一delivery_service_id 可以有多个价格相同的行,您需要添加一些独特的平局,以避免每个delivery_service_id 出现多个结果。至少如果您想要一个完全等效的查询。我的示例将从每组欺骗中选择具有最小 &lt;some_unique_id&gt; 的行。

  • 与DISTINCT ON 不同,ORDER BY 在这里是可选的。

【讨论】:

  • 感谢您的回复,您的查询成功了,它发现了我的送货服务价格逻辑中的一个缺陷(每个送货服务的唯一价格)。我最终使用了以下查询,因为它执行得更快:stackoverflow.com/questions/121387/…。它还使我能够合并第二个查询,所以现在我可以在一个查询中执行所有参数。
  • @JellyFishBoy:收获不错。 :)
【解决方案2】:
DeliveryServicePrice.active.select(:delivery_service_id).distinct.order('delivery_service_id, price ASC')

【讨论】:

  • 这个查询没有返回正确的数据,我希望选择送货服务价格记录,而不是他们的 delivery_service_id 属性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-25
  • 2015-11-10
  • 2016-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多