Как я могу ускорить запросы против огромных таблиц хранилища данных с эффективный датированными данными?

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

Это требует запросов, которые вложили, выбирает в них, для получения новой / текущей строки. Они и эффективные датированный, и в рамках каждой даты (бросок ко дню) у них может быть эффективная последовательность. Таким образом для получения текущей записи для user_id=123, Я должен сделать это:

select * from sometable st
where st.user_id = 123
and st.effective_date = (select max(sti.effective_date) 
  from sometable sti where sti.user_id = st.user_id)
and st.effective_sequence = (select max(sti.effective_sequence) 
  from sometable sti where sti.user_id = st.user_id
  and sti.effective_date = st.effective_date)

Существует феноменальное количество индексов на этих таблицах, и я не могу найти ничто больше, что ускорило бы мои запросы.

Моя проблема состоит в том, что я часто времена хотят получить данные о человеке от этих таблиц для, возможно, 50 user_ids, но когда я присоединяюсь к своим таблицам, имеющим только несколько записей в них с несколькими из этих таблиц PeopleSoft, дела просто идут к дерьму.

Таблицы PeopleSoft находятся на удаленной базе данных, к которой я получаю доступ через ссылку базы данных. Мои запросы имеют тенденцию быть похожими на это:

select st.* from local_table lt, sometable@remotedb st
where lt.user_id in ('123', '456', '789')
and lt.user_id = st.user_id
and st.effective_date = (select max(sti.effective_date) 
  from sometable@remotedb sti where sti.user_id = st.user_id)
and st.effective_sequence = (select max(sti.effective_sequence) 
  from sometable@remotedb sti where sti.user_id = st.user_id
  and sti.effective_date = st.effective_date)

Вещи становятся еще хуже, когда я должен присоединиться к нескольким таблицам PeopleSoft со своей локальной таблицей. Производительность просто недопустима.

Каковы некоторые вещи, которые я могу сделать для улучшения производительности? Я попробовал подсказки запроса, чтобы гарантировать, что моя локальная таблица соединена со своим партнером в PeopleSoft сначала, таким образом, она не пытается присоединиться ко всем своим таблицам вместе перед сужением его к корректному user_id. Я попробовал LEADING подскажите и играл вокруг с подсказками, которые пытались продвинуть обработку к удаленной базе данных, но объяснить план был затенен и просто сказал 'УДАЛЕННЫЙ' для нескольких из операций, и я понятия не имел, что продолжалось.

Принятие я не имею права изменять PeopleSoft и местоположение моих таблиц, подсказки мой лучший выбор? Если я присоединялся к локальной таблице с четырьмя удаленными таблицами и локальной таблице, к которой присоединяются с двумя из них, как я отформатирую подсказку так, чтобы моя локальная таблица (который является очень маленьким - на самом деле, я могу просто сделать, встроенное представление, чтобы иметь мою локальную таблицу только является user_ids, я интересуюсь), присоединен сначала с каждым из удаленных?

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

7
задан aw crud 24 June 2010 в 20:29
поделиться

10 ответов

Помогает ли вообще рефакторинг запроса подобным образом?

SELECT *
  FROM (SELECT st.*, MAX(st.effective_date) OVER (PARTITION BY st.user_id) max_dt,
                     MAX(st.effective_sequence) OVER (PARTITION BY st.user_id, st.effective_date) max_seq
          FROM local_table lt JOIN sometable@remotedb st ON (lt.user_id = st.user_id)
         WHERE lt.user_id in ('123', '456', '789'))
 WHERE effective_date = max_dt
   AND effective_seq = max_seq;

Я согласен с @Mark Baker, что объединение производительности через DB Links действительно может быть отстойным, и вы, скорее всего, будете ограничены в том, чего вы можете достичь с помощью этого подхода.

4
ответ дан 6 December 2019 в 19:32
поделиться

Один из подходов - использовать PL / SQL-функции во всем. В качестве примера

create table remote (user_id number, eff_date date, eff_seq number, value varchar2(10));

create type typ_remote as object (user_id number, eff_date date, eff_seq number, value varchar2(10));
.
/

create type typ_tab_remote as table of typ_remote;
.
/

insert into remote values (1, date '2010-01-02', 1, 'a');
insert into remote values (1, date '2010-01-02', 2, 'b');
insert into remote values (1, date '2010-01-02', 3, 'c');
insert into remote values (1, date '2010-01-03', 1, 'd');
insert into remote values (1, date '2010-01-03', 2, 'e');
insert into remote values (1, date '2010-01-03', 3, 'f');

insert into remote values (2, date '2010-01-02', 1, 'a');
insert into remote values (2, date '2010-01-02', 2, 'b');
insert into remote values (2, date '2010-01-03', 1, 'd');

create function show_remote (i_user_id_1 in number, i_user_id_2 in number) return typ_tab_remote pipelined is
    CURSOR c_1 is
    SELECT user_id, eff_date, eff_seq, value
    FROM
        (select user_id, eff_date, eff_seq, value, 
                        rank() over (partition by user_id order by eff_date desc, eff_seq desc) rnk
        from remote
        where user_id in (i_user_id_1,i_user_id_2))
    WHERE rnk = 1;
begin
    for c_rec in c_1 loop
        pipe row (typ_remote(c_rec.user_id, c_rec.eff_date, c_rec.eff_seq, c_rec.value));
    end loop;
    return;
end;
/

select * from table(show_remote(1,null));

select * from table(show_remote(1,2));

Вместо того, чтобы передавать user_id индивидуально в качестве параметров, вы можете загрузить их в локальную таблицу (например, глобальную временную таблицу). Затем PL / SQL будет перебирать таблицу, выполняя удаленный выбор для каждой строки в локальной таблице. Ни один запрос не может иметь как локальные, так и удаленные таблицы. Фактически вы бы написали свой собственный код соединения.

4
ответ дан 6 December 2019 в 19:32
поделиться

Один из вариантов - сначала материализовать удаленную часть запроса с использованием общего табличного выражения, чтобы вы могли быть уверены, что из удаленной базы данных извлекаются только релевантные данные. Другим улучшением было бы объединение двух подзапросов против удаленной базы данных в один подзапрос на основе аналитических функций. Такой запрос также может использоваться в вашем текущем запросе. Другие предложения я могу сделать только после того, как поиграю с db.

см. Ниже

with remote_query as
(
    select /*+ materialize */  st.* from sometable@remotedb st
    where st.user_id in ('123', '456', '789')
    and st.rowid in( select first_value(rowid) over (order by effective_date desc, 
                         effective_sequence desc ) from sometable@remotedb st1 
                      where st.user_id=st1.user_id)
)

select lt.*,st.* 
FROM local_table st,remote_query rt
where st.user_id=rt.user_id
3
ответ дан 6 December 2019 в 19:32
поделиться

Вы не упомянули требования к актуальности данных, но одним из вариантов было бы создание материализованных представлений (вы будете ограничены REFRESH COMPLETE, поскольку вы не можете создавать журналы моментальных снимков) в исходной системе), которые содержат данные только для текущей версированной строки таблиц транзакций. Эти материализованные таблицы представлений будут находиться в вашей локальной системе, и к ним можно добавить дополнительную индексацию для повышения производительности запросов.

1
ответ дан 6 December 2019 в 19:32
поделиться

Проблема с производительностью будет заключаться в доступе по ссылке. Что касается части запроса к локальным таблицам, все это выполняется локально, поэтому нет доступа к удаленным индексам, и он извлекает все удаленные данные обратно для тестирования lkocally.

Если бы вы могли использовать материализованные представления в локальной базе данных, обновляемые из базы данных peoplesoft на периодической (еженощной) основе для исторических данных, обращаясь к удаленной базе данных peoplesoft только для сегодняшних изменений (добавление Effective_date = today к вашему предложению where) и объединение двух запросов.

Другой вариант может заключаться в использовании INSERT INTO X SELECT FROM только для удаленных данных, чтобы вывести их во временную локальную таблицу или материализованное представление, а затем второй запрос, чтобы объединить их с вашими локальными данными ... аналогично предложению josephj1989

В качестве альтернативы (хотя могут быть проблемы с лицензированием) попробуйте RAC кластеризовать локальную базу данных с удаленной базой данных peoplesoft.

1
ответ дан 6 December 2019 в 19:32
поделиться

Вместо использования подзапросов вы можете попробовать это. Я не знаю, будет ли Oracle работать с этим лучше или нет, так как я мало использую Oracle.

SELECT
    ST1.col1,
    ST1.col2,
    ...
FROM
    Some_Table ST1
LEFT OUTER JOIN Some_Table ST2 ON
    ST2.user_id = ST1.user_id AND
    (
        ST2.effective_date > ST1.effective_date OR
        (
            ST2.effective_date = ST1.effective_date AND
            ST2.effective_sequence > ST1.effective_sequence
        )
    )
WHERE
    ST2.user_id IS NULL

Другое возможное решение:

SELECT
    ST1.col1,
    ST1.col2,
    ...
FROM
    Some_Table ST1
WHERE
    NOT EXISTS
    (
        SELECT
        FROM
            Some_Table ST2
        WHERE
            ST2.user_id = ST1.user_id AND
            (
                ST2.effective_date > ST1.effective_date OR
                (
                    ST2.effective_date = ST1.effective_date AND
                    ST2.effective_sequence > ST1.effective_sequence
                )
            )
    )
0
ответ дан 6 December 2019 в 19:32
поделиться

Будет ли вариантом создать базу данных, которую вы используете для нестандартных типов, которые вы могли бы обновлять на ночной основе? Если это так, вы можете создать ночной процесс, который будет перемещаться только по самым последним записям.Это избавит от MAX-вещей, которые вы делаете для повседневных запросов, и значительно уменьшит количество или записи.

Кроме того, зависит от того, можете ли вы иметь 1-дневный промежуток между самыми последними данными и тем, что доступно.

Я не очень хорошо знаком с Oracle, поэтому может быть способ получить улучшения, внеся изменения в ваш запрос...

0
ответ дан 6 December 2019 в 19:32
поделиться

Можете ли вы выполнить ETL строк с желаемыми user_id в вашей собственной таблице, создав только необходимые индексы для поддержки ваших запросов и выполнения ваших запросов по ней?

0
ответ дан 6 December 2019 в 19:32
поделиться

Является ли таблица PeopleSoft поставляемой или пользовательской? Вы уверены, что это физическая таблица, а не плохо написанное представление на стороне PS? Если речь идет о поставляемой записи (пример выглядит как PS_JOB или представление, которое на нее ссылается), то, возможно, вы могли бы указать это. PS_JOB - это чудовище с тоннами поставленных индексов, а большинство сайтов добавляют еще больше.

Если вы знаете индексы таблицы, вы можете использовать подсказки Oracle, чтобы указать предпочтительный индекс для использования; иногда это помогает.

Делали ли вы план объяснения, чтобы посмотреть, можете ли вы определить, где проблема? Может быть, там есть cartesian join, полное сканирование таблицы и т.д.?

.
0
ответ дан 6 December 2019 в 19:32
поделиться

Мне кажется, вы имеете дело с измерением типа 2 в хранилище данных. Существует несколько способов реализации измерения типа 2, в основном с такими столбцами, как ValidFrom, ValidTo, Version, Status . Не все они всегда присутствуют, было бы интересно, если бы вы могли опубликовать схему для своей таблицы. Вот пример того, как это может выглядеть (Джон Смит переехал из Индианы в Огайо 24.06.2010)

UserKey  UserBusinessKey State    ValidFrom    ValidTo   Version  Status
7234     John_Smith_17   Indiana  2005-03-20  2010-06-23    1     expired
9116     John_Smith_17   Ohio     2010-06-24  3000-01-01    2     current

Чтобы получить последнюю версию строки, обычно используют

WHERE Status = 'current'

или

WHERE ValidTo = '3000-01-01'

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

или

WHERE ValidTo > CURRENT_DATE

Похоже, что в вашем примере используется ValidFrom (эффективная_дата), поэтому вы вынуждены искать max () , чтобы найти последнюю строку. Взгляните на схему - есть ли в ваших таблицах эквиваленты Status или ValidTo ?

0
ответ дан 6 December 2019 в 19:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: