多种订购选项

时间:2012-01-28 15:13:38

标签: postgresql indexing sql-order-by postgresql-9.0

网页对表有多个排序选项是很常见的。现在我有一个案例,有12个选项(ordenable列)。最简单的(我所知道的)方法是构建连接字符串的SQL查询。但我想知道这是否是最好的方法。字符串连接就像这样(python代码):

order = {
    1: "c1 desc, c2",
    2: "c2, c3",
    ...
    12: "c10, c9 desc"
    }
...
query = """
select c1, c2
from the_table
order by %(order)s
"""
...
cursor.execute(query, {'order': AsIs(order[order_option])})
...

到目前为止,我的替代解决方案是在order by子句中放置一系列案例:

select c1, c2
from the_table
order by
    case %(order_option)s
        when 1 then array[c1 * -1, c2]
        when 2 then array[c2, c3]
        else [0.0, 0.0]
    end
    ,
    case %(order_option)s
        when 3 then c4
        else ''
    end
    ,
    ...
    ,
    case when %(order_option)s < 1 or %(order_option)s > 12 then c5 end
;

有关多个订购选择的最佳做法是什么?在我的替代代码中使用索引利用会发生什么?

1 个答案:

答案 0 :(得分:1)

首先,@order是无效的PostgreSQL语法。您可能从MS SQL Server或MySQL借用了语法样式。你不能在像这样的普通SQL查询中使用变量。

在PostgreSQL中你可能会create a function。您可以在那里使用变量,只需删除@

ARRAY 排序通常相当慢 - 在您的情况下不是必需的。您可以简化为:

ORDER  BY
       CASE _order
          WHEN 1 THEN c2
          WHEN 2 THEN c3 * -1
          ELSE NULL  -- undefined!
       END
     , c1

但是,像这样的CASE表达式不能使用普通索引。所以,如果你正在寻找性能,一种方式(几种)就像这样plpgsql function

CREATE OR REPLACE FUNCTION foo(int)
  RETURNS TABLE(c1 int, c2 int) AS
$BODY$
BEGIN

CASE $1
WHEN 1 THEN
    RETURN QUERY
    SELECT t.c1, t.c2
    FROM   tbl t
    ORDER  BY t.c2, t.c1;

WHEN 2 THEN
    RETURN QUERY
    SELECT t.c1, t.c2
    FROM   tbl t
    ORDER  BY t.c3 DESC, t.c1;
ELSE
    RAISE WARNING 'Unexpected parameter: "%"', $1;
END CASE;

END;
$BODY$
  LANGUAGE plpgsql STABLE;

这样,甚至可以使用普通索引。

如果你实际上只有两个ORDER BY选项,你也可以写两个 功能

创建multi-column indexes on (c2, c1)(c3 DESC, c1)以获得最佳效果。但请注意,维护索引也会带来成本,特别是如果您的表看到很多写操作。


改写问题的补充答案

正如我所说,CASE结构不会使用普通索引。 Indexes on expressions将是一个选项,但您的示例中的内容超出了范围。

所以,如果你想要性能,在你的应用程序中构建查询(第一种方法)或编写一个服务器端函数(可能使用动态SQL和EXECUTE),它在PostgreSQL中做了类似的事情。带有复杂WHERE语句的CASE子句有效,但速度较慢。