5 Stimmen

Orakel: Zur Aktualisierung wählen Sie die ersten 10 Zeilen aus

Ich habe eine ITEM Tabelle mit einer der Spalten als CREATED_DATE . In einer geclusterten Umgebung werden viele Kopien von Diensten Elemente aus dieser Tabelle auswählen und verarbeiten. Jeder Dienst sollte die ältesten 10 Einträge aus der Tabelle ITEM auswählen.

Ich bin in der Lage, die ersten 10 Zeilen in einer Stored Procedure auszuwählen:

select * from (
    select  item_id, row_number() over (order by CREATED_DATE) rownumber
    FROM item )
where rownumber < 11

Da viele Dienste dies nutzen sollten, verwende ich select ... for update um die Zeilen als "Verarbeitung" zu aktualisieren. Aber die unten FOR UPDATE Anweisung, schlägt für die obige Select-Anweisung mit dem Fehler "ORA-02014: cannot select FOR UPDATE from view with DISTINCT, GROUP BY, etc." fehl.

OPEN items_cursor FOR
**select Statement**
FOR UPDATE;

Bitte helfen Sie mir mit einer Lösung.

2voto

DCookie Punkte 41310

Ist dies für Ihre Situation geeignet?

SELECT *
  FROM item
 WHERE (item_id,created_date) IN
       (SELECT item_id,created_date
          FROM (SELECT item_id, created_date
                     , ROW_NUMBER() OVER (ORDER BY created_date) rownumber
                  FROM item)
         WHERE rownumber < 11)

1voto

Alex Poole Punkte 172685

Sie können verwenden skip locked und einen Zähler, um dies zu erreichen, solange Sie nicht unbedingt jede Sitzung benötigen, um zusammenhängende Zeilen zu erhalten. Zum Beispiel:

declare
    l_cursor sys_refcursor;
    l_name all_objects.object_name%type;
    l_found pls_integer := 0;
begin
    open l_cursor for
        select  object_name
        from all_objects
        order by created
        for update skip locked;

    loop
        fetch l_cursor into l_name;
        dbms_output.put_line(l_fetches || ':' || l_name);
        if l_cursor%found then
            l_found := l_found + 1;
            -- dbms_lock.sleep(1);
        end if;
        exit when l_cursor%notfound or l_found = 10;
    end loop;
end;
/

Wenn Sie dies gleichzeitig von zwei Sitzungen aus ausführen, erhalten sie unterschiedliche Objekte (allerdings müssen Sie möglicherweise den Aufruf von dbms_lock.sleep innerhalb der found Block, damit er langsam genug ist, um sichtbar zu sein).

Nach Angaben von diese Stelle bei der Verwendung von skip locked werden die ausgewählten Zeilen erst gesperrt, wenn sie abgerufen werden, und alle Zeilen, die von einer anderen Sitzung gesperrt werden, nachdem der Cursor geöffnet wurde, werden einfach ignoriert.

1voto

marcink Punkte 66

Die Antwort von DCookie löst die Multisession-Verarbeitung nicht (es ist nur eine Korrektur der FOR UPDATE-Syntax). Wenn Sie nicht manipulieren rownumber Bereich, jede Instanz des Dienstes, wenn zu wählen, für die Aktualisierung der gleichen Zeilen. Wenn Sie that_for_update_select in zwei Sitzungen ausführen, wird die zweite warten, bis die erste die Transaktion beendet hat. Die parallele Verarbeitung wird eine Illusion sein.

Ich würde eine effiziente Massenverarbeitung zusammen mit for update skip locked Ansatz. Meine Antwort siehe unten:

declare 
  con_limit constant number default 10;
  cursor cItems is
    select i.item_id, i.created_date
    from item i
    order by i.created_date
    for update skip locked;
  type t_cItems is table of cItems%rowtype;
  tItems t_cItems;
begin
  open cItems;
  while true loop
    fetch cItems bulk collect into tItems limit con_limit;
    -- processing tItems
    exit when tItems.count < con_limit;
  end loop;
end;

Eine möglicherweise lange Transaktionsdauer könnte ein Nachteil sein. Erwägen Sie die Verwendung von Oracle Streams Advanced Queuing (DBMS_AQ) als Alternative zu dieser Lösung.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X