gpt4 book ai didi

sql - Express SQL UNION 查询 Rails 方式

转载 作者:行者123 更新时间:2023-11-29 11:49:59 25 4
gpt4 key购买 nike

我得到了一个运行良好但用 SQL 表示的查询。我希望使用 ActiveRecord 查询接口(interface)表达相同的查询(Arel 也可以)。查询最好返回 ActiveRecord::Relation,或者至少它的结果应该可以转换为一组客户模型。

目标是获取company's customers 没有关联import_logs with remote_type = 'account', 作为以及具有 import_logremote_type = 'account'status = 'pending'customers

客户 可以根本没有关联的import_logs,或者每个remote_type 都有一个import_log,或者仅适用于某些 remote_types。只能有一个关联的 import_log 具有特定的 remote_type 值。

这反射(reflect)了客户可以导入为accountcontact或两者和import_log rails 的要求导入状态。

尽管 import_logcustomer 具有多态关联,但这与任务无关。

现有查询:

Customer.find_by_sql(
<<-SQL
SELECT
customers.*
FROM
customers
WHERE
company_id = #{@company.id}
AND NOT EXISTS
( SELECT *
FROM import_logs
WHERE import_logs.importable_id = customers.id
AND import_logs.importable_type = 'Customer'
AND import_logs.remote_type = 'account'
)
UNION
SELECT
customers.*
FROM
customers,
import_logs
WHERE
import_logs.importable_id = customers.id AND
import_logs.importable_type = 'Customer' AND
company_id = #{@company.id} AND
import_logs.remote_type = 'account' AND
import_logs.status = 'pending';
SQL
)

ImportLog 模型的相关部分:

create_table "import_logs", force: true do |t|
t.integer "importable_id"
t.string "importable_type"
t.string "status", default: "pending", null: false
t.string "remote_type"
...
end

add_index "import_logs", ["importable_id", "importable_type", "remote_type"], unique: true ...

class ImportLog < ActiveRecord::Base
...
belongs_to :importable, polymorphic: true
...
end

客户模型的相关部分:

create_table "customers", force: true do |t|
t.integer "company_id"
...
end

class Customer < ActiveRecord::Base
...
belongs_to :company
has_many :import_logs, as: :importable
...
end

以及公司模式,以防万一:

class Company < ActiveRecord::Base
...
has_many :customers
...
end

最佳答案

合并关联

事实上,只有一种关联是由查询常量驱动的。

"customers"."company_id" = #{@company.id}

这等同于:

.merge(@company.customers)

...这看起来更安全、更明智

Arel 表

我们很快就会需要它。

customers = Customer.arel_table

NOT EXISTS ... 子查询

Arel 可以做到这一点,唯一不太明显的是如何引用外部表:

ne_subquery = ImportLog.where(
importable_type: Customer.to_s,
importable_id: customers[:id],
remote_type: 'account'
).exists.not

这会产生一大块 Arel AST,我们可以将其提供给 Rails 的 where 语句。

现在两个查询都变得显而易见了:

first  = @company.customers.where(ne_subquery)
second = @company.customers.joins(:import_logs).merge(
ImportLog.where(
# importable_id: customers[:id], # `joins` already does it
importable_type: Customer.to_s,
remote_type: 'acoount',
status: 'pending'
)
)

这几乎是一对一的转换。

联盟

这是一个棘手的部分,我找到的唯一解决方案具有非常丑陋的语法并且输出有点不同的查询。给定A union B,我们只能构建select X.* from (A union B) X。效果是一样的。

好吧,让我们开始吧

Customer.from(
customers.create_table_alias(
first.union(second),
Customer.table_name
)
)

当然,为了使这个查询更具可读性,您应该:

  • 将它作为一个范围放在 Customer 类中
  • 将可重用部分拆分为范围和关联

关于sql - Express SQL UNION 查询 Rails 方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30789334/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com