5

很简单,尝试这样做

 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

我试过在车站之前添加表名,但没有效果。

4

3 回答 3

11

正如评论中提到的那样,您不能在 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 查询会导致表扫描,而表扫描会导致悲伤。

于 2012-12-03T07:50:24.570 回答
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 列然后对结果进行排序。您也许可以将索引添加到“工作”以加快这些查询。

这是postgresql中的一个sql小提琴

于 2012-12-03T07:27:19.130 回答
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')
于 2019-05-05T06:51:02.683 回答