如何以 Rails 的方式写一个 SQL NOT EXISTS query/scope?
How to write an SQL NOT EXISTS query/scope in the Rails way?
我有一个数据库范围来过滤特定 Proxy
和 environment
的最新 ProxyConfig
版本。
这是与 MySQL、PostgreSQL 和 Oracle:
配合得很好的原始 SQL
class ProxyConfig < ApplicationRecord
...
scope :current_versions, -> do
where %(NOT EXISTS (
SELECT 1 FROM proxy_configs pc
WHERE proxy_configs.environment = environment
AND proxy_configs.proxy_id = proxy_id
AND proxy_configs.version < version
))
end
...
end
你可以在我的 baby_squeel issue.
中找到一个简单的测试用例
但我发现最好不要直接使用 SQL。我花了很多时间尝试不同的方法以 Rails 的方式编写它,但无济于事。我找到了通用的 Rails 和 baby_squeel 示例,但它们总是涉及不同的表。
PS 以前的版本使用 joins
但是它非常慢并且搞乱了一些查询。例如 #count
产生了 SQL 语法错误。所以我对使用其他方法不是很开放。相反,我更愿意知道如何准确地实现这个查询。尽管我至少很想看看其他简单的解决方案。
PPS关于直接SQL可以的问题。在这种情况下,大部分是。也许所有 RDBMS 都能理解这句话。如果需要比较 text
字段,虽然这需要 Oracle 上的特殊功能。在 Postgres 上,不区分大小写的 LIKE
是 ILIKE
。它可以由 Arel
自动处理。在原始 SQL 中,不同的 RDBMS 需要不同的字符串。
这实际上不是您可以单独使用 ActiveRecord 查询接口构建的查询。不过可以通过少量的 Arel 来完成:
class ProxyConfig < ApplicationRecord
def self.current_versions
pc = arel_table.alias("pc")
where(
unscoped.select(1)
.where(pc[:environment].eq(arel_table[:environment]))
.where(pc[:proxy_id].eq(arel_table[:proxy_id]))
.where(pc[:version].gt(arel_table[:version]))
.from(pc)
.arel.exists.not
)
end
end
生成的 SQL 不相同,但我认为它在功能上应该是等效的。
SELECT "proxy_configs".* FROM "proxy_configs"
WHERE NOT (
EXISTS (
SELECT 1 FROM "proxy_configs" "pc"
WHERE "pc"."environment" = "proxy_configs"."environment"
AND "pc"."proxy_id" = "proxy_configs"."proxy_id"
AND "pc"."version" > "proxy_configs"."version"
)
)
我有一个数据库范围来过滤特定 Proxy
和 environment
的最新 ProxyConfig
版本。
这是与 MySQL、PostgreSQL 和 Oracle:
配合得很好的原始 SQLclass ProxyConfig < ApplicationRecord
...
scope :current_versions, -> do
where %(NOT EXISTS (
SELECT 1 FROM proxy_configs pc
WHERE proxy_configs.environment = environment
AND proxy_configs.proxy_id = proxy_id
AND proxy_configs.version < version
))
end
...
end
你可以在我的 baby_squeel issue.
中找到一个简单的测试用例但我发现最好不要直接使用 SQL。我花了很多时间尝试不同的方法以 Rails 的方式编写它,但无济于事。我找到了通用的 Rails 和 baby_squeel 示例,但它们总是涉及不同的表。
PS 以前的版本使用 joins
但是它非常慢并且搞乱了一些查询。例如 #count
产生了 SQL 语法错误。所以我对使用其他方法不是很开放。相反,我更愿意知道如何准确地实现这个查询。尽管我至少很想看看其他简单的解决方案。
PPS关于直接SQL可以的问题。在这种情况下,大部分是。也许所有 RDBMS 都能理解这句话。如果需要比较 text
字段,虽然这需要 Oracle 上的特殊功能。在 Postgres 上,不区分大小写的 LIKE
是 ILIKE
。它可以由 Arel
自动处理。在原始 SQL 中,不同的 RDBMS 需要不同的字符串。
这实际上不是您可以单独使用 ActiveRecord 查询接口构建的查询。不过可以通过少量的 Arel 来完成:
class ProxyConfig < ApplicationRecord
def self.current_versions
pc = arel_table.alias("pc")
where(
unscoped.select(1)
.where(pc[:environment].eq(arel_table[:environment]))
.where(pc[:proxy_id].eq(arel_table[:proxy_id]))
.where(pc[:version].gt(arel_table[:version]))
.from(pc)
.arel.exists.not
)
end
end
生成的 SQL 不相同,但我认为它在功能上应该是等效的。
SELECT "proxy_configs".* FROM "proxy_configs"
WHERE NOT (
EXISTS (
SELECT 1 FROM "proxy_configs" "pc"
WHERE "pc"."environment" = "proxy_configs"."environment"
AND "pc"."proxy_id" = "proxy_configs"."proxy_id"
AND "pc"."version" > "proxy_configs"."version"
)
)