#oracle #oracle11g #oracle12c
Вопрос:
Прежде всего, я знаю об основах.
select to_number('A231') from dual;
—это не сработает, но
select to_char('123') from dual;
— это сработает
select to_number('123') from dual;
— это тоже сработает
На самом деле в моем пакете у нас есть 2 таблицы A(X number
) и B(Y varchar)
есть много столбцов, но нас беспокоит только X
и Y
. X
содержит только числовые значения, такие как 123,456
и т. Д., Но Y содержит некоторые string
и некоторые number
, например '123','HR123','Hello'
. Мы должны объединить эти 2 таблицы. его устаревшее приложение, поэтому мы не можем изменять таблицы и столбцы. До этого времени состояние ниже работало нормально
to_char(A.x)=B.y;
Но так как есть индекс по Y, команда performance предложила нам сделать
A.x=to_number(B.y);
это в dev env.
Мой вопрос в том, при каких обстоятельствах этот запрос выдаст ошибку? если он выберет '123'
определенно, он даст 123
. но если он выберет 'AB123'
, то потерпит неудачу. может ли это потерпеть неудачу? может ли он выбирать 'AB123'
, даже когда он соединяется с другими table
.
Комментарии:
1. 1. Как ваш пост, кажется, что индекс находится в A. x, а не в Y. 2. Ваша команда по производительности права , но это только в теории, и в ваших реальных данных кажется, что у B. y их много
not_number_string
, так что вам не следует поступать так, как они предлагают. 3. Что касается вашего вопроса, ваш запрос всегда будет выдавать ошибки до тех пор, пока в вашей таблице B естьnot_number_string
y
столбец.2. Команда исполнителей сделала все наоборот. Их предложение даже предотвратило бы использование существующего индекса. Они должны были предложить добавить индекс функции
A ( to_char(x) )
.3. выберите to_char(‘123’) из dual;— это будет работать Да, но это не имеет смысла. ‘123’ (заключенный в одинарные кавычки) уже является символьной строкой. Так что нет причин передавать его TO_CHAR. Это просто заставляет oracle выполнять подразумеваемый TO_NUMBER, чтобы создать правильный тип данных, вводимый в TO_CHAR.
Ответ №1:
может ли это потерпеть неудачу?
ДА. Он должен пропустить каждую строку, TO_NUMBER
прежде чем сможет проверить, соответствует ли она условию фильтра. Поэтому, если у вас есть какая-либо одна строка, в которой она потерпит неудачу, она всегда будет терпеть неудачу.
Из Oracle 12.2 (поскольку вы отметили Oracle 12) вы можете использовать:
SELECT *
FROM A
INNER JOIN B
ON (A.x = TO_NUMBER(B.y DEFAULT NULL ON CONVERSION ERROR))
В качестве альтернативы, добавьте индекс TO_CHAR(A.x)
и используйте свой исходный запрос:
SELECT *
FROM A
INNER JOIN B
ON (TO_CHAR(A.x) = B.y)
Также обратите внимание: наличие индекса B.y
не означает, что индекс будет использоваться. Если вы выполняете фильтрацию TO_NUMBER(B.y)
(с ошибкой преобразования по умолчанию или без нее), вам потребуется индекс на основе функции TO_NUMBER(B.Y)
для используемой функции. Вам следует профилировать запросы и проверять планы объяснений, чтобы узнать, есть ли какие-либо улучшения или изменения в использовании индексов.
Ответ №2:
Никогда не преобразуйте VARCHAR2
столбец, который может содержать немумерные строки to_number
.
Это может частично сработать, но в конечном итоге окончательно потерпит неудачу.
Небольшой Пример
create table a as
select rownum X from dual connect by level <= 10;
create table b as
select to_char(rownum) Y from dual connect by level <= 10
union all
select 'Hello' from dual;
Это может сработать (так как вы ограничиваете строки, чтобы преобразование работало; если вам повезет и Oracle выберет правильный план выполнения; что вероятно, но не гарантировано;)
select *
from a
join b on A.x=to_number(B.y)
where B.y = '1';
Но это не удастся
select *
from a
join b on A.x=to_number(B.y)
ORA-01722: invalid number
Производительность
Но так как на Y есть индекс, команда performance предложила нам сделать A. x=to_number(B. y);
Вы должны изменить команду, как если бы вы использовали функцию для индекса столбца ( to_number(B.y)
), которую нельзя использовать.
Напротив, ваш исходный запрос может идеально использовать следующие индексы:
create index b_y on b(y);
create index a_x on a(x);
Запрос
select *
from a
join b on to_char(A.x)=B.y
where A.x = 1;
План Выполнения
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 5 | 1 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 5 | 1 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN| A_X | 1 | 3 | 1 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN| B_Y | 1 | 2 | 0 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"."X"=1)
3 - access("B"."Y"=TO_CHAR("A"."X"))
Комментарии:
1. Даже запрос, который, как вы полагаете, должен работать, может не сработать. СУБД может свободно попытаться сначала объединить все строки и только затем применить
WHERE
предложение.2. Спасибо, что резюмировали это @ThorstenKettner. Это было именно то, на что я хотел указать, см. формулировку: это может частично сработать… В любом случае, я отредактировал предложение, о котором вы упомянули, чтобы не возникало никаких сомнений. Надеюсь, я не был слишком строг против Oracle;)