пятница, 13 ноября 2009 г.

Логическая оптимизация - Массивы

Встроенная реализация массивов является всего лишь примером создания многомерных типов данных и мало пригодна в реальных приложениях. Напишите свою реализацию или возьмите готовую, подходящую для Ваших задач. Например, для разработчиков на pltcl может оказаться полезным используемый автором подход, при котором строка (тип text) может представлять собой тиклевский список. Создав набор функций на pltcl для работы с таким типом данных, мы получаем значительные преимущества, поскольку к строке можно применить все стандартные функции базы данных и кроме того, можно интерпретировать ее как список, обрабатывая собственными функциями. Изложенный метод имеет свои ограничения, но тем не менее хранить атрибутивную информацию таким способом и удобнее и выгоднее, чем в формате xml. Заметим, что список может иметь произвольную вложенность и скорость работы с ним не зависит от длины списка (не учитывая время на чтение с диска). Приведу набор функций, часто используемых при работе со списком, в большинстве случаев Вам их будет достаточно.

Запись в лог
select elog('log message');

CREATE OR REPLACE FUNCTION public.elog(text)
RETURNS void AS
$BODY$

elog NOTICE $1

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


Набор записей из массива
select array2items('{a,b,c}'::text[]);

CREATE OR REPLACE FUNCTION public.array2items(in_array anyarray)
RETURNS SETOF anyelement AS
$BODY$

SELECT ($1)[s] FROM generate_series(1,array_upper($1, 1)) AS s;

$BODY$
LANGUAGE 'sql' IMMUTABLE;


Список из массива
Внимание: ранее использовалась функция, но для массивов с кучей различных кавычек и скобочек происходили сбои, так что пришлось сделать идеологически правильным, но менее удобным для использования способом. Агрегатные функции см. ниже.

select list_accum(array2items) from array2items('{a,b,c}'::text[]);

Массив из списка
select list2array('a b c');
select * from array2items((select list2array('c v { a b}'))::text[]);

CREATE OR REPLACE FUNCTION public.list2array(list text)
RETURNS text[] AS
$BODY$

if {$1 eq ""} {return "{}"}
return [concat "{" [join $1 ", "] "}"]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


Удаление элемента из списка
select ldelete('1 2 3 4 5','5');

CREATE OR REPLACE FUNCTION public.ldelete(list text, value text)
RETURNS text AS
$BODY$

set ix [lsearch -exact $1 $2]
if {$ix >= 0} {
return [lreplace $1 $ix $ix]
} else {
return $1
}

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


Замена элемента в списке
select lreplace('1 2 3 4 5','5', '7');

CREATE OR REPLACE FUNCTION public.lreplace(list text, old_value text, new_value text)
RETURNS text AS
$BODY$

set ix [lsearch -exact $1 $2]
if {$ix >= 0} {
return [lreplace $1 $ix $ix $3]
} else {
return $1
}

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


Добавление элемента в список
select lappend('1 2 3 4 5','10');

CREATE OR REPLACE FUNCTION public.lappend(list text, value text)
RETURNS text AS
$BODY$

return [lappend 1 $2]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


Объединение списков
select lconcat('a b c', '1 2 3');

CREATE OR REPLACE FUNCTION public.lconcat(list1 text, list2 text)
RETURNS text AS
$BODY$

return [concat $1 $2]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


Сортировка списка
select lsort('cd 1 99 2 100 ab bc');

CREATE OR REPLACE FUNCTION public.lsort(list text)
RETURNS text AS
$BODY$

return [lsort -dictionary $1]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


Выбор уникальных значений из списка
Возвращаемые значения также отсортированы аналогично lsort
select lunique('cd 1 99 2 100 ab bc 1 2');

CREATE OR REPLACE FUNCTION public.lunique(list text)
RETURNS text AS
$BODY$

return [lsort -dictionary -unique $1]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


1-й элемент списка
select lfirst('1 2 3 4 5 6 7 8 9 10');

CREATE OR REPLACE FUNCTION public.lfirst(list text)
RETURNS text AS
$BODY$

return [lindex $1 0]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


2-й элемент списка
select lsecond('1 2 3 4 5 6 7 8 9 10');

CREATE OR REPLACE FUNCTION public.lsecond(list text)
RETURNS text AS
$BODY$

return [lindex $1 1]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


i-й элемент списка
Внимание: нумерация элементов с нуля!
select lindex('5 10 15', 2);

CREATE OR REPLACE FUNCTION public.lindex(list text, index int4)
RETURNS text AS
$BODY$

return [lindex $1 $2]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


Длина списка
select llength('1 2 3 4 5');

CREATE OR REPLACE FUNCTION public.llength(list text)
RETURNS int4 AS
$BODY$

return [llength $1]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


Набор записей из списка
select list2items('a b c');

CREATE OR REPLACE FUNCTION public.list2items(in_list text)
RETURNS SETOF text AS
$BODY$

SELECT lindex($1, s) FROM generate_series(0,llength($1)-1) AS s;

$BODY$
LANGUAGE 'sql' IMMUTABLE;


Агрегаты
select list_accum(id) from merch.answers;

CREATE AGGREGATE public.list_accum(
BASETYPE=text,
SFUNC=lappend,
STYPE=text,
INITCOND=''
);

CREATE AGGREGATE public.list_accum_list(
BASETYPE=text,
SFUNC=lconcat,
STYPE=text,
INITCOND=''
);


Обратите внимание, что мы использовали только стандартные тиклевские функции. А если еще загрузить в PostgreSQL вот такие функции (см. pltcl_loadmod, кстати, там есть баг - этот скрипт все функции от суперпользователя создает, что приводит к неприятным последствиям в крупных системах, так что если кого заинтересует, выложу пропатченную версию):


proc lfilter {list script} {
set res {}
foreach e $list {if {[uplevel 1 $script $e]} {lappend res $e}}
set res
}
proc lin {list e} {expr {[lsearch -exact $list $e]>=0}}
proc lintersection {set1 set2} {lfilter $set1 {lin $set2}}

proc joinquote {list {joinstring { }}} {
foreach x $list {
lappend list2 "'[quote $x]'"
}
return [join $list2 $joinstring]
}


можно расширить наш набор следующими pltcl функциями:

Пересечение списков
select lintersection('a b c d e g', 'a d f g');

CREATE OR REPLACE FUNCTION public.lintersection(lista text, listb text)
RETURNS text AS
$BODY$

return [lintersection $1 $2]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;


Проверка существования элемента в списке
select lin('a b c', 'd');

CREATE OR REPLACE FUNCTION public.lin(list text, item text)
RETURNS bool AS
$BODY$

return [lin $1 $2]

$BODY$
LANGUAGE 'pltcl' IMMUTABLE;

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


(C) Alexey Pechnikov aka MBG, mobigroup.ru