01 февраля 2008

Аггрегируй это.

Проблема
Уже в стародавние времена в STL языка C++ была функция для подсчёта элементов, удовлетворяющих условию. В SQL этому, по идее, должны служить аггрегатные функции (аггрегаты), однако там есть лишь элементарные count.
А они и не умеют, поскольку могут принимать только 1 столбец. А нам надо туда же передавать и значение для сравнения. И решение есть, неизящное, но надёжное.
Решение


 1 create or replace function inc_if(
 2     count int8,
 3     arr anyarray
 4 )
 5 returns int8 as
 6 $$
 7 declare
 8 begin
 9     if (arr[1]=arr[2]) then
10         return count+1;
11     else
12         return count;
13     end if;
14 end
15 $$
16 language 'plpgsql';
17
18 CREATE AGGREGATE count_if
19 (
20 BASETYPE=anyarray,
21 SFUNC=inc_if,
22 STYPE=int8,
23 INITCOND=0
24 );


Использование

Удобно использовать при группировке:
select count_if(ARRAY["passed_exams", 0]) from students group by id_group;
Вот вам и количество кандидатов на отчисление в каждой группе (чёрный юмор).

Недостатки
  • Накладные расходы на создание массивов. Не лечится. Аггрегаты принимают 1 столбец.

  • Необходимость создания агрегата для каждого потенциально нужного условия, негибкость. Возможно решение в виде использования динамических языков (PL/Python && PL/Ruby), передачи условия внутрь аггрегата с последующим его вычислеием (eval).

Комментариев нет: