按特定值或属性值的数组排序

时间:2013-07-26 02:20:08

标签: ruby-on-rails postgresql

Active Record(Postgres)中是否可以按属性的特定值或数组值对对象进行排序?

让我说我首先要显示带有某些标签的内容,然后继续使用类似的标签,继续使用没有标签的内容。这打算在具有无限滚动的页面上使用,因此首先显示最相关的内容,然后在向下滚动时显示不太相关的内容是有意义的。

Order通过提供属性和DESC或ASC来工作,但是如果我想通过一组特定属性的值对它们进行排序呢?

1 个答案:

答案 0 :(得分:3)

听起来你想说的话是:

  

“对blah列中的项目按以下顺序排序:('fred', 'bob', 'joe', [others], NULL)

如果是这样,那在SQL中本身就不可能。 ActiveRecord可能会在顶部添加一些东西,但我不确定你是如何表达它的。

在PostgreSQL中,您可能会将其写为ORDER BY ... CASE,例如:

SELECT ...
FROM ...
ORDER BY CASE 
  WHEN the_col = 'fred' THEN 1
  WHEN the_col = 'bob' THEN 2
  WHEN the_col = 'joe' THEN 3
  WHEN the_col IS NOT NULL THEN 99999
END;

NULL在默认ASC订单中的任何值之后排序,除非您指定NULLS FIRST,如果没有匹配,则CASE生成NULL

请参阅this simple demo

随着列表的增长,性能将显着更糟糕。这是你可以使用的一些值,最好不是几十,当然不是几百。此外,标准SQL不要求数据库支持ORDER BY子句中的表达式,因此这可能不是特别便携。在ActiveRecord中表达它取决于你。

如果PostgreSQL为idx(element, array)模块中为整数数组提供的idx之类的任意数组提供intarray函数,那么你可以简化一下,但是......它没有'吨。所以你需要一个全长CASE

但是,您可以将其包装在带有数组参数的SQL或PL / PgSQL函数中。这可能让你从AR调用它,虽然它可能会使效率情况更糟,因为它为每个元组执行两个低效的数组排序和扫描操作:

http://sqlfiddle.com/#!12/0885d/1

-- See http://stackoverflow.com/questions/8798055
CREATE FUNCTION array_search(needle ANYELEMENT, haystack ANYARRAY)
RETURNS INT AS '
    SELECT i
      FROM generate_subscripts($2, 1) AS i
     WHERE $2[i] = $1
  ORDER BY i'
LANGUAGE sql STABLE;

CREATE OR REPLACE FUNCTION case_sort(e anyelement, a anyarray)
RETURNS integer
AS '
SELECT CASE
  -- Yes, this wastes time searching the array twice.
  -- You could use PL/PgSQL to avoid that, but it would
  -- probably be slower over-all.
  WHEN array_search(e, a) IS NOT NULL THEN array_search(e,a)
  -- maxint
  WHEN e IS NOT NULL then 2147483647
END;'
LANGUAGE sql STABLE;

SELECT *
FROM order_demo
ORDER BY case_sort(sortcol, ARRAY['fred','joe','bob']);

或者,正如Mu在评论中所建议的,您可以看到ActiveRecord是否可以比数组更容易地处理可变参数函数:

CREATE OR REPLACE FUNCTION case_sort(e anyelement, VARIADIC a anyarray)
RETURNS integer
AS '/* function body unchanged from above */'
LANGUAGE sql STABLE;

SELECT *
FROM order_demo
ORDER BY case_sort(sortcol, 'fred', 'joe','bob');

每次更新演示:http://sqlfiddle.com/#!12/84ed8/1