powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Пользовательские аггрегируемые функции
6 сообщений из 6, страница 1 из 1
Пользовательские аггрегируемые функции
    #39566811
Valentina.Yu
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Задача следующая: необходимо расширить/перегрузить постгресовские аггрегируемые функции LEAD и LAG для работы с NULL значениями, чтобы эмулировать работу IGNORE NULLS, реализованную в Oracle.

Вариант с WITH или сабселектом не подходит , так как функции используются в сложном запросе, а решение должно быть 100% рабочим.

Пыталась разобраться с пользовательскими аггрегируемыми функциями (полагаю можно реализовать необходимую логику этим способом) по документации https://www.postgresql.org/docs/9.6/static/sql-createaggregate.html, но там черт глову сломит. Не понятно какие значения можно передавать в аргументы, как вообще постгрес понимает что необходимо делать с функцией?


Для примера привожу как работает в Oracle IGNORE NULLS.

Код: plsql
1.
2.
3.
4.
5.
6.
-- без IGNORE NULLS - слева на скрине
[SRC PLSQL]SELECT empno,
       ename,
       orig_salary,
       LAG(orig_salary, 1, 0) OVER (ORDER BY orig_salary) AS sal_prev
FROM   tbl_lead;



Код: plsql
1.
2.
3.
4.
5.
6.
-- с IGNORE NULLS - справа на скрине
SELECT empno,
       ename,
       orig_salary,
       LAG(orig_salary, 1, 0) IGNORE NULLS OVER (ORDER BY orig_salary) AS sal_prev
FROM   tbl_lead;





Необходимо добиться подобного поведения в постгрес (возможно через пользовательские аггрегируемые функции).
Может кто разбирается в этом, может помочь?
...
Рейтинг: 0 / 0
Пользовательские аггрегируемые функции
    #39566814
Фотография Щукина Анна
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Valentina.Yu,

Lead/lag with ignore nulls можно сэмулировать «трюком» на аналитических min/max
...
Рейтинг: 0 / 0
Пользовательские аггрегируемые функции
    #39566817
Фотография Щукина Анна
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Пользовательские аггрегируемые функции
    #39566818
Фотография Щукина Анна
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Щукина АннаValentina.Yu,

вариант реализации на last_value/first_value
вариант реализации на min/max а, пардон... первый вариант - тоже на аналитическом min/max :)
...
Рейтинг: 0 / 0
Пользовательские аггрегируемые функции
    #39705495
nikodim99
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем добрый день. Хотелось бы продолжить разговор, чтобы без трюков. В этих ваших нашел способ решения для lag ignore nulls (правда пришлось допилить, заодно и посмотреть какие значения передаются в эти агрегаты/функции).
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
-- Реализация аггрегата lag с параметром ignore nulls для postgres
CREATE OR REPLACE FUNCTION swf_lag_trans(
                                p_state anyarray, -- внутреннее состояние
                                p_current anyelement, -- значение, по которому работает агрегат (следующие значение данных)
                                p_offset INTEGER, -- смещение
                                p_default anyelement -- значение по-умолчанию
                                )
RETURNS anyarray -- следующее внутреннее состояние
LANGUAGE plpgsql AS 
$function$
BEGIN
    raise notice 'p_state: %, p_current: %, p_offset: %, p_default: %', p_state, p_current, p_offset, p_default;
    IF p_state IS NULL THEN
        -- если состояние пустое, то заполняем его значениями по-умолчанию
        p_state := array_fill(p_default, ARRAY[p_offset+1]);        
    END IF;    
                        
    IF p_state[p_offset+1] IS NOT NULL THEN         
        -- если последнее значение в состоянии не нулл, то сдвигаем окно на величину offset
        FOR i IN 1..p_offset loop
            p_state[i] := p_state[i+1];           
        END loop;                 
    END IF;        
    
    IF p_current IS NOT NULL THEN
        p_state[p_offset+1] := p_current;    
    END IF;
    
    raise notice 'Result p_state: %', p_state;
    raise notice '***********************************************************************';
        
    return p_state;
END $function$;

CREATE OR REPLACE FUNCTION swf_lag_final(anyarray)
RETURNS anyelement LANGUAGE SQL 
AS 
$function$
select $1[1];-- возвращает 1й элемент состояния
$function$;
                        
CREATE AGGREGATE swf_lag(anyelement, -- что
                        INTEGER, -- на сколько
                        anyelement -- значение если NULL
                        ) 
(sfunc = swf_lag_trans,
stype = anyarray,
finalfunc = swf_lag_final);



Проверочный запрос:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
WITH my_table(NAME, salary) AS 
(VALUES ('A', 100), ('B', 200), ('C', 300), ('D', NULL), ('E', NULL), ('F', NULL), ('J',500), ('H',600), ('I',700)
)
SELECT 
    NAME, salary, 
    lag(salary, 1, NULL) OVER (ORDER BY NAME) prev_salary,    
    swf_lag(salary, 1, NULL)  OVER (ORDER BY NAME) my_prev_salary,
    lead(salary, 1, -1) OVER (ORDER BY NAME) lead_salary
    -- а вот тут должна быть функция swf_lead :(((
FROM my_table;


выдает корректные данные:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Name	salary	prev_salary	my_prev_salary	lead_salary
A	100					200
B	200	100		100		300
C	300	200		200		
D		300		300		
E				300		
F				300		600
H	600			300		700
I	700	600		600		500
J	500	700		700		-1



Но никак не могу понять, как сделать реализацию lead через агрегаты (если вот только как-то отсортировать в обратном порядке на этапе реализации). Может кто-нибудь встречал/делал реализацию?
...
Рейтинг: 0 / 0
Пользовательские аггрегируемые функции
    #39705572
nikodim99
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Точно!
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
WITH my_table(NAME, salary) AS 
(VALUES ('A', 100), ('B', 200), ('C', 300), ('D', NULL), ('E', NULL), ('F', NULL), ('G',500), ('H',600), ('I',700)
)
select * from (
SELECT 
    NAME, salary, 
    lag(salary, 1, NULL) OVER (ORDER BY NAME) prev_salary,    
    swf_lag(salary, 1, NULL)  OVER (ORDER BY NAME) ign_prev_salary,
    lead(salary, 1, NULL) OVER (ORDER BY NAME) lead_salary,
    swf_lag(salary, 1, NULL) OVER (ORDER BY NAME DESC) ign_lead_salary    --меняем сортировку на обратную и вуа-ля!
FROM my_table) a
order by 1 asc;



Получили:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Name	salary	prev_salary	ign_prev_salary	lead_salary	ign_lead_salary    
A	100					200		200
B	200	100		100		300		300
C	300	200		200				500
D		300		300				500
E				300				500
F				300		500		500
G	500			300		600		600
H	600	500		500		700		700
I	700	600		600				
...
Рейтинг: 0 / 0
6 сообщений из 6, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Пользовательские аггрегируемые функции
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]