Может кто-нибудь объяснить этот SQL? (и как я могу «параметризировать» его и вызвать как функцию?) - PullRequest
1 голос
/ 04 января 2012

Я натолкнулся на этот фрагмент SQL-кода voodoo, который используется для пользовательской группировки данных из таблицы.Я хотел бы понять, как это делает магию, но я не в состоянии справиться с этим.Может ли эксперт по SQL на простом английском языке объяснить кому-то, кто не очень хорошо разбирается в SQL, различные части этого фрагмента, что позволяет ему творить чудеса?

select ceil(rnk/10.0) as grp,
       col1, col2, col3, col4, col5, col6, col7
from (select e.col1, e.col2, e.col3, e.col4, e.col5, e.col6, e.col7,
             (select count(*) 
              from mytable d
              where e.col1 > d.col1)+1 as rnk
      from mytable e) x
order by grp;

Часть SQL выше, чтоКажется, я не могу понять, что это за внутренний SQL, который возвращает столбец «x»:

(select count(*) from mytable d
                where e.col1 > d.col1)+1 as rnk
                from mytable e
                ) x

Я ожидал бы, что смогу выполнить этот запрос сам по себе:

select count(*) from mytable d
                where e.col1 > d.col1)+1 as rnk
                from mytable e

Однако, когда я это делаю, я получаю сообщение об ошибке:

ОШИБКА: синтаксическая ошибка в или около "+" ЛИНИЯ 2: где e.col1> d.col1) +1 как rnk

Итак, что там происходит?!

Кроме того, текущий SQL жестко запрограммирован с номером 10. Я хотел бы обернуть функцию вокруг него, чтобывозможность вызывать функцию с номерами, отличными от 10.

Бэкэнд-база данных - PostgreSQL, поэтому функция будет в PL / pgSQL.Вот моя первая попытка написания такой функции - однако, это не совсем правильно, так как я хочу вернуть несколько строк указанных столбцов - так что функцию, приведенную ниже, нужно немного изменить, не совсем уверенный как:

CREATE OR REPLACE FUNCTION my_custom_grouping(in integer, 
                                              out grp integer,
                                              out col1 double,
                                              out col2 double,
                                              out col3 double,
                                              out col4 double,
                                              out col5 double,
                                              out col6 double,
                                              out col7 double
                                              )

    AS $$ SELECT 
       ceil(rnk/$1) as grp,
                col1, col2, col3, col4, col5, col6, col7
                from (
                select e.col1, e.col2, e.col3, e.col4, e.col5, e.col6, e.col7,
                (select count(*) from mytable d
                where e.col1 > d.col1)+1 as rnk
                from mytable e
                ) x
                order by grp;
    $$
    LANGUAGE SQL;

Помимо функции, не возвращающей более одной строки, я не уверен, что это лучший способ параметризации запроса - я на верном пути?- если да, как бы я изменил приведенную выше функцию, чтобы она возвращала несколько строк вместо текущей единственной «строки» (т. е. вывод «нескольких столбцов»)?

Это правильный способ запуска агрегатных функций (сгруппированных по'grp') для данных, возвращаемых функцией?

1 Ответ

5 голосов
/ 04 января 2012

Ваша (упрощенная!) Функция может выглядеть следующим образом:

CREATE OR REPLACE FUNCTION my_custom_grouping(integer)
RETURNS TABLE (
   grp integer,
   col1 double precision,
   col2 double precision,
   col3 double precision,
   col4 double precision,
   col5 double precision,
   col6 double precision,
   col7 double precision) AS
$BODY$
    SELECT ceil(rank() OVER (ORDER BY col1) / $1)::int as grp
          ,col1, col2, col3, col4, col5, col6, col7
    FROM   mytable 
    ORDER  BY 1;
$BODY$ LANGUAGE SQL;

Основные баллы:

  • Обратите внимание, что это language SQL, поэтому не является функцией PL / pgSQL. Вы также можете использовать language plpgsql, но здесь это не обязательно.

  • Я заменил ядро ​​вашего вуду на оконную функцию rank(), которая должна делать то же самое, просто проще.

  • Я также полностью удалил подзапрос. Это не обязательно.

  • Тип double в PostgreSQL называется double precision.

  • Чтобы вернуть несколько строк, определите функцию как RETURNS SETOF record или RETURNS TABLE, как я сделал.

  • ORDER BY может использовать позиционные параметры, поэтому вам не нужно снова описывать вычисление первого столбца: ORDER BY 1.
    Тем не менее, несколько строк в одном grp. Добавьте дополнительные столбцы или выражения в предложение ORDER BY, чтобы получить стабильный порядок сортировки.

...