Yesterday, an anomaly occurred in our legacy software that I’ve never seen before. It triggers the following error:
multiple rows in singleton select At procedure 'POINTS_BALANCE'
Here is the Stored Procedure
CREATE PROCEDURE POINTS_BALANCE (
OPERATOR CHAR (3),
PERIOD VARCHAR (75))
RETURNS (
P_BALANCE INTEGER)
AS
DECLARE VARIABLE B_DATE timestamp;
DECLARE VARIABLE E_DATE timestamp;
DECLARE VARIABLE ALLOWED_POINTS INTEGER;
begin
P_BALANCE = NULL;
SELECT DATE_BEGIN, DATE_END, TOTAL_POINTS FROM SCHED_POINT_PERIODS
WHERE DESCRIPTION = :PERIOD INTO :B_DATE, :E_DATE, :ALLOWED_POINTS;
IF (B_DATE IS NULL) THEN
BEGIN
SELECT DATE_BEGIN, DATE_END, TOTAL_POINTS FROM SCHED_POINT_PERIODS
WHERE cast('NOW' as timestamp) BETWEEN DATE_BEGIN AND DATE_END+1 INTO :B_DATE, :E_DATE,
:ALLOWED_POINTS;
END
IF (B_DATE IS NOT NULL) THEN
BEGIN
E_DATE = E_DATE + 1;
SELECT SUM(POINTS)+:ALLOWED_POINTS FROM SCHED_ACTUAL
WHERE OPR = :OPERATOR AND BEGIN_TIME BETWEEN :B_DATE AND :E_DATE
INTO :P_BALANCE;
IF (P_BALANCE IS NULL) THEN
P_BALANCE = ALLOWED_POINTS;
END
SUSPEND;
end
SCHED_ACTUAL
is a table that includes the check-in, check-out times of each user
SCHED_POINT_PERIODS
is a table that holds the Allowed_Point values for each period (like Spring 2013, Fall 2013, Christmas Break 2013)
I’m not sure which one is a singleton. Is there a way I can tell just from this stored procedure?
01.01.2007
Очевидно что данная ошибка происходит в вашем триггере или хранимой процедуре. Обычный SELECT внутри триггера или процедуры должен возвращать одну строку (row), т.к. при двух и более строках IB не знает куда поместить значения полей этих строк. Если ваш SELECT возвращает несколько записей, то нужно пользоваться конструкцией FOR SELECT … INTO … DO … которая производит обработку возвращаемого набора записей в цикле.
Если-же вы уверены, что ваш SELECT должен вернуть только одну запись, а ошибка все-таки возникает, то давайте рассмотрим следующую ситуацию:
существуют таблицы ORDERS (заказы) и CLIENTS (клиенты).
обе эти таблицы имеют поле связи CLIENT_ID INTEGER.
для того чтобы вытащить информацию о клиенте используется запрос:
SELECT CLIENT_ID, CLIENT_NAME FROM CLIENTS WHERE CLIENT_ID = ?
где ? — либо значение либо переменная.
Теперь представим себе, что этот запрос должен выполняться в триггере при вставке записи в таблицу ORDERS
CREATE TRIGGER TI_ORDERS FOR ORDERS ACTIVE AFTER INSERT POSITION 0 AS DECLARE VARIABLE CID INTEGER; DECLARE VARIABLE CNAME CHAR(30); BEGIN SELECT C.CLIENT_ID, C.CLIENT_NAME FROM CLIENTS C WHERE C.CLIENT_ID = CLIENT_ID INTO :CID, :CNAME; ...
Итак, поскольку в запросе использован псевдоним C (FROM CLIENTS C), то якобы существует гарантия что в предложении WHERE будут сравниваться поле C.CLIENT_ID из таблицы CLIENTS и поле CLIENT_ID из таблицы ORDERS (в триггере доступны имена полей собственной таблицы). На самом деле даже использование псевдонимов не дает гарантии что переменные будут разичаться, и получается что в предложении WHERE сравнивается само с собой поле таблицы CLIENTS.CLIENT_ID, и в запросе возвращается ВСЯ таблица CLIENTS.
Вот почему возникает вышеупомянутое сообщение об ошибке.
Избавиться от него можно несколькими путями:
Использовать разные имена полей для связи между CLIENTS и ORDERS. например OCLIENT_ID и CCLIENT_ID.
Использовать уточнитель new.CLIENT_ID, несмотря на то что в документации указано что для триггеров последействия (AFTER) он не имеет смысла.
SELECT C.CLIENT_ID, C.CLIENT_NAME FROM CLIENTS C WHERE C.CLIENT_ID = new.CLIENT_ID ...
Перед запросом поместить CLIENT_ID в локальную переменную, и в запросе использовать сравнение не с полем, а с этой локальной переменной.
CID=CLIENT_ID; SELECT C.CLIENT_ID, C.CLIENT_NAME FROM CLIENTS C WHERE C.CLIENT_ID = :CID ...
Borland Interbase / Firebird FAQ
Borland Interbase / Firebird Q&A, версия 2.02 от 31 мая 1999
последняя редакция от 17 ноября 1999 года.
Часто задаваемые вопросы и ответы по Borland Interbase / Firebird
Материал подготовлен в Демо-центре клиент-серверных технологий. (Epsylon Technologies)
Материал не является официальной информацией компании Borland.
E-mail mailto:delphi@demo.ru
www: http://www.ibase.ru/
Телефоны: 953-13-34
источники: Borland International, Борланд АО, релиз Interbase 4.0, 4.1, 4.2, 5.0, 5.1, 5.5, 5.6, различные источники на WWW-серверах, текущая переписка, московский семинар по Delphi и конференции, листсервер ESUNIX1, листсервер mers.com.
Cоставитель: Дмитрий Кузьменко
Добрый день! есть процедура добавления в таблицу с входными параметрами :
При попытки вызвать ее пишет ошибку Multiple rows in singleton select
SQL | ||
|
во код самой таблицу куда я пытаюсь добавить
SQL | ||
|
Подскажите что я нетак делаю?
Добавлено через 23 минуты
думаю может быть переделать запрос таким образом ради проверки добавления
SQL | ||
|
но тут опять ругается на ошибку
Invalid token.
Dynamic SQL Error.
SQL error code = -104.
Token unknown — line 11, column 1.
end.
(((((
Falk0ner, вс, 06/07/2008 — 15:34.
- Выборка данных
Очевидно что данная ошибка происходит в вашем триггере или хранимой процедуре. Обычный SELECT внутри триггера или процедуры должен возвращать одну строку (row), т.к. при двух и более строках IB не знает куда поместить значения полей этих строк. Если ваш SELECT возвращает несколько записей, то нужно пользоваться конструкцией FOR SELECT … INTO … DO … которая производит обработку возвращаемого набора записей в цикле.
Если-же вы уверены, что ваш SELECT должен вернуть только одну запись, а ошибка все-таки возникает, то давайте рассмотрим следующую ситуацию:
существуют таблицы ORDERS (заказы) и CLIENTS (клиенты).
обе эти таблицы имеют поле связи CLIENT_ID INTEGER.
для того чтобы вытащить информацию о клиенте используется запрос:
SELECT CLIENT_ID, CLIENT_NAME
FROM CLIENTS
WHERE CLIENT_ID = ?
где ? — либо значение либо переменная.
Теперь представим себе, что этот запрос должен выполняться в триггере при вставке записи в таблицу ORDERS
CREATE TRIGGER TI_ORDERS FOR ORDERS
ACTIVE AFTER INSERT POSITION 0
AS
DECLARE VARIABLE CID INTEGER;
DECLARE VARIABLE CNAME CHAR(30);
BEGIN
SELECT C.CLIENT_ID, C.CLIENT_NAME
FROM CLIENTS C
WHERE C.CLIENT_ID = CLIENT_ID
INTO :CID, :CNAME;
…
Итак, поскольку в запросе использован псевдоним C (FROM CLIENTS C), то якобы существует гарантия что в предложении WHERE будут сравниваться поле C.CLIENT_ID из таблицы CLIENTS и поле CLIENT_ID из таблицы ORDERS (в триггере доступны имена полей собственной таблицы). На самом деле даже использование псевдонимов не дает гарантии что переменные будут разичаться, и получается что в предложении WHERE сравнивается само с собой поле таблицы CLIENTS.CLIENT_ID, и в запросе возвращается ВСЯ таблица CLIENTS.
Вот почему возникает вышеупомянутое сообщение об ошибке.
Избавиться от него можно несколькими путями:
Использовать разные имена полей для связи между CLIENTS и ORDERS. например OCLIENT_ID и CCLIENT_ID.
Использовать уточнитель new.CLIENT_ID, несмотря на то что в документации указано что для триггеров последействия (AFTER) он не имеет смысла.
SELECT C.CLIENT_ID, C.CLIENT_NAME
FROM CLIENTS C
WHERE C.CLIENT_ID = new.CLIENT_ID
…
Перед запросом поместить CLIENT_ID в локальную переменную, и в запросе использовать сравнение не с полем, а с этой локальной переменной.
CID=CLIENT_ID;
SELECT C.CLIENT_ID, C.CLIENT_NAME
FROM CLIENTS C
WHERE C.CLIENT_ID = :CID
…
Borland Interbase / Firebird FAQ
Borland Interbase / Firebird Q&A, версия 2.02 от 31 мая 1999
последняя редакция от 17 ноября 1999 года.
Часто задаваемые вопросы и ответы по Borland Interbase / Firebird
Материал подготовлен в Демо-центре клиент-серверных технологий. (Epsylon Technologies)
Материал не является официальной информацией компании Borland.
E-mail mailto:delphi@demo.ru
www: http://www.ibase.ru/
Телефоны: 953-13-34
источники: Borland International, Борланд АО, релиз Interbase 4.0, 4.1, 4.2, 5.0, 5.1, 5.5, 5.6, различные источники на WWW-серверах, текущая переписка, московский семинар по Delphi и конференции, листсервер ESUNIX1, листсервер mers.com.
Cоставитель: Дмитрий Кузьменко
Очевидно что данная ошибка происходит в вашем триггере или хранимой процедуре. Обычный SELECT внутри триггера или процедуры должен возвращать одну строку (row), т.к. при двух и более строках IB не знает куда поместить значения полей этих строк. Если ваш SELECT возвращает несколько записей, то нужно пользоваться конструкцией FOR SELECT … INTO … DO … которая производит обработку возвращаемого набора записей в цикле.
Если-же вы уверены, что ваш SELECT должен вернуть только одну запись, а ошибка все-таки возникает, то давайте рассмотрим следующую ситуацию:
существуют таблицы ORDERS (заказы) и CLIENTS (клиенты).
обе эти таблицы имеют поле связи CLIENT_ID INTEGER.
для того чтобы вытащить информацию о клиенте используется запрос:
Код |
|
где ? — либо значение либо переменная.
Теперь представим себе, что этот запрос должен выполняться в триггере при вставке записи в таблицу ORDERS
Код |
|
Итак, поскольку в запросе использован псевдоним C (FROM CLIENTS C), то якобы существует гарантия что в предложении WHERE будут сравниваться поле C.CLIENT_ID из таблицы CLIENTS и поле CLIENT_ID из таблицы ORDERS (в триггере доступны имена полей собственной таблицы). На самом деле даже использование псевдонимов не дает гарантии что переменные будут разичаться, и получается что в предложении WHERE сравнивается само с собой поле таблицы CLIENTS.CLIENT_ID, и в запросе возвращается ВСЯ таблица CLIENTS.
Вот почему возникает вышеупомянутое сообщение об ошибке.
Избавиться от него можно несколькими путями:
Использовать разные имена полей для связи между CLIENTS и ORDERS. например OCLIENT_ID и CCLIENT_ID.
Использовать уточнитель new.CLIENT_ID, несмотря на то что в документации указано что для триггеров последействия (AFTER) он не имеет смысла.
Код |
|
Перед запросом поместить CLIENT_ID в локальную переменную, и в запросе использовать сравнение не с полем, а с этой локальной переменной.
Код |
|
Borland Interbase / Firebird FAQ
Borland Interbase / Firebird Q&A, версия 2.02 от 31 мая 1999
последняя редакция от 17 ноября 1999 года.
Часто задаваемые вопросы и ответы по Borland Interbase / Firebird
Материал подготовлен в Демо-центре клиент-серверных технологий. (Epsylon Technologies)
Материал не является официальной информацией компании Borland.
E-mail mailto:[email protected]
www: http://www.ibase.ru/
Телефоны: 953-13-34
источники: Borland International, Борланд АО, релиз Interbase 4.0, 4.1, 4.2, 5.0, 5.1, 5.5, 5.6, различные источники на WWW-серверах, текущая переписка, московский семинар по Delphi и конференции, листсервер ESUNIX1, листсервер mers.com.
Cоставитель: Дмитрий Кузьменко