Russian Qt Forum
Ноябрь 23, 2024, 03:12 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 ... 4 5 [6] 7 8 ... 14   Вниз
  Печать  
Автор Тема: Транзакции Interbase  (Прочитано 150566 раз)
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #75 : Март 19, 2010, 21:48 »

Цитировать
Так можно делать только если id - простое число, если же этот id формируэтся но некоему алгоритму:
AABBBBCCCC, где АА - номер отдела, BBBB - тип документа, СССС - собственно инкремент
то такой алгоритм не подойдет

То что вы описали не является СУРРОГАТНЫМ ключем - для того чтобы не было головняка при работе с таблицей нужен именно суррогатный ключ!!! Это многие опытные разработчики в своих книгах описывают. То есть если есть серия паспорта - то это не суррогатный ключ и его мы как PK не берем - и пусть формируется по какому угодно алгоритму. А суррогатный пусть формирует БД!

Я для себя завел строгое правило - все таблицы имеют такой PK - как правило поле называется ROW_ID имеет триггер как раз описанный выше. Это также помогает написать универсальное окошко  "справочник" которое при удалении - изменении - создание записей дергает этот ROW_ID.

Теперь на клиенте дергаю генератор через

SELECT GEN_ID( <GeneratorName>, 1 ) FROM RDB$DATABASE;
Цитировать

при этом у меня в каждой таблице триггер есть но он не отрабатывает т.к. действительно нам нужен ID записи на клиенте - а если его создаст сервер триггером то мы его не получим до первого селекта (а переоткрывать запрос нехотелось бы), но он нужен для тех случаев когда запись будет вставлять не справочник - а например когда будет пакетный импорт из снешнего источника и ID как раз нужно будет сгенерить...
Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #76 : Март 19, 2010, 22:15 »

>>А другого пути просто нету. Триггер - это несомненно хорошо, но он не отдаст полученный id, как вы его узнаете?
есть такой концепт - авто инкремент, класcика для Firebird - это связка Генератор + Тригер.
Выглядит так:
Код
SQL
/* создаём таблицу */
CREATE TABLE ENTITIES
(
 ID Integer NOT NULL,
 PARENT_ID Integer,
 SINGULAR_NAME D_NAME NOT NULL,
 PLURAL_NAME D_NAME,
 CONSTRAINT PK_ENTITIES PRIMARY KEY (ID)
);
/* создаём для неё генератор искусственного ПК */
CREATE GENERATOR GEN_ENTITIES;
 
/* Создаем тригер для вставки в таблицу искусственного ПК */
SET TERM ^ ;
CREATE TRIGGER T_ENTITIES_ID FOR ENTITIES ACTIVE
BEFORE INSERT POSITION 0
AS
BEGIN
 
IF (NEW.ID IS NULL) THEN
NEW.ID = GEN_ID(GEN_ENTITIES, 1);
END^
SET TERM ; ^
SET TERM ^ ;
 
Далее в таблицу вставляются значения, но ПК при вставке не используется, им полностью занимается тригер
Записан

Юра.
sarbash
Гость
« Ответ #77 : Март 19, 2010, 22:23 »

Вы наверное меня недопоняли немного... Улыбающийся
Я говорил о случае, когда клиенту нужно знать pk только что вставленной записи. Триггер её вам не отдаст.
А вот такая штука, как "RETURNING <column_list> [INTO <variable_list>]" в Firebird 2.1 - это уже интересно... Сейчас и проверю.
В общем, всех интересующихся отсылаю к "Firebird_v2.1.3.ReleaseNotes.pdf" Улыбающийся
Записан
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #78 : Март 19, 2010, 22:58 »

Я действительно значит недопонял

1) нужно значение только что вставленной записи на клиенте - без переоткрытия набора - делаем SELECT GEN_ID( <GeneratorName>, 1 ) FROM RDB$DATABASE и вставляем уже с этим полученным увеличенным на 1 новым значением генератора - при этом мы его знаем
2) не нужен - просто не указываем в полях для вставки ROW_ID и вставится базой

мне кажется механизм автоинкремента через генератор + before_insert триггер нужен обязательно в БД - создаю его для всех таблиц

Так получается

MY_Table ( таблица с обязательным полем ROW_ID - суррогатным)

MY_TABLE_GEN ( генератор для новых значений ROW_ID )

MY_TABLE_BI ( стандартный триггер Before Insert для вставки нововго значения генератора MY_TABLE_GEN в ROW_ID если текущее NULL)
Записан
sarbash
Гость
« Ответ #79 : Март 19, 2010, 23:25 »

Всё это мне было ясно сразу, как Божий день. Улыбающийся
Так вот, вскрытие показало, что пациент умер от вскрытия... Firebird-овский returning оказался useless на данном этапе его реализации. Запрос отрабатывается, но вытащить значение я не смог, т.к. там ограничение на into :variable, into работает только для psql, а значит, меня жестко побрили.

Итак, я вижу два способа решения проблемы pk на клиенте:
1) вставка через процедуру, которая и будет возвращать нам значение ПК только что вставленной записи.
2) дергать селектом генератор и использовать полученное значение.

Собственно, второй способ у меня и был реализован. Итак, похоже, тема выглядит исчерпанной. Выглядит так, что QSqlQuery ничего не может сделать с "RETURNING", либо это от драйвера зависит и в нём надо допиливать, или это ограничение самой квэри, ну тут уж я не силён, я с Qt совсем недавно...
Записан
MoPDoBoPoT
Гость
« Ответ #80 : Март 20, 2010, 00:53 »

Выглядит так, что QSqlQuery ничего не может сделать с "RETURNING", либо это от драйвера зависит и в нём надо допиливать, или это ограничение самой квэри, ну тут уж я не силён, я с Qt совсем недавно...
Не знаю как в Firebird, но в Oracle конструкция "INSERT INTO <table_name> (column_list) VALUES (values_list) RETURNING <value_name> INTO <variable_name" работает. Если драйвер поддерживает связываемые переменные (подготовленный запрос), то должно работать.
Записан
sarbash
Гость
« Ответ #81 : Март 20, 2010, 10:22 »

Что ж, за Oracle можно порадоваться... Ну а мы пока имеем:

1. The INTO part (i.e. the variable list) is allowed in PSQL only, for assigning the output set to local variables.
It is rejected in DSQL.
2. The presence of the RETURNING clause causes an INSERT statement to be described by the API as
isc_info_sql_stmt_exec_procedure rather than isc_info_sql_stmt_insert. Existing connectivity
drivers should already be capable of supporting this feature without special alterations.

2axax: твой драйвер "supporting this feature"? Если нет, очень хотелось бы увидеть реализацию. Готов помочь, чем могу. А может, это надо допиливать ibpp? А с другой стороны думаю - если "It is rejected in DSQL", то как вообще получить возвращаемое значение, если нельзя забиндить на переменную? Тупик какой-то...
« Последнее редактирование: Март 20, 2010, 10:49 от sarbash » Записан
Tonal
Гость
« Ответ #82 : Март 21, 2010, 16:21 »

RETURNING оч. правильный вариант, но его должен поддерживать драйвер.
Если используешь qtfirebirdibppsqldriver можно автора попросить допилить. Улыбающийся
Записан
xokc
Птица говорун
*****
Offline Offline

Сообщений: 976



Просмотр профиля
« Ответ #83 : Март 21, 2010, 19:44 »

Если используешь qtfirebirdibppsqldriver можно автора попросить допилить. Улыбающийся
Так об этом тут последние постов 20 и просят Улыбающийся
Записан
sarbash
Гость
« Ответ #84 : Март 22, 2010, 23:33 »

Попросить автора не сложно, тут загвоздка в другом - в "returning" невозможно забиндить переменные. Получается, драйвер должен распознать такой запрос и обрабатывать его каким-то особым образом, плюс, насколько я могу это представить, биндинг всё-таки должен быть доступен, но драйвер должен уметь транслировать запрос в форму, доступную для dsql, суметь получить возвращённые значения и перетранслировать всё обратно так, чтобы эмулированный биндинг работал. То есть, как я вижу, драйверу придётся эмулировать биндинг.
Ну, может, всё намного проще, и я просто чего-то не знаю или недопонимаю, но мои эксперименты с "returning" меня привели к нулевому результату. С "into" я получаю неподготовленный запрос, без "into" запрос исполняется, но я ничего не получаю обратно, ведь биндинга нету, логично? Вот такие пироги...

P.S. ...Так что пока просто продолжаю дёргать генератор селектом.
« Последнее редактирование: Март 22, 2010, 23:35 от sarbash » Записан
Tonal
Гость
« Ответ #85 : Март 23, 2010, 08:35 »

Вызов INSERT ... RETURNING эквивалентен вызову процедуры.
Собственно, насколько я в курсе, после prepare сервер так флаги и устанавливает.
Так что никаких сильно мудрёных и уникальных телодвижений тут не нужно.
Вопрос только в том, поддерживает это ibpp или нет.
Если нет, придётся его авторов пинать.

П. С. В драйвере к Python это реализовали сразу, как только RETURNING появился. Улыбающийся
Записан
sarbash
Гость
« Ответ #86 : Март 23, 2010, 23:23 »

Вот, я тут написал в мэйл-лист ibpp:

Le 23 mars 2010 à 07:13, Сергей Сарбаш a écrit :
> Hello, All.
>
> I suppose you know about this new feature of the Firebird 2.1 server.
> So, just the question. Has anyone already tried to implement this?
> Thank you.

There is nothing to implement in IBPP to use this nice feature.  I use it everyday.  The server returns the "returning" value just as it would do for a stored procedure returning a single value.  So just Statement->Get(...) after your Execute() and you're done.

The code sample by Borr says it all, too.

Enjoy Улыбающийся
--
Olivier Mascia

P.S. Как видно из текста, ibpp поддерживает returning. Осталось подкрутить драйвер...
Записан
gigabyte
Гость
« Ответ #87 : Март 24, 2010, 16:22 »

Посмотрел я сегодня IBPP.
returning он НЕ ПОДДЕРЖИВАЕТ!!!!!
Записан
gigabyte
Гость
« Ответ #88 : Март 24, 2010, 16:44 »

Значит так, чтобы можно было выполнить такой  запрос:
Код:
INSERT INTO SYS$ACTIONS(ID,CAPTION) VALUES(NULL,'CAPTION') RETURNING ID
и при этом обработать возврат:
Код:
        while(q.next())
            qDebug() << "Value: " << q.value(0);
Я сделал следующее:
Файл В qsqlfirebird qsql_ibpp.cpp:
Код:
bool QFBResultPrivate::isSelect()
{
    bool iss = false;
    try
    {
        iss = (iSt->Type() == IBPP::stSelect) || ((iSt->Type() == IBPP::stExecProcedure) && (iSt->Columns()>0));
    }
    catch (IBPP::Exception& e)
    {
        setError("Unable get type of statement", e, QSqlError::StatementError);
    }
    return iss;
}
Файлы в IBPP 2.5.3.1:
файл _ibpp.h в клаcсе StatementImpl добавить поля:
Код:
        bool mIsSingleResult;
        bool mIsSingleResultDone;
Файл Statement.cpp изменить метод Fetch() вот на это:
Код:
bool StatementImpl::Fetch()
{
        if((mIsSingleResult) && (!mIsSingleResultDone)) {
            IBS status;
            int code = (*gds.Call()->m_dsql_fetch)(status.Self(), &mHandle, 1, mOutRow->Self());
            if (status.Errors())
            {
                    Close();
                    throw SQLExceptionImpl(status, "Statement::Fetch",
                            _("isc_dsql_fetch failed."));
            }
            mIsSingleResultDone = true;
            return true;
        }
        else if ((mIsSingleResultDone) && (mIsSingleResult)) {
            CursorFree();
            mIsSingleResult = false;
            mIsSingleResultDone = false;
            CursorFree();
            return false;
        }

        if (! mResultSetAvailable)
throw LogicExceptionImpl("Statement::Fetch",
_("No statement has been executed or no result set available."));

IBS status;
int code = (*gds.Call()->m_dsql_fetch)(status.Self(), &mHandle, 1, mOutRow->Self());
if (code == 100) // This special code means "no more rows"
{
mResultSetAvailable = false;
// Oddly enough, fetching rows up to the last one seems to open
// an 'implicit' cursor that needs to be closed.
mCursorOpened = true;
CursorFree(); // Free the explicit or implicit cursor/result-set
return false;
}
if (status.Errors())
{
Close();
throw SQLExceptionImpl(status, "Statement::Fetch",
_("isc_dsql_fetch failed."));
}
return true;
}
Файл Statement.cpp изменить метод Execute() вот на это:
Код:
void StatementImpl::Execute(const std::string& sql)
{
if (! sql.empty()) Prepare(sql);

if (mHandle == 0)
throw LogicExceptionImpl("Statement::Execute",
_("No statement has been prepared."));

// Check that a value has been set for each input parameter
if (mInRow != 0 && mInRow->MissingValues())
throw LogicExceptionImpl("Statement::Execute",
_("All parameters must be specified."));

CursorFree(); // Free a previous 'cursor' if any

IBS status;
        if (mType == IBPP::stSelect)
{
// Could return a result set (none, single or multi rows)
(*gds.Call()->m_dsql_execute)(status.Self(), mTransaction->GetHandlePtr(),
&mHandle, 1, mInRow == 0 ? 0 : mInRow->Self());
if (status.Errors())
{
//Close(); Commented because Execute error should not free the statement
std::string context = "Statement::Execute( ";
context.append(mSql).append(" )");
throw SQLExceptionImpl(status, context.c_str(),
_("isc_dsql_execute failed"));
}
if (mOutRow != 0)
{
mResultSetAvailable = true;
mCursorOpened = true;
                        mIsSingleResult = false;
                        mIsSingleResultDone = false;
}
}
        else
            if((mType == IBPP::stExecProcedure) && (mOutRow->Columns() != 0))
        {
            (*gds.Call()->m_dsql_execute)(status.Self(), mTransaction->GetHandlePtr(),
                    &mHandle, 1, mInRow == 0 ? 0 : mInRow->Self());
            if (status.Errors())
            {
                    //Close(); Commented because Execute error should not free the statement
                    std::string context = "Statement::Execute( ";
                    context.append(mSql).append(" )");
                    throw SQLExceptionImpl(status, context.c_str(),
                            _("isc_dsql_execute failed"));
            }
            if (mOutRow != 0)
            {
                mIsSingleResult = true;
                mIsSingleResultDone = false;
            }
        }
        else
{
// Should return at most a single row
                (*gds.Call()->m_dsql_execute2)(status.Self(), mTransaction->GetHandlePtr(),
&mHandle, 1, mInRow == 0 ? 0 : mInRow->Self(),
mOutRow == 0 ? 0 : mOutRow->Self());               
if (status.Errors())
{
//Close(); Commented because Execute error should not free the statement
std::string context = "Statement::Execute( ";
context.append(mSql).append(" )");
throw SQLExceptionImpl(status, context.c_str(),
_("isc_dsql_execute2 failed"));
}
}
}
Проверял шас сижу, проверяю вроде ничего не поламалось, select-ы работают, insert-ы тоже
через isql пробовал сделать returning нескольких строк но получил multiple rows in singleton select  (Пример из документации Firebird 2.1: delete from sys$accounts returning id;) так что дальше в плане возвраoения многих строк я не копал.
Записан
gigabyte
Гость
« Ответ #89 : Март 24, 2010, 16:49 »

There is nothing to implement in IBPP to use this nice feature.  I use it everyday.  The server returns the "returning" value just as it would do for a stored procedure returning a single value.  So just Statement->Get(...) after your Execute() and you're done.

Мдас в сторону Statements->Get() также не копал кому надо будет посмотрите
Записан
Страниц: 1 ... 4 5 [6] 7 8 ... 14   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.096 секунд. Запросов: 23.