Несколько вариантов заказа - PullRequest
1 голос
/ 28 января 2012

Очень часто веб-страницы имеют несколько опций заказа для таблицы.Прямо сейчас у меня есть случай, когда есть 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 Ответ

1 голос
/ 29 января 2012

Прежде всего, @order не является допустимым синтаксисом PostgreSQL. Вы, вероятно, позаимствовали стиль синтаксиса у MS SQL Server или MySQL. Вы не можете использовать переменные в простом SQL-запросе, подобном этому.

В PostgreSQL вы, вероятно, создадите функцию . Вы можете использовать переменные там, просто бросьте @.

Сортировка по ARRAY , как правило, довольно медленная - и не требуется в вашем случае. Вы можете упростить до:

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

Однако , подобное выражение CASE не может использовать простые индексы. Итак, если вы ищете производительность, одним из способов (из нескольких) будет функция plpgsql , например:

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, вы также можете просто написать две функции.

Создание многостолбцовых индексов для (c2, c1) и (c3 DESC, c1) для максимальной производительности. Но имейте в виду, что поддержание индексов также сопряжено с затратами, особенно если в вашей таблице много операций записи.


Дополнительный ответ на перефразированный вопрос

Как я уже сказал, конструкция CASE не будет использовать простые индексы. Индексы для выражений будет вариант, но то, что у вас есть в вашем примере, выходит за рамки.

Итак, если вам нужна производительность, создайте запрос в своем приложении (ваш первый подход) или напишите функцию на стороне сервера (возможно, с динамическим SQL и EXECUTE ), которая делает нечто подобное внутри PostgreSQL. Предложение WHERE со сложным оператором CASE работает, но медленнее.

...