Подтвердить что ты не робот

Правильный способ проверки наличия строки в таблице [pl/sql block]

Я писал несколько заданий вчера, и мне показалось, что я действительно не знаю PROPER и ACCEPTED проверки наличия строки в таблице, когда я использую pl/sql.

В качестве примеров можно использовать таблицу

PERSON(ID, Name); 

Очевидно, что не может (если не существует секретного метода):

BEGIN 
  IF EXISTS SELECT id FROM person WHERE ID = 10; 
    -- do things when exists
  ELSE
    -- do things when doesn't exist
  END IF;
END;

Итак, мой стандартный способ решения этого вопроса:

DECLARE
  tmp NUMBER;
BEGIN 
  SELECT id INTO tmp FROM person WHERE id = 10; 
  --do things when record exists
EXCEPTION
  WHEN no_data_found THEN
  --do things when record doesn't exist
END; 

Однако я не знаю, согласился ли он на это, или если у вас есть лучший способ проверки, я действительно буду apprieciate, если кто-то может поделиться со мной своей мудростью:)

Приветствия.

4b9b3361

Ответ 1

Я бы не вводил обычный код в блок исключений. Просто проверьте, существуют ли какие-либо строки, соответствующие вашему условию, и оттуда:

declare
  any_rows_found number;
begin
  select count(*)
  into   any_rows_found
  from   my_table
  where  rownum = 1 and
         ... other conditions ...

  if any_rows_found = 1 then
    ...
  else
    ...
  end if;

Ответ 2

IMO-код с автономным SELECT, используемый для проверки того, существует ли строка в таблице, не использует надлежащее преимущество базы данных. В вашем примере у вас есть твердое идентификационное значение, но не то, как приложения работают в "реальном мире" (по крайней мере, в моем мире - ваше может быть другим:-). В типичном приложении вы будете использовать курсор для поиска данных - так что скажем, у вас есть приложение, которое смотрит на данные счета и должно знать, существует ли клиент. Основной частью приложения может быть что-то вроде

FOR aRow IN (SELECT * FROM INVOICES WHERE DUE_DATE < TRUNC(SYSDATE)-60)
LOOP
  -- do something here
END LOOP;

а в -- do something here вы хотите найти, существует ли клиент, а если не печатать сообщение об ошибке.

Один из способов сделать это - установить какой-то одноэлементный SELECT, как в

-- Check to see if the customer exists in PERSON

BEGIN
  SELECT 'TRUE'
    INTO strCustomer_exists
    FROM PERSON
    WHERE PERSON_ID = aRow.CUSTOMER_ID;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    strCustomer_exists := 'FALSE';
END;

IF strCustomer_exists = 'FALSE' THEN
  DBMS_OUTPUT.PUT_LINE('Customer does not exist!');
END IF;

но IMO это относительно медленно и подвержено ошибкам. ИМО - лучший способ (tm) сделать это - включить его в основной курсор:

FOR aRow IN (SELECT i.*, p.ID AS PERSON_ID
               FROM INVOICES i
               LEFT OUTER JOIN PERSON p
                 ON (p.ID = i.CUSTOMER_PERSON_ID)
               WHERE DUE_DATA < TRUNC(SYSDATE)-60)
LOOP
  -- Check to see if the customer exists in PERSON

  IF aRow.PERSON_ID IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('Customer does not exist!');
  END IF;
END LOOP;

Этот код рассчитывает, что PERSON.ID будет объявлен как ПЕРВИЧНЫЙ КЛЮЧ для ЧЕЛОВЕКА (или, по крайней мере, как NOT NULL); логика заключается в том, что если таблица PERSON является внешним соединением с запросом, а PERSON_ID появляется как NULL, это означает, что ни одна строка не найдена в PERSON для данного CUSTOMER_ID, потому что PERSON.ID должен иметь значение (то есть по крайней мере NOT NULL).

Поделитесь и наслаждайтесь.

Ответ 3

Много способов обмануть этого кота. Я поставил простую функцию в каждом пакете таблицы...

function exists( id_in in yourTable.id%type ) return boolean is
  res boolean := false;
begin
  for c1 in ( select 1 from yourTable where id = id_in and rownum = 1 ) loop
    res := true;
    exit; -- only care about one record, so exit.
  end loop;
  return( res );
end exists;

Делает ваши проверки действительно чистыми...

IF pkg.exists(someId) THEN
...
ELSE
...
END IF;

Ответ 4

select nvl(max(1), 0) from mytable;

Этот оператор возвращает 0, если строк нет, 1, если в этой таблице есть хотя бы одна строка. Это быстрее, чем выборка count (*). Оптимизатор "видит", что для ответа на вопрос нужно выбрать только одну строку.

Здесь (подробный) небольшой пример:

declare
  YES constant      signtype := 1;
  NO  constant      signtype := 0;
  v_table_has_rows  signtype;
begin

  select nvl(max(YES), NO)
    into v_table_has_rows
    from mytable -- where ...
  ;

  if v_table_has_rows = YES then
    DBMS_OUTPUT.PUT_LINE ('mytable has at least one row');
  end if;

end;

Ответ 5

Select 'YOU WILL SEE ME' as ANSWER from dual
where exists (select 1 from dual where 1 = 1);

Select 'YOU CAN NOT SEE ME' as ANSWER from dual
where exists (select 1 from dual where 1 = 0);

Select 'YOU WILL SEE ME, TOO' as ANSWER from dual
where not exists (select 1 from dual where 1 = 0);

Ответ 6

Если вы используете явный курсор, он должен быть следующим.

DECLARE
   CURSOR get_id IS 
    SELECT id 
      FROM person 
      WHERE id = 10;

  id_value_ person.id%ROWTYPE;
BEGIN 
   OPEN get_id;
   FETCH get_id INTO id_value_;

   IF (get_id%FOUND) THEN
     DBMS_OUTPUT.PUT_LINE('Record Found.');
   ELSE
     DBMS_OUTPUT.PUT_LINE('Record Not Found.');
   END IF;
   CLOSE get_id;

EXCEPTION
  WHEN no_data_found THEN
  --do things when record doesn't exist
END;