Ich habe gestern einige Aufgaben geschrieben und es ist mir aufgefallen, dass ich DER RICHTIGE und AKZEPTIERTE Weg nicht wirklich weiß, ob eine Zeile in der Tabelle existiert, wenn ich pl/sql verwende.
Verwenden wir zum Beispiel die Tabelle
PERSON(ID, Name);
Offensichtlich kann ich kann nicht (es sei denn, es gibt eine geheime Methode) so etwas wie:
BEGIN
IF EXISTS SELECT id FROM person WHERE ID = 10;
-- do things when exists
ELSE
-- do things when doesn't exist
END IF;
END;
Meine Standardlösung war also:
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;
Allerdings weiß ich nicht, ob dies akzeptiert wird oder ob es eine bessere Möglichkeit gibt, dies zu überprüfen. Ich würde es wirklich begrüßen, wenn jemand seine Weisheit mit mir teilen könnte :)
Prost.
Ich würde regulären Code nicht in einen Ausnahmeblock verschieben. Prüfen Sie einfach, ob Zeilen vorhanden sind, die Ihrer Bedingung entsprechen, und fahren Sie mit dem Vorgang fort:
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;
IMO-Code mit einer eigenständigen SELECT-Anweisung, mit der überprüft wird, ob eine Zeile in einer Tabelle vorhanden ist, nutzt die Datenbank nicht ordnungsgemäß. In Ihrem Beispiel haben Sie einen fest codierten ID-Wert, aber so funktionieren Apps nicht in der "realen Welt" (zumindest nicht in my world - Ihre können anders sein :-). In einer typischen App verwenden Sie einen Cursor zum Suchen von Daten. Angenommen, Sie haben eine App, die Rechnungsdaten betrachtet, und müssen wissen, ob der Kunde vorhanden ist. Der Hauptteil der App könnte etwa so aussehen
FOR aRow IN (SELECT * FROM INVOICES WHERE DUE_DATE < TRUNC(SYSDATE)-60)
LOOP
-- do something here
END LOOP;
und im -- do something here
möchten Sie herausfinden, ob der Kunde existiert, und falls nicht, eine Fehlermeldung ausgeben.
Eine Möglichkeit, dies zu tun, besteht darin, eine Art Singleton SELECT wie in einzufügen
-- 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 ist dies jedoch relativ langsam und fehleranfällig. IMO ein besserer Weg (tm), um dies zu tun, ist es, es in den Hauptcursor zu integrieren:
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;
Dieser Code zählt, wenn PERSON.ID als PRIMARY KEY für PERSON deklariert ist (oder zumindest nicht NULL ist). Die Logik ist, dass, wenn die PERSON-Tabelle mit der Abfrage außerhalb verbunden ist und die PERSON_ID als NULL angegeben wird, keine Zeile in PERSON für die angegebene CUSTOMER_ID gefunden wurde, da PERSON.ID einen Wert haben muss (dh mindestens NOT ist.) NULL).
Teile und genieße.
Viele Möglichkeiten, diese Katze zu enthäuten. Ich habe eine einfache Funktion in das Paket jeder Tabelle eingefügt ...
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;
Macht Ihre Schecks wirklich sauber ...
IF pkg.exists(someId) THEN
...
ELSE
...
END IF;
select nvl(max(1), 0) from mytable;
Diese Anweisung ergibt 0, wenn keine Zeilen vorhanden sind, 1, wenn mindestens eine Zeile in dieser Tabelle enthalten ist. Es ist viel schneller als eine Auswahlzählung (*). Der Optimierer "sieht", dass nur eine einzelne Zeile abgerufen werden muss, um die Frage zu beantworten.
Hier ist ein (ausführliches) kleines Beispiel:
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;
select max( 1 )
into my_if_has_data
from MY_TABLE X
where X.my_field = my_condition
and rownum = 1;
Nicht alle Datensätze durchlaufen.
Wenn MY_TABLE keine Daten enthält, wird my_if_has_data auf null gesetzt.
Wenn Sie einen expliziten Cursor verwenden, sollte es folgendermaßen aussehen.
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;
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);