简体   繁体   English

如何在MySQL和Rails中使用SELECT DISTINCT ON

[英]How to use SELECT DISTINCT ON with MySQL and Rails

I have quite a complex query to essentially select the cheapest delivery service price per delivery service . 我有一个非常复杂的查询,基本上选择每个送货服务最便宜的送货服务价格

In order to get unique records per delivery service, I utilise the DISTINCT function in SQL. 为了获得每个传递服务的唯一记录,我在SQL中使用DISTINCT函数。 This query provides correct results: 此查询提供了正确的结果:

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

(only a part of the query) (只是查询的一部分)

However, this query only seems to work with PostgreSQL (which I think is strange considering PostgreSQL is a lot more strict with SQL standards); 但是,这个查询似乎只适用于PostgreSQL(考虑到PostgreSQL对SQL标准的严格要求,我认为这很奇怪); it does not work with MySQL and SQLite. 它不适用于MySQL和SQLite。 I receive the following error: 我收到以下错误:

Mysql2::Error: You have an error in your SQL syntax; Mysql2 ::错误:您的SQL语法有错误; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ON (delivery_service_id) * FROM `delivery_service_prices` WHERE `delivery_servi' at line 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 <= max_weight AND 104.0 >= min_length AND 104.0 <= max_length AND 104.0 >= min_thickness AND 104.0 <= max_thickness) ORDER BY delivery_service_id, price ASC 查看与您的MySQL服务器版本相对应的手册,以便在第1行'ON(delivery_service_id)* FROM`defer_service_prices` WHERE`deport_servi'附近使用正确的语法:SELECT DISTINCT ON(delivery_service_id)* FROM`defer_service_prices` WHERE`deport_service_prices` .`active` = 1 AND(2808.0> = min_weight AND 2808.0 <= max_weight AND 104.0> = min_length AND 104.0 <= max_length AND 104.0> = min_thickness AND 104.0 <= max_thickness)ORDER BY delivery_service_id,价格ASC

The application I'm building is open source, so it's required to support all 3 database types. 我正在构建的应用程序是开源的,因此需要支持所有3种数据库类型。 How do I create DISTINCT ON queries for MySQL and SQLite in the Rails framework syntax? 如何在Rails框架语法中为MySQL和SQLite创建DISTINCT ON查询?

I'm using Rails 4.1. 我正在使用Rails 4.1。

Resources 资源

My previous problem for reference: 我以前的问题供参考:
How to select unique records based on foreign key column in Rails? 如何在Rails中基于外键列选择唯一记录?

File and line number for where the query is being used. 查询使用位置的文件和行号。

Finished answer 完成答案

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 ON is a Postgres specific extension to the standard SQL DISTINCT . DISTINCT ON是标准SQL DISTINCT的Postgres特定扩展。 Neither of them is a "function", both are SQL key words - even though the parentheses required after DISTINCT ON make it look like a function. 它们都不是“函数”,都是SQL关键词 - 即使在DISTINCT ON之后需要的括号使它看起来像一个函数。

There are a couple of techniques to rewrite this with standard-SQL, all of them more verbose, though. 有一些技术可以使用标准SQL重写它,但所有这些技术都更加冗长。 Since MySQL does not support window-functions row_number() is out. 由于MySQL不支持窗口函数,因此row_number()已经输出。

Details and more possible query techniques: 细节和更多可能的查询技术:

Rewritten with NOT EXISTS : 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
  • If there can be multiple rows with the same price for the same delivery_service_id , you need to add some unique tie-breaker to avoid multiple results per delivery_service_id . 如果同一个delivery_service_id可能存在多个具有相同价格的行,则需要添加一些独特的tie-breaker以避免每个delivery_service_id多个结果。 At least if you want a perfectly equivalent query. 至少如果你想要一个完全等价的查询。 My example would select the row with the smallest <some_unique_id> from each set of dupes. 我的例子将从每组欺骗中选择具有最小<some_unique_id>的行。

  • Unlike with DISTINCT ON , ORDER BY is optional here. DISTINCT ON不同, ORDER BY在这里是可选的。

DeliveryServicePrice.active.select(:delivery_service_id).distinct.order('delivery_service_id, price ASC')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM