Oracle: приведение VARCHAR2 и CLOB к одному типу без усечения

В приложении, которое поддерживает MS SQL Server, MySQL и Oracle, есть таблица со следующими соответствующими столбцами (типы, показанные здесь, относятся к Oracle):

ShortText VARCHAR2(1700) indexed
LongText CLOB

Приложение хранит значения длиной 850 символов или меньше в формате ShortText и более длинные — в формате LongText. Мне нужно создать представление, которое возвращает эти данные, в каком бы столбце они ни находились. Это работает для SQL Server и MySQL:

SELECT
  CASE
    WHEN ShortText IS NOT NULL THEN ShortText
    ELSE LongText
  END AS TheValue
FROM MyTable

Однако в Oracle он генерирует эту ошибку:

ORA-00932: inconsistent datatypes: expected CHAR got CLOB 

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

Это работает на Oracle:

SELECT
  CASE
    WHEN ShortText IS NOT NULL THEN TO_CLOB(ShortText)
    ELSE LongText
  END AS TheValue
FROM MyTable

Тем не менее, производительность удивительно ужасна. Запрос, возвращающий LongText напрямую, занял 70-80 мс для примерно 9 000 строк, но приведенная выше конструкция заняла от 30 до 60 секунд, что неприемлемо.

So:

  1. Существуют ли какие-либо другие типы Oracle, к которым я мог бы принудить оба столбца, которые могут содержать столько же данных, сколько CLOB? В идеале что-то более текстовое, например LONGTEXT в MySQL или NTEXT в SQL Server (или, что еще лучше, NVARCHAR(MAX))?
  2. Любые другие подходы, на которые я должен обратить внимание?

Некоторые особенности, в частности те, которые запросил @Guido Leenders:

Oracle version: Oracle Database 11g 11.2.0.1.0 64bit Production
Not certain if I was the only user, but the relative times are still striking.

Stats for the small table where I saw the performance I posted earlier:
  rowcount: 9,237
  varchar column total length: 148,516
  clob column total length: 227,020

person enigment    schedule 23.11.2013    source источник
comment
Если бы вы поместили весь короткий текст в длинный текст и у вас был бы только столбец CLOB, это ускорило бы работу?   -  person Ben    schedule 23.11.2013
comment
Легче да, но, как указано выше, короткий индексируется, чего я довольно CLOB не могу. Его также можно объединить, и он поддерживает тесты на равенство в предложении WHERE. Это часть некоторой общей инфраструктуры хранения, где подмножества данных используются для определенных целей, поэтому эти функции ценны, когда известно, что конкретные рассматриваемые данные всегда помещаются в короткий столбец.   -  person enigment    schedule 24.11.2013
comment
Обратите внимание, что Oracle 12c увеличивает максимальный размер varchar2 до 32 767.   -  person David Aldridge    schedule 25.11.2013
comment
@David Aldridge: Полезно знать, это будет полезно в будущем, но на данный момент приложение также должно поддерживать более старые версии Oracle.   -  person enigment    schedule 25.11.2013


Ответы (1)


to_clob довольно дорог, поэтому старайтесь его избегать. Но я думаю, что он должен работать достаточно хорошо для строк 9K. Следующий тестовый пример, основанный на одном из разработанных нами приложений, имеет аналогичное поведение модели данных:

create table bubs_projecten_sample
( id number
, toelichting varchar2(1700)
, toelichting_l clob
)

begin
  for i in 1..10000
  loop
    insert into bubs_projecten_sample
    ( id
    , toelichting
    , toelichting_l
    )
    values
    ( i
    , case when mod(i, 2) = 0 then 'short' else null end
    , case when mod(i, 2) = 0 then rpad('long', i, '*') else null end
    )
    ;
  end loop;
  commit;
end;

Теперь убедитесь, что все в кеше и грязных блоках записано:

select *
from   bubs_projecten_sample

Тест производительности:

create table bubs_projecten_flat
as
select id
,      to_clob(toelichting) toelichting_any
from   bubs_projecten_sample
where  toelichting is not null
union all
select id
,      toelichting_l
from   bubs_projecten_sample
where  toelichting_l is not null

Создание таблицы занимает менее 1 секунды на обычном сервере начального уровня, включая запись данных, последовательное получение 17 КБ, физическое чтение 4 КБ. На диске (обратите внимание на rpad) хранится 25К для toelichting и 16М для toelichting_l.

Можете ли вы подробнее рассказать о проблеме?

Убедитесь, что большие CLOB не хранятся в строке. Обычно большие CLOB хранятся в отдельной поддерживаемой системой таблице. Хранение больших CLOB внутри таблицы может сделать обход таблицы с полным сканированием дорогим.

Кроме того, я могу представить, что оба столбца всегда заполняются. У вас все еще есть преимущества индексации, работающей для первых стольких символов. Нужно просто запомнить в таблице с помощью индикатора, колонка CLOB или shortText является ведущей.

В качестве примечания; Я вижу разницу между 850 и 1700. Я бы рекомендовал сделать их равными, но не забудьте проверить, что вы создаете таблицу, используя семантику символов. Это можно сделать на уровне оператора, используя: «varchar2 (850 char)». Обратите внимание, что Oracle фактически создаст столбец размером 850 * 4 байта (по крайней мере, в AL32UTF8 "32" означает "максимум 4 байта на символ"). Удачи!

person Guido Leenders    schedule 25.11.2013
comment
Хммм, не думайте, что встроенное хранилище CLOB или нет, может быть разницей, учитывая относительное время моего запроса выше, который вернул необработанное значение LongText по сравнению с TO_CLOB (LongText). Интересно, почему ваш пример намного быстрее, чем то, что я вижу. Сервер разработки, на который я натыкаюсь, не очень мощный, но и не беспомощный, и производительность в других вещах (например, в необработанной версии запроса LongText) в порядке, если не фантастична. Re длины столбцов, спасибо за ваш отзыв. Не хвастаюсь, но я отправлю это разработчику, который принял эти решения, я сам не так силен в Oracle. - person enigment; 25.11.2013
comment
Не очень важно, но оказывается, что несоответствие размера столбца было артефактом того, как я получил эту информацию, а не самим столбцом. Еще раз спасибо, что указали на это. - person enigment; 25.11.2013
comment
На какой именно версии Oracle вы тестируете производительность? Вы единственный пользователь при бенчмаркинге? Можете ли вы включить в свой вопрос DDL для воссоздания таблицы и точной статистики (количество строк, поле суммы (длина (clob)), сумма (длина (текст))?) Разница в производительности действительно странно. - person Guido Leenders; 26.11.2013
comment
Добавлены детали, которые вы просили, к исходному вопросу, отчасти для того, чтобы они могли быть многострочными. Здесь нет исходного DDL, но спецификации для двух соответствующих столбцов соответствуют заявленным. И спасибо, что отозвались! - person enigment; 27.11.2013
comment
Хм, учитывая статистику, которую вы указали в вопросе, не имеет значения, хранятся ли клобы внутри или снаружи, все они очень маленькие, всего несколько байтов на запись. На 11.2.0.1 я не знаю о проблемах clob, просто вспомните что-то о проблемах с описанием метаданных для исправления типа данных в числовых столбцах (точность/масштаб потеряны). Доступ к SGA не должен быть проблемой и достаточен для ресурсов. Это действительно слишком мало, чтобы поддаваться влиянию других. Может попробовать переписать как СОЮЗ ВСЕ и потом констатировать показатели производительности? Это действительно небольшие данные, и тогда тот же сценарий занимает больше времени. - person Guido Leenders; 27.11.2013
comment
Не слежу за вами по поводу СОЮЗА ВСЕХ, извините, уточните пожалуйста. И хотя игровые данные, в которых я видел такую ​​​​производительность, очень малы, у некоторых клиентов в этой таблице миллионы строк. Они будут получать доступ только к его сегментам за раз, относительно редко через представления, о которых я думаю, каждое из которых ограничено определенными его подмножествами на основе других столбцов, которые индексируются. - person enigment; 27.11.2013
comment
Ненавижу говорить это без объяснений, но сегодня мои результаты немного лучше. Все еще страшно, но не так страшно. SELECT TO_CLOB(ShortText) занял около 13 секунд для более 9000 строк, но в целом это не то, что приложение будет делать в реальном мире. Ограничено более реалистично, чтобы вернуть 1k+ строк, это заняло ~ 1100 мс, все еще плохо, но лучше. Ясно одно: без TO_CLOB первый запуск запроса выполняется медленнее, но последующие кэшируются или иным образом оптимизируются и значительно ускоряются. TO_CLOB блокирует это, он никогда не работает быстрее. Для реального использования это неудачно. - person enigment; 28.11.2013
comment
Протестировано на сервере SE1 11.2.0.3 здесь, таблица с 1,3 млн строк, как описано. Прохождение его запросом с объединением всего занимает 911 мс (из кеша). Второе выполнение 617 мс. Помните, что Oracle может хорошо работать только при наличии достаточного объема памяти. Это связано с процессором. При ограничении памяти он будет плохо работать. Под «объединением всех» я имею в виду: используйте select id , to_clob(ShortText) из MyTable, где ShortText не является нулевым, union all select id , LongText из MyTable, где LongText не равен нулю - person Guido Leenders; 28.11.2013