C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
User defined function, заморочка...
Shagg
  Отправлено: 08.11.2006, 07:25


Дежурный стрелочник

Группа: Участник
Сообщений: 69



Всем привет! Появилась необходимость написать функцию на сервере, которая выполняла бы передаваемый ей запрос и возвращала бы это все одной строкой. Т.е. формат функции приблизительно такой:
CODE
GetRowSetAsString(@table, @col, @condition);

где
@table = название таблицы из которой вытаскивать
@col = колонки
@condition = некоторое условие.
Функция строит запрос типа
CODE
'select '+@col+' from '+@table+' where '+@condition

собирает все в строку и возвращает.
Пробовал через курсоры, но на объявление типа
CODE
declare cur cursor for 'select '+@col+' from '+@table+' where '+@condition
выдает "Incorrect syntax"
Как сделать? Подскажите или намекните.
Заранее прошу прощения если вопрос ламерский, но с БД я не очень близко дружу
Shagg
Отправлено: 08.11.2006, 07:26


Дежурный стрелочник

Группа: Участник
Сообщений: 69



Да забыл. Сервер — MSSQL 2000.
olegenty
Отправлено: 08.11.2006, 08:13


Ветеран

Группа: Модератор
Сообщений: 2412



1. помещаешь результат DSQL во временную таблицу.
2. пробегаешь курсором по временной таблице и генерируешь выходную строку.
Shagg
Отправлено: 08.11.2006, 09:46


Дежурный стрелочник

Группа: Участник
Сообщений: 69



А как нить по другому? Так долго получается, мне эту функцию часто нужно будет вызывать.
AVC
Отправлено: 08.11.2006, 10:10


Ветеран

Группа: Модератор
Сообщений: 1583



QUOTE (Shagg @ 08.11.2006, 07:25)
Появилась необходимость написать функцию которая выполняла бы передаваемый ей запрос и возвращала бы это все одной строкой ... Функция строит запрос типа
'select '+@col+' from '+@table+' where '+@condition
собирает все в строку и возвращает.

Не понял, что за строку должна возвращать функция? Конкатенация (может с разделителями) всех значений одной колонки выборки ?
olegenty
Отправлено: 08.11.2006, 10:26


Ветеран

Группа: Модератор
Сообщений: 2412



по-другому никак, без перебора записей столбец в строку ты не поместишь. вопрос только в том, сделаешь ты это на стороне сервера, или на стороне клиента. а вот это как раз стоит обдумать.
Shagg
Отправлено: 08.11.2006, 11:29


Дежурный стрелочник

Группа: Участник
Сообщений: 69



Извините.Наверно я не так описал проблему.
Результирующая строка формируется конкатенацией всех значений столбца @col через запятую (например). Выполняться естессно будет на сервере (USER DEFINED FUNCTION). А перебор на клиенте типа
CODE
for (ADOQuery1->First(); !ADOQuery1->Eof; ADOQuery1->Next()) s += ADOQuery->FieldByName("ffield")->AsString+ ',';
не подходит, так как мне нужно обрабатывать запросы типа:
CODE
select ('table2', 'col1', 'id in('+idlist+')') from table1
А писать вложенные циклы с запросами так не охота.

В принципе, задача сводится к тому, чтобы открыть курсор для запроса, передаваемого строкой. Некое подобие функции EXEC.
AVC
Отправлено: 08.11.2006, 12:17


Ветеран

Группа: Модератор
Сообщений: 1583



Да, курсор правильное решение если, конечно, нет готовой функции (например у sybase asa она называется List). Осталось только разобраться с синтаксисом MS Sql, но тут не моя епархия а, напимер, olegenty. Если он говорит курсоров нет — значит нет.
На всяк случай привожу решение для Oracl'a (8). Может что полезное подчерпнете.
CODE

CREATE OR REPLACE Function HHC.ToList
(pSqlText  in varchar2
,pSep      in varchar2 default Null
,pPref     in varchar2 default Null
,pPost     in varchar2 default Null
,pBaseText in varchar2 default Null
)
return varchar2
As
Type  TAnyCursor Is REF CURSOR;
tcur  TAnyCursor;
vSep  varchar2(  100) := Trim(NVL(pSep,','));
vPref varchar2(  100) := Trim(pPref);
vPost varchar2(  100) := Trim(pPost);

vtext varchar2(32700) := pBaseText;
vVal  varchar2( 4200);
vbylo boolean;
Begin

Open tcur For pSqlText;

vbylo := false;
Begin
Loop
 Fetch tcur Into vVal;
 Exit when not tcur%FOUND;
 if (vbylo) then vtext := vtext || vSep; end if;
 vbylo := true;
 vtext  := vtext || vPref || vval || vPost;
end Loop;
Exception when OTHERS then vtext := Null;
End;

Close tcur;

return vtext;
End;
/
olegenty
Отправлено: 08.11.2006, 12:29


Ветеран

Группа: Модератор
Сообщений: 2412



я как раз говорю — с курсором. но через временную таблицу.

например:
CODE

create table #List(Line varchar(200));

declare @DSQL varchar(8000)

set @DSQL = 'insert into #List select ' + @column + ' from table ' + @table + ' where ' + @conditions

exec(@DSQL)

declare @Line varchar(200)
declure @Result varchar(8000)
set @Result = ''

declare C cursor local fast_forward for select Line from #List

open C
fetch next from C into @Line
while (@@fetch_status = 0) begin
   @Result = @Result + ','  + @Line
   fetch next from C into @Line
end
close C
deallocate ActionC

-- тут удали первую запятую

return @Result
olegenty
Отправлено: 08.11.2006, 12:33


Ветеран

Группа: Модератор
Сообщений: 2412



только хрен там, временные таблицы нельзя использовать в теле функции, можно только табличные переменные. но табличная переменная не будет видна из DSQL. курсор для DSQL выражения не откроешь, скажет fuck you...

щас ещё подумаю...
olegenty
Отправлено: 08.11.2006, 12:42


Ветеран

Группа: Модератор
Сообщений: 2412



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

Теперь о самой постановке. если тебе это надо для in, то тебе вообще не нужна такая функция, ты можешь так и написать:

where some_field in (select some_other_field from some_other_table where conditions)

и последнее — динамическое in надо использовать так редко, как только это возможно, поскольку нарвёшься если не на table scan в плане выполнения, то на фетч всех записей по условию подзапроса в in, а реально нужна только одна запись — достаточно знать, есть ли ХОТЬ ОДНА удовлетворяющая условию запись. а для этого надо использовать where exists и where not exists... это азы...
Shagg
Отправлено: 10.11.2006, 07:15


Дежурный стрелочник

Группа: Участник
Сообщений: 69



мда. Пробдема в том, что in вернет несколько строк. А мне то вся эта барахляндия нужна в одной ячейке... Ладно буду мутить на клиенте, благо возможности С++ неисчерпаемы. Спасибо всем за помощь.

Вернуться в Работа с базами данных в C++Builder