Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Добавление/расширение record'a новыми записями / 10 сообщений из 10, страница 1 из 1
02.08.2007, 14:43
    #34701391
Mayc
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление/расширение record'a новыми записями
Пишу функцию, которая возвращает setof record.
Требуется в цикле составить этот самый record, но каждый элемент записи - это отдельная строка, выбираемая из таблицы.
CREATE TABLE foo(field int);
INSERT VALUES INTO foo (1);
INSERT VALUES INTO foo (2);
INSERT VALUES INTO foo (3);
...
INSERT VALUES INTO foo (20);

В теле функции идёт селект, подобный этому:
DECLARE
fields RECORD;
result RECORD;
...
FOR fields IN SELECT * FROM foo WHERE field < 4;
LOOP
а здесь нужно в result добавлять fields.field
END LOOP;
И в результате нужно получить запись (1,2,3) .
В самой функции используется именно return next result;. Известно заранее, что кол-во возвращаемых строк в селекте всегда одинаково (в реальности это контроллируется left join'ом). Но сама функция, взависимости от входного параметра, может возвращать различное кол-во полей:
SELECT * from my_func(3) AS (f1 int, f2 text, f3 int);
SELECT * from my_func(2) AS (f1 int, f2 int);

Делается это с целью уйти от динамического sql (execute). Я пытался пошариться в гугле и тут на форуме, но не смог найти способа преобразования массива в row или конкатенации рекорда.

Спасибо.
...
Рейтинг: 0 / 0
02.08.2007, 14:53
    #34701427
Serik Akhmetov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление/расширение record'a новыми записями
поищите по слову refcursor
...
Рейтинг: 0 / 0
02.08.2007, 16:37
    #34701943
Mayc
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление/расширение record'a новыми записями
Serik Akhmetovпоищите по слову refcursor
Что-то у меня получаются те же туфли, только шнурками вниз.
DECLARE
rc REFCURSOR:
val RECORD;
OPEN rc FOR SELECT field FROM foo WHERE field < 4;
Если я делаю FETCH rc INTO val, то получаю одно значение, если сделаю loop, то вернусь к схеме с обычным FOR val IN SELECT .... LOOP ... END LOOP;
FETCH ALL вне функции возвращает просто все записи в столбик. А мне нужно вернуть строчки вида:
(1,2,3,4), а не по четыре строки вместо одной.
Вот на это я не смог найти ответа.
...
Рейтинг: 0 / 0
02.08.2007, 17:24
    #34702170
Mayc
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление/расширение record'a новыми записями
Mayc...
А мне нужно вернуть строчки вида:
(1,2,3,4), а не по четыре строки вместо одной.

Имеется ввиду, что нужно возвращать рекорд из четырёх полей. А в общем случае, n-ое количество. На данный момент приходится выполнять динамический SQL. Чего очень не хочется.
...
Рейтинг: 0 / 0
05.08.2007, 20:04
    #34706426
Mayc
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление/расширение record'a новыми записями
В общем, сотворил я агрегатную функцию:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
CREATE OR REPLACE FUNCTION public.catwsep_fs(text, text, varchar)
    RETURNS text
    SECURITY DEFINER LANGUAGE 'plpgsql'
    AS $$
    DECLARE
        x text;
    BEGIN
        IF $ 1  = '' THEN
            x := $ 2 ;
        ELSE
            x := $ 1  || $ 3  || $ 2 ;
        END IF;
        RETURN x;
    END;
    $$;

DROP AGGREGATE IF EXISTS public.catwsep(text, varchar);
CREATE AGGREGATE public.catwsep(text, varchar) (
    sfunc = public.catwsep_fs,
    stype = text,
    initcond = '');
И заюзал так:
Код: plaintext
1.
2.
3.
4.
5.
6.
select ci.prod_id,public.catwsep(ch.value, ' ') from products.class_instances ci, products.class_fields f, products.chars_vt ch where f.class_id= 1  and f.field_id=ci.field_id and ci.value_id=ch.value_id group by ci.prod_id order by prod_id limit  10 ;
 prod_id |                                                     catwsep                                                      
---------+------------------------------------------------------------------------------------------------------------------
        1  |  6  Str_0000008  0 . 666666666666667   9  Str_0000002  6 . 66666666666667   3   4 . 66666666666667  Str_0000005  2 . 66666666666667 
        2  | Str_0000014  10 . 6666666666667   6 . 66666666666667  Str_0000020  12  Str_0000008  6  Str_0000002  18   2 . 66666666666667 
...
Ибо клиент - это ПыхПых и он способен распарсить строчки сам через split. Собственно, можно было и не писать свою функцию catwsep_fs, а воспользоваться готовой textcat, но всё упёрлось в то, что textcat'у нельзя передать сепаратор. А конкатенация поля с сепаратором была нежелательна.
...
Рейтинг: 0 / 0
07.08.2007, 01:33
    #34709201
iz
iz
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление/расширение record'a новыми записями
Mayc
Ибо клиент - это ПыхПых и он способен распарсить строчки сам через split

ой
вы же сами знаете, что это bad... что прямо-таки никак нельзя уйти от record-а произвольной ширины? misdesign какой-то, поскольку в SQL у таблицы всегда должна быть определенная ширина и record соответственно тоже хорошо иметь какого-то определенного типа, связанного с конкретной таблицей. иначе же нужно как-то пытаться сделать через массивы, но уже понятно что такое решение тоже не будет простым и красивым.
...
Рейтинг: 0 / 0
07.08.2007, 11:27
    #34709931
4321
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление/расширение record'a новыми записями
этот вопрос тут всплывает с периодичностью 2-3 раза в месяц. понятно, что поиском пользоваться лома. но краткое содержание предыдущих серий:
1. основной тезис - пользоваться каким либо иным языком для хранимки (plperl)
2. финт ушами для размещения в рекорд предлагаю примерно такой (найдете его в поиске для специфических вариантов рекорда именуемых NEW /OLD) :
Код: plaintext
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.
CREATE OR REPLACE FUNCTION test_record()
RETURNS record
 AS $$

--В теле функции идёт селект, подобный этому:
DECLARE
fields RECORD;
result RECORD;
ssql text;
i int8 ;
--...
BEGIN
	--result. добавлять fields.field
	--fint ушами, хорошоб, еслиб TEMPORARY всегда размещались в память
	i:= 0 ;
	ssql = 'CREATE TEMPORARY TABLE one_record AS SELECT 0 AS f0';
	FOR fields IN SELECT * FROM foo WHERE field <  4 
	LOOP
		i := i+ 1 ;
		ssql := ssql ||',' || fields.field || ' AS f' ||i;
	
	END LOOP;
	EXECUTE ssql;
	FOR result IN EXECUTE 'SELECT * FROM one_record;' LOOP
	end LOOP;
	EXECUTE 'DROP /*TEMPORARY*/ TABLE one_record;';
	RETURN result ;
END;
$$
SECURITY DEFINER LANGUAGE 'plpgsql';

-- SELECT test_record();
- это общий метод обход размещения произвольной бяки в записи

, хотя именно в вашем случае -
есть идея поманьячить на тему запихивания полей в конструкцию
Код: plaintext
FOR result IN EXECUTE 'SELECT ROW(0,1,2,3)'
- т.е. без создания темповой таблы, должно получицо

но вот так не получицо:
Код: plaintext
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.
CREATE OR REPLACE FUNCTION test_record()
RETURNS record
 AS $$

--В теле функции идёт селект, подобный этому:
DECLARE
fields RECORD;
result RECORD;
ssql text;
i int8 ;
--...
BEGIN
	--result. добавлять fields.field
	i:= 0 ;
	--ssql = 'CREATE TEMPORARY TABLE one_record AS SELECT 0 AS f0';
	ssql = 'SELECT ROW (0';-- AS f0';
	FOR fields IN SELECT * FROM foo WHERE field < 4
	LOOP
		--i := i+1;
		ssql := ssql ||',' || fields.field ; -- '|| AS f' ||i;	
	END LOOP;
	ssql = ssql || ');';
	EXECUTE ssql;
	--FOR result IN EXECUTE 'SELECT * FROM one_record;' LOOP
	FOR result IN EXECUTE ssql LOOP		
	end LOOP;
	
	--EXECUTE 'DROP /*TEMPORARY*/ TABLE one_record;';
	RETURN result;
END;
$$
SECURITY DEFINER LANGUAGE 'plpgsql';

-- SELECT test_record();
- запись вернется не то строкой, не то массивом,
а вот так - похоже на правду
Код: plaintext
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.
CREATE OR REPLACE FUNCTION test_record()
RETURNS record
 AS $$

--В теле функции идёт селект, подобный этому:
DECLARE
fields RECORD;
result RECORD;
result1 RECORD;
ssql text;
i int8 ;
--...
BEGIN
	--result. добавлять fields.field
	--fint ушами, хорошоб, еслиб TEMPORARY всегда размещались в память
	i:= 0 ;
	--ssql = 'CREATE TEMPORARY TABLE one_record AS SELECT 0 AS f0';
	ssql = 'SELECT ROW (0';-- AS f0';
	FOR fields IN SELECT * FROM foo WHERE field < 4
	LOOP
		--i := i+1;
		ssql := ssql ||',' || fields.field ; -- '|| AS f' ||i;	
	END LOOP;
	ssql = ssql || ') AS a;';
	EXECUTE ssql;
	--FOR result IN EXECUTE 'SELECT * FROM one_record;' LOOP
	FOR result IN EXECUTE ssql LOOP
		result1:= result.a;
	end LOOP;
	
	--EXECUTE 'DROP /*TEMPORARY*/ TABLE one_record;';
	RETURN result1;
END;
$$
SECURITY DEFINER LANGUAGE 'plpgsql';

-- SELECT test_record();
...
Рейтинг: 0 / 0
08.08.2007, 20:23
    #34715306
Mayc
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление/расширение record'a новыми записями
iz Mayc
Ибо клиент - это ПыхПых и он способен распарсить строчки сам через split

ой
вы же сами знаете, что это bad... что прямо-таки никак нельзя уйти от record-а произвольной ширины? misdesign какой-то, поскольку в SQL у таблицы всегда должна быть определенная ширина и record соответственно тоже хорошо иметь какого-то определенного типа, связанного с конкретной таблицей. иначе же нужно как-то пытаться сделать через массивы, но уже понятно что такое решение тоже не будет простым и красивым.
Эмм.. У меня структура a-la Модель Тенцера. Вы видели на форуме лучший способ по вытаскиванию всех параметров за раз? Без использования Execute? Другой вариант, что может и не понадобится вовсе для всех сущностей определённого класса за раз выбирать его характеристики.
...
Рейтинг: 0 / 0
08.08.2007, 20:36
    #34715318
Mayc
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление/расширение record'a новыми записями
4321этот вопрос тут всплывает с периодичностью 2-3 раза в месяц. понятно, что поиском пользоваться лома. но краткое содержание предыдущих серий:
1. основной тезис - пользоваться каким либо иным языком для хранимки (plperl)
2. финт ушами для размещения в рекорд предлагаю примерно такой (найдете его в поиске для специфических вариантов рекорда именуемых NEW /OLD) :
Код: plaintext
1.
... EXECUTE ...

Во-первых, я прекрасно умею пользоваться поиском. И уже находил рецепт с plperl. Позвольте, вы мерили скорость выполнения таких хранимок? Во-вторых, я чётко обозначил, что пытаюсь уйти от динамического SQL, т.е. никаких EXECUTE. Если совместить второе и первое, то ни первый, ни второй ваш совет не подходит под постановку задачи. Посему, прежде чем тыкать носом в поисковик (что для меня обидно), сначала вникните в тему вопроса. А не кидайтесь по первым двум-трём моим словам, что это уже все пытались делать и общее решение такое. Кстати, через Execute я уже делал - это слишком банальный выход. А так, зайдите в раздел "Проектирование БД" и ищите по словам: EAV , Модель Тенцера . Вот мне нужно нечто подобное, но без использования Execute и уж точно без всяких там plperl/ruby/... Пока схожусь во мнении, что нужно менять немного логику того, что будет тащить данные из базы. Благо, есть, где разгуляться.
...
Рейтинг: 0 / 0
09.08.2007, 17:10
    #34717857
4321
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление/расширение record'a новыми записями
MaycВо-первых, я прекрасно умею пользоваться поиском. И уже находил рецепт с plperl. Позвольте, вы мерили скорость выполнения таких хранимок? Во-вторых, я чётко обозначил, что пытаюсь уйти от динамического SQL, т.е. никаких EXECUTE. Если совместить второе и первое, то ни первый, ни второй ваш совет не подходит под постановку задачи. Посему, прежде чем тыкать носом в поисковик (что для меня обидно), сначала вникните в тему вопроса. А не кидайтесь по первым двум-трём моим словам, что это уже все пытались делать и общее решение такое. Кстати, через Execute я уже делал - это слишком банальный выход. А так, зайдите в раздел "Проектирование БД" и ищите по словам: EAV , Модель Тенцера . Вот мне нужно нечто подобное, но без использования Execute и уж точно без всяких там plperl/ruby/... Пока схожусь во мнении, что нужно менять немного логику того, что будет тащить данные из базы. Благо, есть, где разгуляться.1. гм. и чем вам не нравится Execute? Имхо, мутный вы какой-то.
2. под динамич. SQL что только не подпадает (в том числе и пошив скл на клиенте и т.п.), т.ч. я просил бы в следующий раз пояснее выражать свою мысль.
3. просил бы идти с вашими пожеланиями вникания фтему вопроса далеко и надолго. преимущественно - лесом. Да и не забывать: на обиженных - воду возят.
4. с еавом и тенцером - идите в проектирование. к гуесту_ххх - он вас там просветит. потом догонит и еще раз просветит (желательно - чтобы до гланд проняло). И будет прав, что интересно. и поиск мне в этом случае не нужен - не мою задачу решаем, что симптоматично.
5. зы: последний предложенный мной скрипт без создание времянок должен неплохо выигрывать по времени (нет дисковых промежуточных). но это если вы до него дошли, пусть и затуманившись слёзками абидок.

успехов.
...
Рейтинг: 0 / 0
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Добавление/расширение record'a новыми записями / 10 сообщений из 10, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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