Обновления PL / SQL rownum - PullRequest
       12

Обновления PL / SQL rownum

1 голос
/ 18 марта 2011

Я работаю над базой данных с парой таблиц. Они

      districts table
      PK district_id

      student_data table
      PK study_id
      FK district_id

      ga_data table
      PK study_id
      district_id

Таблица ga_data - это данные, которые я добавляю. И таблица student_data, и ga_data содержат 1,3 миллиона записей. Идентификаторы study_id равны 1 к 1 между двумя таблицами, но ga_data.district_id имеют значение NULL и требуют обновления. У меня проблемы со следующим PL / SQL:

update ga_data
set district_id = (select district_id from student_data
where student_data.study_id = ga_data.study_id)
where ga_data.district_id is null and rownum < 100;

Мне нужно делать это постепенно, поэтому мне нужен rownum. Но правильно ли я это использую? После выполнения запроса несколько раз он обновлял только около 8 000 записей из 1,3 миллиона (должно быть около 1,1 миллиона обновлений, так как некоторые из district_ids являются нулевыми в student_data). Спасибо!

Ответы [ 3 ]

5 голосов
/ 18 марта 2011

ROWNUM просто отрубает запрос после первых n строк. У вас есть несколько строк в STUDENT_DATA, которые имеют NULL для DISTRICT_ID. Таким образом, после нескольких запусков ваш запрос может застрять в колее, возвращая те же 100 записей QA_DATA, все из которых соответствуют одной из этих неприятных строк STUDENT_DATA.

Таким образом, вам нужен какой-то механизм, чтобы гарантировать, что вы постепенно продвигаетесь по таблице QA_DATA. Столбец флага будет одним из решений. Разделение запроса так, чтобы он совпадал с другим набором STUDENT_ID, - это другое.

Непонятно, почему вы должны делать это партиями по 100, но, возможно, самый простой способ сделать это - использовать BULK PROCESSING (по крайней мере, в Oracle: этот синтаксис PL / SQL не будет работать в MySQL).

Вот некоторые тестовые данные:

SQL> select district_id, count(*)
  2  from student_data
  3  group by district_id
  4  /

DISTRICT_ID   COUNT(*)
----------- ----------
   7369        192
   7499        190
   7521        192
   7566        190
   7654        192
   7698        191
   7782        191
   7788        191
   7839        191
   7844        192
   7876        191
   7900        192
   7902        191
   7934        192
   8060        190
   8061        193
   8083        190
   8084        193
   8085        190
   8100        193
   8101        190
               183

22 rows selected.

SQL> select district_id, count(*)
  2  from qa_data
  3  group by district_id
  4  /

DISTRICT_ID   COUNT(*)
----------- ----------
                  4200

SQL>

Этот анонимный блок использует предложение LIMIT «Массовая обработка» для пакетирования результирующего набора в куски по 100 строк.

SQL> declare
  2      type qa_nt is table of qa_data%rowtype;
  3      qa_recs qa_nt;
  4
  5      cursor c_qa is
  6          select qa.student_id
  7                 , s.district_id
  8          from qa_data qa
  9                  join student_data s
 10                      on (s.student_id = qa.student_id);
 11  begin
 12      open c_qa;
 13
 14      loop
 15          fetch c_qa bulk collect into qa_recs limit 100;
 16          exit when qa_recs.count() = 0;
 17
 18          for i in qa_recs.first()..qa_recs.last()
 19          loop
 20              update qa_data qt
 21                  set qt.district_id = qa_recs(i).district_id
 22                  where qt.student_id = qa_recs(i).student_id;
 23          end loop;
 24
 25      end loop;
 26  end;
 27  /

PL/SQL procedure successfully completed.

SQL>

Обратите внимание, что эта конструкция позволяет нам выполнить дополнительную обработку выбранных строк перед выпуском обновления. Это удобно, если нам нужно применять сложные исправления программно.

Как видите, данные в QA_DATA теперь совпадают с данными в STUDENT_DATA

SQL> select district_id, count(*)
  2  from qa_data
  3  group by district_id
  4  /

DISTRICT_ID   COUNT(*)
----------- ----------
   7369        192
   7499        190
   7521        192
   7566        190
   7654        192
   7698        191
   7782        191
   7788        191
   7839        191
   7844        192
   7876        191
   7900        192
   7902        191
   7934        192
   8060        190
   8061        193
   8083        190
   8084        193
   8085        190
   8100        193
   8101        190
               183

22 rows selected.

SQL>
0 голосов
/ 19 марта 2011

Если это однократное преобразование, вам следует рассмотреть совершенно другой подход. Воссоздайте таблицу как объединение ваших двух таблиц. Я обещаю, что вы будете смеяться вслух, когда поймете, насколько быстро он сравнивается со всеми смешными обновлениями по 100 строк за раз.

create table new_table as
   select study_id
         ,s.district_id
         ,g.the_remaining_columns_in_ga_data
    from student_data s
    join ga_data      g using(study_id);

   create indexes, constraints etc 
   drop table ga_data;
   alter table new_table rename to ga_data;

Или если это не однократное преобразование, или вы не можете заново создать / удалить таблицы, или вам просто хочется потратить несколько дополнительных часов на загрузку данных:

merge
 into ga_data      g
using student_data s
   on (g.study_id  = s.study_id)
when matched then
   update
      set g.district_id = s.district_id;

Последнее утверждение также может быть переписано как обновляемый вид, но я лично никогда не использую их.

Удаление / отключение индексов / ограничений на ga_data.district_id перед запуском слияния и последующее их воссоздание улучшит производительность.

0 голосов
/ 18 марта 2011

Это странное требование обновлять только 100 строк одновременно. Почему это так?

В любом случае, так как district_id в student_data может быть нулевым, возможно, вы обновляете те же 100 строк снова и снова.

Если вы расширите свой запрос, чтобы убедиться, что существует не нулевой district_id, вы можете оказаться там, где хотите:

update ga_data
set district_id = (
  select district_id 
  from student_data
  where student_data.study_id = ga_data.study_id
)
where ga_data.district_id is null 
and exists (
  select 1
  from student_data
  where student_data.study_id = ga_data.study_id
  and district_id is not null
)
and rownum < 100;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...