оракул: для обновления выберите первые 10 строк - PullRequest
1 голос
/ 14 июня 2011

У меня есть таблица ITEM с одним из столбцов как CREATED_DATE. В кластерной среде многие копии сервисов будут выбирать элементы из этой таблицы и обрабатывать их. Каждый сервис должен выбрать 10 самых старых элементов из таблицы ITEM.

Я могу выбрать первые 10 строк, используя это в хранимой процедуре:

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

Так как многие службы должны использовать это, я использую select ... for update, чтобы обновить строки как "обработку". Но приведенный ниже оператор FOR UPDATE завершается с ошибкой для указанного выше оператора select с ошибкой «ORA-02014: невозможно выбрать FOR UPDATE в представлении с DISTINCT, GROUP BY и т. Д.»

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

Пожалуйста, помогите мне с решением.

Ответы [ 3 ]

1 голос
/ 14 июня 2011

Вы можете использовать skip locked и счетчик для достижения этой цели, если вам не обязательно каждый сеанс для получения смежных строк. Например:

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;
/

Если вы запустите это одновременно из двух сеансов, они получат разные объекты (хотя вам может понадобиться включить вызов dbms_lock.sleep внутри блока found, чтобы сделать его достаточно медленным, чтобы его можно было увидеть).

Согласно этой записи , при использовании skip locked выбранные строки не блокируются, пока они не извлечены, а любые строки, заблокированные другой сессией после открытия курсора, просто игнорируются.

1 голос
/ 14 июня 2011

Это работает для вашей ситуации?

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)
0 голосов
/ 14 июня 2011

Ответ DCookie не решает многосессионную обработку (это просто исправление синтаксиса FOR UPDATE). Если вы не будете манипулировать диапазоном номеров строк, каждый экземпляр службы, если вы собираетесь выбирать, обновляет одни и те же строки. Если вы выполните that_for_update_select в двух сеансах, второй будет ждать, пока первый не завершит транзакцию. Параллельная обработка будет иллюзией.

Я бы рассмотрел эффективную массовую обработку вместе с for update skip locked подходом. Мой ответ ниже:

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;

Возможная длинная транзакция может быть недостатком. Рассмотрите возможность использования Oracle Streams Advanced Queuing (DBMS_AQ) в качестве альтернативы этому решению.

...