18 сентября 2019 г. 7:25

Можно ли в SQL при команде Inser узнать id , в который записалось.

Добрый день.
Можно ли при команде Inser узнать id , в который записалось.
Пологаю можно в какое-нибудь поле записать уникальное число и потом по нему искать id, но хотелась бы узнать, есть варианты пологичней и с меньшими затратами системы?
Записываю так

    QSqlQuery query(db);
    query.prepare("insert into UESRS (NAME) "
                  "values (:NAME)");
    query.bindValue(":NAME", "UserNotRegistered");

    if(!query.exec())
    {
        emit sig_error(query.lastError().text());
        qDebug()<<"не записалось";
        qDebug()<<"query.lastError().text() : "<<query.lastError().text();
        msg = "false";
    }
    else {
        qDebug()<<"Ok";
        msg = "true";
    }
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
38

https://docs.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql?redirectedfrom=MSDN&view=sql-server-2017

CREATE TABLE USERS в студию.
И вообще для чего используется таблица?
у меня такая таблица пользователей например:

--Таблица пользователей
CREATE TABLE IF NOT EXISTS `users` (
        `user_id`   INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
        `fio`   TEXT NOT NULL,
        `password`  TEXT,
        `isactive`  TEXT NOT NULL DEFAULT 'true'
);

Таблица хранит данные пользователй
Вот код таблицы:

CREATE GENERATOR GEN_UESRS_ID;

CREATE TABLE UESRS (
    ID          INTEGER NOT NULL,
    PHONE       CHAR(1024),
    NAME        CHAR(1024),
    "PASSWORD"  CHAR(1024),
    RATING      FLOAT
);




/******************************************************************************/
/***                              Primary keys                              ***/
/******************************************************************************/

ALTER TABLE UESRS ADD PRIMARY KEY (ID);

Еще у ID есть автоинкримент.
Работа с базой идет в многопоточном режиме.

А что дальше надо делать с id пользователя?

Два варианта
1)
Если есть поддержка автоинкримента, значит есть тригер и генератор, значит:
Первым действием получать новый ID например - SELECT GEN_ID( , ) FROM RDB$DATABASE; (пример от СУБД будет отличаться, смотри доку по своей СУБД)
Далее вставляешь куда угодно, при этом обеспечиваешь уникальность (за этим следит СУБД и дублирования не будет), даже если будет ошибка вставки твой ID просто останется не учтенным, но это без разницы, главная задача уникальности будет обеспечина

2) Делать вставку и сразу чтение в одной транзакции, т.е. записал данные и прочитал их (с MSSQL так делали, проблем не было)

А вообще
- Какая у вас СУБД (от нее зависит много например в MySQL есть команда LAST_INSERT_ID)
- На чем пишете (Например в Django после сохранении данных сразу можно обратиться и получить ID, без проблем)
- Задачу бы более шире узнать, т.к. решение может быть проще и вообще лежать в другой плоскости

Я пишу на с++ в Qt.
База данных Firebird.
Нужно заргистрировать пользователя без данных, после этого вернуть id, под которым пользователь зарегистрирован. Больше ничего делать не нужно.

Вариант 1 используйте - SELECT GEN_ID(my_gen , incr) FROM RDB$DATABASE (https://firebirdsql.org/refdocs/langrefupd20-genid.html)

Когда пишу под FB делаю именно так, проблем не встречал
пример таблицы и тригера ниже

CREATE GENERATOR GEN_USERS_ID;

CREATE TABLE USERS (
    ID                        DT_ID /* DT_ID = INTEGER NOT NULL */,
    NAME                      VARCHAR(500),
    LOGIN                     VARCHAR(50),
    PWD                       VARCHAR(50),
    PROF                      VARCHAR(50),

);


/******************************************************************************/
/***                              Primary keys                              ***/
/******************************************************************************/

ALTER TABLE USERS ADD CONSTRAINT PK_USERS PRIMARY KEY (ID);


/******************************************************************************/
/***                                Indices                                 ***/
/******************************************************************************/

CREATE INDEX USERS_ID ON USERS (ID);
CREATE INDEX USERS_NAME ON USERS (NAME);


/******************************************************************************/
/***                                Triggers                                ***/
/******************************************************************************/



SET TERM ^ ;



/******************************************************************************/
/***                          Triggers for tables                           ***/
/******************************************************************************/



/* Trigger: USERS_BI */
CREATE OR ALTER TRIGGER USERS_BI FOR USERS
ACTIVE BEFORE INSERT POSITION 0
as
begin
  if (new.id is null) then
    new.id = gen_id(gen_users_id,1);
end
^

Думаю если будете делать в потоках то мне кажется что просле INSERT сдулать SELECT u.id FROM users u WHERE u.name = 'UserName' и вернуть Id из потока.

Мне кажется плохая идея для FB, впрочем я не знаком с тонкостями работы C++ для MySQL где-то читал там такая фишка работает, но почему-то у меня сложилось мнение что именно MySQL обеспечивает изоляцию. В любом случае по ресурсоемкости будет одно и тоже, мой вариант, он однозначен как топор - будет работать, проблем с уникальностью не будет, а вот с поотками еще вопрос... Только время и нагрузка покажет)

При такой записи имена всех юзеров будут одинаковыми.
В таком случае выдаст наверно первый id из базы, а не последний

SELECT u.id FROM users WHERE u.name = 'UserName'  

вам написали рецепт, если идей лучше нет делайте по рецепту. так как вы написали идентифицировать id нельзя

Да, спасибо буду пытаться сделать по Вашему варианту.

у вас два запрлса первый получает id второй вставляет новую запись используя полученный в первом запросе id. Все это вы делаете в С++ так сказать. не забудьте дописать нужный тригер и создать ненератор, если его нет, если делать базу через ibEpert и поставить флаг автоинкримент, то оно все само сделает

Попробовал Ваш запрос

SELECT GEN_ID(GEN_UESRS_ID , 1) FROM UESRS

Но получаю ошибку

value: not positioned on a valid record

Скажите пожалуйста что не так в моем запросе?
Ниже полный код моей таблицы:

/******************************************************************************/
/***               Generated by IBExpert 18.09.2019 18:59:25                ***/
/******************************************************************************/

/******************************************************************************/
/***      Following SET SQL DIALECT is just for the Database Comparer       ***/
/******************************************************************************/
SET SQL DIALECT 3;



/******************************************************************************/
/***                                 Tables                                 ***/
/******************************************************************************/


CREATE GENERATOR GEN_UESRS_ID;

CREATE TABLE UESRS (
    ID          INTEGER NOT NULL,
    PHONE       CHAR(1024),
    NAME        CHAR(1024),
    "PASSWORD"  CHAR(1024),
    RATING      FLOAT
);




/******************************************************************************/
/***                              Primary keys                              ***/
/******************************************************************************/

ALTER TABLE UESRS ADD PRIMARY KEY (ID);


/******************************************************************************/
/***                                Triggers                                ***/
/******************************************************************************/



SET TERM ^ ;



/******************************************************************************/
/***                          Triggers for tables                           ***/
/******************************************************************************/



/* Trigger: UESRS_BI */
CREATE OR ALTER TRIGGER UESRS_BI FOR UESRS
ACTIVE BEFORE INSERT POSITION 0
as
begin
  if (new.id is null) then
    new.id = gen_id(gen_uesrs_id,1);
end
^

SET TERM ; ^



/******************************************************************************/
/***                               Privileges                               ***/
/******************************************************************************/

А если в базе выполняю эту команду, то получаю 3 числа в столбик, но никак не одно.
Скажите пожалуйста, почему так?

SELECT GEN_ID(GEN_UESRS_ID , 1) FROM UESRS

Оно похоже пробегает ко всему столбцу и добавляет столько значений, сколько есть в таблице.
Если делать так, то добавляет только 1 значение к последнему, но как прописать последнее значение?

SELECT GEN_ID(GEN_UESRS_ID , 1) FROM UESRS  WHERE id = 20

напишите завтра в телеграмм разберемся, потом тут выложите готовый рецепт @Bardin_A_A

Такой запрос в базе работает

SELECT GEN_ID(GEN_UESRS_ID , 1) FROM RDB$DATABASE

Но в Qt такой код возвращает 0. Скажите пожалуйста, что не так в этом коде?

query.prepare("SELECT GEN_ID(GEN_UESRS_ID , 1) FROM RDB$DATABASE");

    if(!query.exec())
    {
        emit sig_error(query.lastError().text());
        qDebug()<<"не записалось";
        qDebug()<<"query.lastError().text() : "<<query.lastError().text();
        msg = "false";
    }
    else {
        qDebug()<<"Ok";
        qDebug()<<"query.value(0):"<<query.value(0).toInt();
        msg = "true";
    }

Процедура не подойдет?

SET TERM ^ ;
CREATE OR ALTER PROCEDURE adduser (name VARCHAR(1024),  phone VARCHAR(1024), password VARCHAR(1024), rating float)
return (user_id integer)
as
DECLARE VARIABLE user_id;
begin
    user_id = GEN_ID(GEN_USERS_ID,1);
    insert into USERS (ID,PHONE,NAME, PASSWORD,RATING)
    values(:user_id, phone, name, password, rating);
end^
SET TERM ; ^

возвращает id и добавляет запись в таблицу.

Скажите пожалуйста, что делать с это процедурой?
Её нужно выполнить в базе данных и потом как то ее вызывать из Qt? Если да, то как потом вызывать процедуру?

Выполнить скрипт на базе через редактор скриптов IBE , а вызывать через QSqlQuery как обычный запрос.
только в доках глянь как получать возвращаемое заначение, я не помню.
q.prepare("execute procedure addser(:name, :ph, :pass, :rat)");

Скажите пожалуйста, как добавить эту процедуру?
Если просто вставляю в скрипт, то оно не работает (что ожидаемо).
Похоже ее нужно добавлять непосредственно в нужную таблицу, но как?

IBexpert Настройки редактор скриптов Ctrl+F12
Не забыть поставить галочку Использовать текщее соединение

https://s.mail.ru/BkqS/KQBWcafHa

только мне кажется в строке
3 - returns (user_id integer) нужно написать, а не return
5 - DECLARE VARIABLE user_id integer; тут по моему надо указать тип переменной integer

Согласен... не было примеров под рукой.

Но у меня почему-то интерфейс отличается, нормально ли это? Или може

да нормально, от версии зависит, и у меня все в одном окне опция стоит, у вас нет

Советы по IBE: ВЫМУЧАННЫЕ массой потерянного времени
1. Использовать последню версию. https://ru.wikipedia.org/wiki/IBExpert в разделе ссылки Бесплатная полнофункциональная версия — Ссылка для скачивания специальной лицензии для бывшего СССР.
2. НИКОГДА не использовать SDI интерфейс. Изменить можно Настройка Настроки среды.

Получилась такая процедура

SET TERM ^ ;
CREATE OR ALTER PROCEDURE adduser (name VARCHAR(1024),  phone VARCHAR(1024), password VARCHAR(1024), rating float)
returns  (user_id integer)
as
DECLARE VARIABLE user_id integer; 
begin
    user_id = GEN_ID(GEN_USERS_ID,1);
    insert into USERS (ID,PHONE,NAME, PASSWORD,RATING)
    values(:user_id, phone, name, password, rating);
end^
SET TERM ; ^

Но при нажатии на выполнение получаю ошибку, говорящую что скрипт выполнен с ошибкой.

Текст или срин ошибки.

Unsuccessful execution caused by system error that does not preclude successful execution of subsequent statements.
Dynamic SQL Error.
SQL error code = -901.
variable USER_ID conflicts with parameter in same procedure.

А если так

SET TERM ^ ;
CREATE OR ALTER PROCEDURE adduser (name VARCHAR(1024),  phone VARCHAR(1024), password VARCHAR(1024), rating float)
returns (out_user_id integer)
as
DECLARE VARIABLE user_id integer;
begin
    user_id = GEN_ID(GEN_USERS_ID,1);
    out_user_id = :user_id;
    insert into USERS (ID,PHONE,NAME, PASSWORD,RATING)
    values(:user_id, phone, name, password, rating);
end^
SET TERM ; ^
  • 19 сентября 2019 г. 3:26
  • Ответ был помечен как решение.

Подправил скрипт, так приняло:

SET TERM ^ ;
CREATE OR ALTER PROCEDURE adduser (name VARCHAR(1024),  phone VARCHAR(1024), password VARCHAR(1024), rating float)
returns (out_user_id integer)
as
DECLARE VARIABLE user_id integer;
begin
    user_id = GEN_ID(GEN_UESRS_ID,1);
    out_user_id = :user_id;
    insert into UESRS (ID,PHONE,NAME, PASSWORD,RATING)
    values(:user_id, :phone, :name, :password, :rating);
end^
SET TERM ; ^

В Qt Сделал такой запрос и все заработало

        query.prepare("execute procedure adduser(:name, :phone, :password, :rating)");
    query.bindValue(":name", "UserNotRegistered");
    query.bindValue(":phone", "UserNotRegistered");
    query.bindValue(":password", "UserNotRegistered");
    query.bindValue(":rating", 0);

    if(!query.exec())
    {
        emit sig_error(query.lastError().text());
        qDebug()<<"не записалось";
        qDebug()<<"query.lastError().text() : "<<query.lastError().text();
        msg = "false";
    }
    else if(query.next()) {
        qDebug()<<"Ok";
        qDebug()<<"query.value(0):"<<query.value(0).toString();
        msg = "true";
    }

Спасибо всем за помощь!

Так а где ты используешь возвращаемый ID?

В клиентской части. Клиентская часть по id будет получать соответствующую информацию.

если просто добавлять запись, то тогда процедра не нужна

insert into USERS (ID,PHONE,NAME, PASSWORD,RATING)
values(GEN_ID(GEN_USERS_ID,1), phone, name, password, rating);

Но мне же нужно еще получить этот id и вернуть его пользователю, а при таком запросе ничего не вернется.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Ищу работу?
25,000.00 руб. - 30,000.00 руб.
Разработчик Qt/C++
Barnaul, Altai Krai, Russia

Для зарегистрированных пользователей на сайте присутствует минимальное количество рекламы

СВ
23 октября 2019 г. 1:00
Семен Волох

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:70баллов,
  • Очки рейтинга1
SS
22 октября 2019 г. 14:31
Samantha Smith

Qt - Тест 001. Сигналы и слоты

  • Результат:52баллов,
  • Очки рейтинга-4
МБ
21 октября 2019 г. 1:25
Михаил Булатов

C++ - Тест 002. Константы

  • Результат:16баллов,
  • Очки рейтинга-10
Последние комментарии
17 октября 2019 г. 2:17
Евгений Легоцкой

Используем, там где требуется :)
MP
17 октября 2019 г. 2:15
Mikhail Petrov

Совет: подключайте ресурсы динамически. Используйте Resource Compiler: https://doc.qt.io/qt-5/rcc.html
16 октября 2019 г. 6:45
Евгений Легоцкой

Если это не чистой воды спам, а по делу, то без проблем. Но в таком случае лучше создавайте отдельный вопрос на форуме . При создании вопроса есть поле, в котором можно указать статью…
КК
16 октября 2019 г. 6:39
Кирилл Кирилыч

А тут можно ссылки на сторонний ресурс показывать? Нашёл на habr похожую статью, только там чуток отличается код и про локальный сервер написано, нужно чтоб кто то понимающий посмотрел и своё …
Сейчас обсуждают на форуме
23 октября 2019 г. 4:06
Евгений Легоцкой

Ну если после обновления начало появляться, то тогда откатить драйвера. А вообще, если это жить не мешает и код работает как и раньше, то просто проигнорировать эти сообщения.
22 октября 2019 г. 2:42
Pavel K.

Всем привет , Пытаюсь реализовать класс для работы с блютуз (Bluetooth Handler) для мобилки , с использование QBluetoothDeviceInfo и QBluetoothDeviceDiscoveryAgent . Может у кого е…
22 октября 2019 г. 2:16
Pavel K.

попробуй сделать через свой собственный компонет , те возьми контрол Component, например , переорпедели как свой , в нем что нить типо проперти type : disk1, disk2 (сделай метод в структуре …
Е
22 октября 2019 г. 0:03
Евгений_Канусовский@1981

Этот алгоритм предназначен для того чтобы исключить из обработки строки содержащие буквенные символы. Если Вам не трудно опишите пожалуйста как бы Вы написали этот алгоритм, желательно в коде?
MP
21 октября 2019 г. 7:03
Mikhail Petrov

Зависит от вашей задачи. Можете обратить внимание на этот пример: https://doc.qt.io/qt-5/qtqml-referenceexamples-properties-example.html QQmlListProperty используется мною достаточно ч…
EVILEG
О нас
Услуги
© EVILEG 2015-2019
Рекомендует хостинг TIMEWEB