2010-02-25 10 views
6

Wiem, że wydaje się to podstawową rzeczą, ale nigdy wcześniej tego nie robiłem.Jak zwrócić rekord z istniejącej tabeli z funkcji Oracle PL/SQL?

Chciałbym zwrócić pojedynczy rekord z istniejącej tabeli w wyniku funkcji Oracle PL/SQL. Znalazłem już kilka różnych sposobów na zrobienie tego, ale interesuje mnie najlepszy sposób, aby to zrobić (czytaj: nie jestem aż tak szczęśliwy z tego, co znalazłem).

Najważniejsze, co robię, jest to ... Mam tabelę o nazwie "użytkownicy" i chcę, aby funkcja "update_and_get_user", która podała nazwę użytkownika (oraz inne zaufane informacje o tym użytkowniku), potencjalnie wykonaj różne akcje na stole użytkowników, a następnie zwróć zero lub jeden wiersz/rekord ze wspomnianej tabeli.

Jest to podstawowy zarys kodu w mojej głowie w tej chwili (czytaj: nie wiem, czy składnia jest nawet blisko do skorygowania):

CREATE FUNCTION update_and_get_user(UserName in VARCHAR2, OtherStuff in VARCHAR2) 
    RETURN users PIPELINED IS 
    TYPE ref0 IS REF CURSOR; 
    cur0  ref0; 
    output_rec users%ROWTYPE; 
BEGIN 
    -- Do stuff 

    -- Return the row (or nothing) 
    OPEN cur0 FOR 'SELECT * FROM users WHERE username = :1' 
    USING UserName; 

    LOOP 
    FETCH cur0 INTO output_rec; 
    EXIT WHEN cur0%NOTFOUND; 
    PIPE ROW(output_rec); 
    END LOOP; 
END update_and_get_user; 

Widziałem przykłady, gdzie zapis lub tabela zwracane, typ rekordu lub tabeli, które zostały wcześniej utworzone/zadeklarowane, ale wydaje się, że jeśli tabela została już zdefiniowana, powinienem móc ją wykorzystać, a tym samym nie martwić się o synchronizację kodu deklaracji typu, jeśli zmiany w tabeli są kiedykolwiek tworzone.

jestem otwarty dla wszystkich potencjalnych rozwiązań i komentarzem, ale robię naprawdę chcą zachować to w jednej funkcji PL/SQL (w przeciwieństwie do kodu w jakimś innym języku/ramy, które komunikuje się z bazą danych wiele razy , kończąc przy użyciu jakiejś formy "SELECT * FROM users WHERE username = blah") jako systemu wywołującego funkcję, a sama baza danych może być innym miastem. Poza tym limitem jestem otwarty na zmianę myślenia.

Odpowiedz

12

Tak to zrobię. Zmienne/nazwy tabel/nazwy kolumn nie uwzględniają wielkości liter w Oracle, więc użyłbym user_name zamiast UserName.

CREATE TABLE users(UserName varchar2(20), OtherStuff VARCHAR2(20)); 

Funkcja update_and_get_user. Zwróć uwagę, że zwracam ROWTYPE zamiast Pipelined Tables.

CREATE OR REPLACE FUNCTION update_and_get_user(
    in_UserName IN users.UserName%TYPE, 
    in_OtherStuff IN users.OtherStuff%TYPE) 
RETURN users%ROWTYPE 
IS 
    output_rec users%ROWTYPE; 
BEGIN 
    UPDATE users 
    SET OtherStuff = in_OtherStuff 
    WHERE UserName = in_UserName 
    RETURNING UserName, OtherStuff 
    INTO output_rec; 
    RETURN output_rec; 
END update_and_get_user; 

I tak można to nazwać. Nie można sprawdzić, czy ROWTYPE jest NULL, ale można na przykład sprawdzić username.

DECLARE 
    users_rec users%ROWTYPE; 
BEGIN 
    users_rec := update_and_get_user('user', 'stuff'); 
    IF(users_rec.username IS NOT NULL) THEN 
    dbms_output.put_line('FOUND: ' || users_rec.otherstuff); 
    END IF; 
END; 

Roztwór stosując PIPED ROWS jest niższa, ale to nie działa w ten sposób. Nie można aktualizować tabel wewnątrz zapytania.

SELECT * FROM TABLE(update_and_get_user('user', 'stuff')) 

ORA-14551: cannot perform a DML operation inside a query 

Rozwiązanie mogłoby wyglądać tak:

CREATE OR REPLACE TYPE users_type 
AS OBJECT 
(
    username VARCHAR2(20), 
    otherstuff VARCHAR2(20) 
) 

CREATE OR REPLACE TYPE users_tab 
    AS TABLE OF users_type; 

CREATE OR REPLACE FUNCTION update_and_get_user(
    in_UserName IN users.username%TYPE, 
    in_OtherStuff IN users.otherstuff%TYPE) 
RETURN users_tab PIPELINED 
IS 
    output_rec users%ROWTYPE; 
BEGIN 
    UPDATE users 
    SET OtherStuff = in_OtherStuff 
    WHERE UserName = in_UserName 
    RETURNING username, otherstuff 
    INTO output_rec; 
    PIPE ROW(users_type(output_rec.username, output_rec.otherstuff)); 
END; 
+0

+1 Kompleksowa odpowiedź Peter - mnie bić do niej (trzeba przyspieszyć moje pisanie) w razie potrzeby Pawła, ten link (http : //download.oracle.com/docs/cd/E11882_01/appdev.112/e10472/rowtype_attribute.htm#LNPLS01342) wyjaśnia atrybut% rowtype –

+1

+1 Peter! Chciałbym móc oddać wiele głosów. Po pierwsze, wygląda to naprawdę dobrze. Po drugie, nie miałem pojęcia, że ​​możesz zrobić "POWRÓT ... NA ... ..." w AKTUALIZACJI, to naprawdę fajne i poprawi wydajność.Po trzecie, nie miałem pojęcia, że ​​nie można wykonać aktualizacji wewnątrz kwerendy funkcji tabeli, co w końcu bardzo by mnie sfrustrowało. Zaznaczę to jako zaakceptowaną odpowiedź, gdy tylko dostanę to zaimplementowałem/zweryfikuję. Dziękuję Ci! –

+0

No cóż, powodzenia w tym momencie :) –

Powiązane problemy