在Postgresql中模拟MySQL的ORDER BY FIELD()

Pee*_*lan 42 mysql postgresql

刚刚试用PostgreSQL,来自MySQL.在我们的Rails应用程序中,我们有几个带SQL的位置,如下所示:

SELECT * FROM `currency_codes` ORDER BY FIELD(code, 'GBP', 'EUR', 'BBD', 'AUD', 'CAD', 'USD') DESC, name ASC
Run Code Online (Sandbox Code Playgroud)

没过多久就发现PostgreSQL不支持/允许这样做.

有谁知道如何在PostgreSQL中模拟这种行为,还是我们必须将整理到代码中?

Gre*_*ith 60

啊,gahooa如此接近:

SELECT * FROM currency_codes
  ORDER BY
  CASE
    WHEN code='USD' THEN 1
    WHEN code='CAD' THEN 2
    WHEN code='AUD' THEN 3
    WHEN code='BBD' THEN 4
    WHEN code='EUR' THEN 5
    WHEN code='GBP' THEN 6
    ELSE 7
  END,name;
Run Code Online (Sandbox Code Playgroud)

  • 使用DISTINCT时,此方法不起作用.针对此场景的任何其他想法? (4认同)
  • 我不确定为什么会这样,但我想出了另一种选择.如果你想按j,a,k,e的顺序得到结果,那么你的顺序是id = e,id = k,id = a,id = j`. (2认同)

ilg*_*gam 26

在mysql中排序:

> ids = [11,31,29]
=> [11, 31, 29]
> User.where(id: ids).order("field(id, #{ids.join(',')})")
Run Code Online (Sandbox Code Playgroud)

在postgres:

def self.order_by_ids(ids)
  order_by = ["CASE"]
  ids.each_with_index do |id, index|
    order_by << "WHEN id='#{id}' THEN #{index}"
  end
  order_by << "END"
  order(order_by.join(" "))
end

User.where(id: [3,2,1]).order_by_ids([3,2,1]).map(&:id) 
#=> [3,2,1]
Run Code Online (Sandbox Code Playgroud)

  • 金子。铁路方式。 (2认同)

pil*_*row 13

更新,充实了@Tometzky的极好建议.

这应该FIELD()在pg 8.4下给你一个MySQL -alike函数:

-- SELECT FIELD(varnames, 'foo', 'bar', 'baz')
CREATE FUNCTION field(anyelement, VARIADIC anyarray) RETURNS numeric AS $$
  SELECT
    COALESCE(
     ( SELECT i FROM generate_subscripts($2, 1) gs(i)
       WHERE $2[i] = $1 ),
     0);
$$ LANGUAGE SQL STABLE
Run Code Online (Sandbox Code Playgroud)

Mea culpa,但我现在无法在8.4上验证以上内容; 但是,我可以倒退到一个"道德"等效版本,该版本适用于我面前的8.1实例:

-- SELECT FIELD(varname, ARRAY['foo', 'bar', 'baz'])
CREATE OR REPLACE FUNCTION field(anyelement, anyarray) RETURNS numeric AS $$
  SELECT
    COALESCE((SELECT i
              FROM generate_series(1, array_upper($2, 1)) gs(i)
              WHERE $2[i] = $1),
             0);
$$ LANGUAGE SQL STABLE
Run Code Online (Sandbox Code Playgroud)

更尴尬的是,您仍然可以移植使用(可能派生的)货币代码排名表,如下所示:

pg=> select cc.* from currency_codes cc
     left join
       (select 'GBP' as code, 0 as rank union all
        select 'EUR', 1 union all
        select 'BBD', 2 union all
        select 'AUD', 3 union all
        select 'CAD', 4 union all
        select 'USD', 5) cc_weights
     on cc.code = cc_weights.code
     order by rank desc, name asc;
 code |           name
------+---------------------------
 USD  | USA bits
 CAD  | Canadian maple tokens
 AUD  | Australian diwallarangoos
 BBD  | Barbadian tridents
 EUR  | Euro chits
 GBP  | British haypennies
(6 rows)
Run Code Online (Sandbox Code Playgroud)


Tom*_*zky 11

这是我认为最简单的方法:

create temporary table test (id serial, field text);
insert into test(field) values
  ('GBP'), ('EUR'), ('BBD'), ('AUD'), ('CAD'), ('USD'),
  ('GBP'), ('EUR'), ('BBD'), ('AUD'), ('CAD'), ('USD');
select * from test
order by field!='GBP', field!='EUR', field!='BBD',
  field!='AUD', field!='CAD', field!='USD';
 id | field 
----+-------
  1 | GBP
  7 | GBP
  2 | EUR
  8 | EUR
  3 | BBD
  9 | BBD
  4 | AUD
 10 | AUD
  5 | CAD
 11 | CAD
  6 | USD
 12 | USD
(12 rows)
Run Code Online (Sandbox Code Playgroud)

在PostgreSQL 8.4中,您还可以使用具有可变数量参数的函数(可变参数函数)来实现端口field函数.


0xF*_*0D3 11

SELECT * FROM (VALUES ('foo'), ('bar'), ('baz'), ('egg'), ('lol')) t1(name)
ORDER BY ARRAY_POSITION(ARRAY['foo', 'baz', 'egg', 'bar'], name)
Run Code Online (Sandbox Code Playgroud)

这个怎么样?上面的一个获取如下:

foo
baz
egg
bar
lol
Run Code Online (Sandbox Code Playgroud)

正如您已经了解的那样,如果某个元素不在数组中,那么它会转到后面。IN请注意,如果您需要具有自定义排序顺序的运算符,您也可以使用相同的技术:

foo
baz
egg
bar
lol
Run Code Online (Sandbox Code Playgroud)


gao*_*fei 5

自 Rails 6.1 起, ilgam 的答案将不起作用,ActiveRecord::UnknownAttributeReference将引发错误:https ://api.rubyonrails.org/classes/ActiveRecord/UnknownAttributeReference.html

推荐的方法是使用 Arel 而不是原始 SQL。

除了ilgam 的答案之外,这里是 Rails 6.1 的解决方案:

def self.order_by_ids(ids)
  t = User.arel_table
  condition = Arel::Nodes::Case.new(t[:id])
  ids.each_with_index do |id, index|
    condition.when(id).then(index)
  end
  order(condition)
end
Run Code Online (Sandbox Code Playgroud)