Как указать список переменных выражений в запросе Pro*C?

У меня проблема с запросом Pro*C, который я пытаюсь оптимизировать.

Чтобы объяснить, наше приложение ищет строки в огромной базе данных. Эти строки существуют на нескольких языках, и старый код выбирал строку для каждого языка в массиве. Теперь, когда эти запросы являются наиболее трудоемкой частью нашего приложения, я хотел сделать только один запрос, который записывает непосредственно в массив.

Коды языков представляют собой двухбуквенные коды ISO-639 (en для английского, fr для французского).

Старый способ (это всего лишь упрощенный код, чтобы показать намерение)

struct ROW arr[MAX_LAN];
struct ROW_IND arr_ind[MAX_LAN];
uint_t LanIdx;
for(LanIdx=0; LanIdx<MAX_LAN; LanIdx++) {
  EXEC SQL SELECT *  /* Don't look at the *, it's for obfuscation only */
      INTO :arr[LanIdx]:arr_ind[LanIdx]
      FROM table WHERE id=:uniqid AND language=:LanCode[LanIdx];
}

Я хотел бы сделать что-то вроде этого:

EXEC SQL SELECT *  /* Don't look at the *, it's for obfuscation only */
    INTO :arr:arr_ind
    FROM table WHERE id=:uniqid AND language IN (:LanCodes);

но не знаю, как мне определить LanCodes.

Он работает с постоянным (время компиляции) списком, подобным этому

EXEC SQL SELECT *  /* Don't look at the *, it's for obfuscation only */
    INTO :arr:arr_ind
    FROM table WHERE id=:uniqid AND language IN ('en','fr','de');

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

Если я напишу что-то вроде

char LanCodes[MAX_LANS*5];
sprintf(LanCodes, "%s", LanCode[LanIdx]);

EXEC SQL SELECT *  /* Don't look at the *, it's for obfuscation only */
    INTO :arr:arr_ind
    FROM table WHERE id=:uniqid AND language IN (:LanCodes);

это работает, только если в строке есть 1 код языка.

Итак, мой вопрос: кто-нибудь знает, как это сделать? Документация Oracle настолько велика, что я не знаю, где ее посмотреть. Я пробовал разные способы, но ни один не работал.

EDIT Хорошо, я нашел работающее решение. Это не элегантно, это не продвинуто, но работает хорошо. Я помещаю список предложений OR в свой запрос, и он возвращает то, что мне нужно, в той форме, которая мне нужна.

EXEC SQL SELECT *  /* Don't look at the *, it's for obfuscation only */
    INTO :arr:arr_ind
    FROM table WHERE id=:uniqid AND (
                language=:v1[ 0] OR
                language=:v1[ 1] OR
                language=:v1[ 2] OR
                language=:v1[ 3] OR
                language=:v1[ 4] OR
                language=:v1[ 5] OR
                language=:v1[ 6] OR
                language=:v1[ 7] OR
                language=:v1[ 8] OR
                language=:v1[ 9] OR
                language=:v1[10] OR
                language=:v1[11] OR
                language=:v1[12] OR
                language=:v1[13] OR
                language=:v1[14] OR
                language=:v1[15] OR
                language=:v1[16] OR
                language=:v1[17] OR
                language=:v1[18] OR
                language=:v1[19] OR
                language=:v1[20] OR
                language=:v1[21] OR
                language=:v1[22] OR
                language=:v1[23] OR
                language=:v1[24] OR
                language=:v1[25] OR
                language=:v1[26] OR
                language=:v1[27] OR
                language=:v1[28] OR
                language=:v1[29] OR
                language=:v1[30]);

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


person Patrick Schlüter    schedule 17.11.2009    source источник
comment
Ознакомьтесь с этим вопросом/ответом ( stackoverflow.com/questions/1745811/ )   -  person pmg    schedule 17.11.2009
comment
Вам все равно придется анализировать предложение IN во время выполнения с использованием динамического SQL.   -  person DCookie    schedule 17.11.2009


Ответы (4)


Вероятно, это статья AskTom может тебе помочь.

person Egor Rogov    schedule 17.11.2009
comment
Спасибо, похоже, что-то попробовать. Продолжите в пятницу, чтобы увидеть, работает ли это (и если это быстрее). - person Patrick Schlüter; 18.11.2009

Вы не можете сделать это без Oracle Dynamic SQL . Вам нужно будет создать предложение IN во время выполнения и EXECUTE IMMEDIATE. По крайней мере, вы можете использовать метод 1 на основе ваших запросов.

person DCookie    schedule 17.11.2009
comment
Я не уверен, что с методом 3 это сведется к той же проблеме. Если я сделаю OPEN C, ИСПОЛЬЗУЯ :LanCodes, будет ли это работать? Одна вещь, которую я забыл упомянуть, заключается в том, что я не могу создать динамическую инструкцию в строке с изменяющимися языковыми кодами, так как это взорвет кеш операторов базы данных. - person Patrick Schlüter; 17.11.2009
comment
Затем, я думаю, вам нужно создать временную таблицу, заполнить ее значениями IN и присоединиться к ней. - person DCookie; 17.11.2009
comment
Спасибо, но это еще хуже (потребуются месяцы, чтобы передать что-то подобное нашим администраторам баз данных, мы здесь в администрации, это потребует много бумажной работы). Я думаю, что мне придется жить с нашим текущим решением (что, кстати, неплохо). Это было только для того, чтобы немного выиграть от выбора, который является доминирующей частью приложения. - person Patrick Schlüter; 17.11.2009
comment
Кроме того, вы что-то теряете, если вам также приходится вставлять значения во временную таблицу. - person DCookie; 17.11.2009

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

create table permute (
  id number,
  lang char(2)
);
create index permute_p1 on permute ( lang, id );
insert into permute ( id, lang ) values ( 1, 'en' );
insert into permute ( id, lang ) values ( 2, 'en' );
insert into permute ( id, lang ) values ( 2, 'fr' );
...

Все, что вам нужно сделать, это выбрать правильное значение «ID» 2, 3, 4 ... и поместить его в соединение.

person Philip Schlump    schedule 18.11.2009
comment
Это невозможно. Это для ЕС, так что мы должны работать с 23 официальными языками (+2 других в ближайшем будущем), так что у нас их 23! возможно, это 25x10 ^ 7, таблица будет почти такой же большой (или даже больше), как наша основная база данных. - person Patrick Schlüter; 18.11.2009

... Main String:= 'Выберите * ИЗ таблицы WHERE id=:uniqid AND language IN'; -- можно разделить на две части для размещения :uniqd ... Выберите Language_code в v_string из x_table; цикл Скопируйте и объедините v_string в LanCode_String и с помощью ' ', ; конечная петля; .. Concat Lancode для основной строки. .. Подготовьте и выполните основную строку.

person Lovell    schedule 19.11.2009
comment
Да, это в основном то, что предложил DCookie. Проблема с этим подходом заключается в том, что он плохо влияет на кеш операторов Oracle. Переполнение этого кеша глобально снижает производительность (некоторое время назад это имело эффект для чего-то другого). Мой вопрос был только в том, чтобы немного сэкономить на поездках туда и обратно. - person Patrick Schlüter; 19.11.2009