非常简单,尝试这样做
SELECT (artist_name || ' ' || name) as full_name FROM "songs" WHERE "songs"."working" = 't' AND (full_name ILIKE('%Jack Beats%')) AND (full_name ILIKE('%Epidemic%')) AND (full_name ILIKE('%Dillon Francis%')) ORDER BY songs.published_at asc LIMIT 1
但是我得到了
ActiveRecord::StatementInvalid: PG::Error: ERROR: column "full_name" does not exist
我已经尝试在电台之前添加表名,但没有效果。
答案 0 :(得分:8)
在注释中提到sub_stantial时,不能在WHERE子句中引用SELECT中的别名。您可以使用派生表作为dwurf建议,但Rails中的派生表有点混乱。您可以在WHERE中扩展连接:
Song.where(:working => true)
.where("artist_name || ' ' || name ILIKE ?", '%Jack Beats%')
.where("artist_name || ' ' || name ILIKE ?", '%Epidemic%')
.where("artist_name || ' ' || name ILIKE ?", '%Dillon Francis%')
.order('songs.published_at asc')
.limit(1)
如果你做了很多这样的事情,命名范围可能会有用:
class Song < ActiveRecord::Base
#...
def self.full_name_like(name)
where("artist_name || ' ' || name ILIKE ?", "%#{name}%")
end
end
然后:
Song.where(:working => true)
.full_name_like('Jack Beats')
.full_name_like('Epidemic')
.full_name_like('Dillon Francis')
.order('songs.published_at asc')
.limit(1)
如果您的应用程序要进行大量ILIKE搜索,那么您可能需要查看全文搜索系统:LIKE查询会导致表扫描,表扫描会导致悲伤。
答案 1 :(得分:2)
您无法在where子句中引用列别名。编写此查询的正确方法是:
SELECT
(artist_name || ' ' || name) AS full_name
FROM "songs"
WHERE "songs"."working" = 't'
AND ((artist_name || ' ' || name) ILIKE('%Jack Beats%'))
AND ((artist_name || ' ' || name) ILIKE('%Epidemic%'))
AND ((artist_name || ' ' || name) ILIKE('%Dillon Francis%'))
ORDER BY songs.published_at ASC
limit 1
;
sub_stantial的方法看起来更像是这样:
select full_name
from (
SELECT
(artist_name || ' ' || name) AS full_name
FROM "songs"
WHERE "songs"."working" = 't'
ORDER BY songs.published_at ASC
)
WHERE (full_name ILIKE('%Jack Beats%'))
AND (full_name ILIKE('%Epidemic%'))
AND (full_name ILIKE('%Dillon Francis%'))
LIMIT 1
;
这两个查询的性能大致相同(非常垃圾),因为它们都必须进行全表扫描以构建full_name列,然后对结果进行排序。您可以将“索引”添加到“工作”以加速这些查询。
答案 2 :(得分:0)
程序化Arel版本
注意:这尚未针对SQL注入进行全面测试。
class ApplicationRecord < ActiveRecord::Base
scope :fields_sentence_ilike, -> (*fields, term) {
sanitized_term = connection.quote("%#{term}%")
# InfixOperation.new(operator, left, right) => left operator right => concatenated_fiels ILIKE '%word%'
# NamedFunction.new(name, expression_nodes) => name(node0, node1, ...nodeN) => CONCAT_WS("columnA", "columnB", "columnC")
where(
Arel::Nodes::InfixOperation.new(
Arel::Nodes::SqlLiteral.new('ILIKE'),
Arel::Nodes::NamedFunction.new(
'CONCAT_WS', # CONCAT_WS concatenates strings using the first argument. In this case, an empty space.
[
Arel::Nodes::SqlLiteral.new("' '"), # CONCAT by empty space
*fields.map { |field|
# CONCATING any NULL fields results NULL (like multiplying any number by 0 equals 0). COALESCE to empty string.
Arel::Nodes::NamedFunction.new('COALESCE', [arel_attribute(field), Arel::Nodes::SqlLiteral.new("''")])
}
]
),
Arel::Nodes::SqlLiteral.new(sanitized_term)
)
)
}
end
然后为您的Songs模型提供特定的实现
class Song < ApplicationRecord
scope :full_name_like, -> (full_name) { fields_sentence_ilike(:artist_name, :name, full_name) }
end
用法
Song.full_name_like('Jack Beats')
.full_name_like('Epidemic')
.full_name_like('Dillon Francis')