Ошибка multiple rows in singleton select

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
1
2
3
4
5
BEGIN
   INSERT INTO phone_vgsch(phone_vgsch.fio,phone_vgsch.fk_dolzhnost,phone_vgsch.fk_location,phone_vgsch.fk_mobile_phone,phone_vgsch.fk_tariff_plan)
   VALUES (:FIOOO,(SELECT dolzhnost.id_dolzhnost FROM dolzhnost WHERE dolzhnost.dolzhnost= :dolzhh),(SELECT location.id_location FROM location WHERE location.loc = :location),
   (SELECT mobile_phone.id_mobile_phone FROM mobile_phone WHERE mobile_phone.mob_phone = :mob_phone),(SELECT tariff_plan.id_tariff_plan FROM tariff_plan WHERE tariff_plan.tar_plan = :tariff_plan));
END

во код самой таблицу куда я пытаюсь добавить

SQL
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
SET SQL DIALECT 3;
 
 
 
/******************************************************************************/
/***                                 Tables                                 ***/
/******************************************************************************/
 
 
 
CREATE TABLE PHONE_VGSCH (
    FIO              VARCHAR(100) CHARACTER SET WIN1251,
    FK_DOLZHNOST     INTEGER,
    FK_LOCATION      INTEGER,
    FK_MOBILE_PHONE  INTEGER,
    FK_TARIFF_PLAN   INTEGER
);
 
 
 
 
/******************************************************************************/
/***                              Foreign keys                              ***/
/******************************************************************************/
 
ALTER TABLE PHONE_VGSCH ADD CONSTRAINT FK_PHONE_VGSCH_1 FOREIGN KEY (FK_DOLZHNOST) REFERENCES DOLZHNOST (ID_DOLZHNOST);
ALTER TABLE PHONE_VGSCH ADD CONSTRAINT FK_PHONE_VGSCH_2 FOREIGN KEY (FK_LOCATION) REFERENCES LOCATION (ID_LOCATION);
ALTER TABLE PHONE_VGSCH ADD CONSTRAINT FK_PHONE_VGSCH_3 FOREIGN KEY (FK_MOBILE_PHONE) REFERENCES MOBILE_PHONE (ID_MOBILE_PHONE);
ALTER TABLE PHONE_VGSCH ADD CONSTRAINT FK_PHONE_VGSCH_4 FOREIGN KEY (FK_TARIFF_PLAN) REFERENCES TARIFF_PLAN (ID_TARIFF_PLAN);

Подскажите что я нетак делаю?

Добавлено через 23 минуты
думаю может быть переделать запрос таким образом ради проверки добавления

SQL
1
2
3
4
5
6
BEGIN
   INSERT INTO test2(test2.fio,test2.dolzh, test2.fk_location)
   VALUES (:FIOOO, (SELECT phone_vgsch.fk_dolzhnost FROM phone_vgsch  WHERE phone_vgsch.fk_dolzhnost IN  (SELECT dolzhnost.id_dolzhnost FROM dolzhnost WHERE (dolzhnost.dolzhnost) IN (:dolzhh))),
   (SELECT phone_vgsch.fk_location FROM phone_vgsch  WHERE phone_vgsch.fk_location IN  (SELECT location.id_location FROM location WHERE (location.loc) IN (:locccc)))
 
END

но тут опять ругается на ошибку
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. 
для того чтобы вытащить информацию о клиенте используется запрос: 

Код

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:[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оставитель: Дмитрий Кузьменко

Понравилась статья? Поделить с друзьями:
  • Ошибка multiple irp complete requests windows 10
  • Ошибка msvcr100 dll что делать windows 10 pro
  • Ошибка msvcr100 dll скачать файл
  • Ошибка msvcr100 dll скачать для windows 10 x64 windows
  • Ошибка msvcr100 dll симс 4