Возможно ли расширить статические операторы SQL динамическими частями? - PullRequest
2 голосов
/ 31 мая 2011

Я хотел бы создать пакет Oracle, где у меня есть процедура, которая выполняет некоторый динамический SQL.Это не проблема, если я делаю все это динамически с EXECUTE IMMEDIATE, но было бы лучше, если бы статические части запроса могли быть закодированы как статические (для проверки времени компиляции).

Пример полностью динамическогоquery:

-- v_stmt is built dynamically.
v_stmt := 'SELECT count(*) FROM <here some joins> WHERE <here some conditions>';
EXECUTE IMMEDIATE v_stmt
  USING v_param1, v_param2
  RETURNING INTO v_count;

Пример того, что я пытался сделать статической частью FROM:

-- v_stmt is built dynamically.
v_stmt := 'SELECT count(*) FROM my_package.my_function(:param1, :param2) WHERE <here some conditions>';
EXECUTE IMMEDIATE v_stmt
  USING v_param1, v_param2
  RETURNING INTO v_count;

FUNCTION my_function(
  i_param1 IN VARCHAR2,
  i_param2 IN NUMBER
)
RETURN SYS_REFCURSOR
AS
  v_cursor SYS_REFCURSOR;
BEGIN
  -- Open a cursor for different queries depending on params.
  IF i_param2 = 1 THEN
      OPEN v_cursor FOR <some static query>;
  ELSE
      OPEN v_cursor FOR <some other static query>;
  END IF;
  RETURN v_cursor;
END;

Это не работает, потому что невозможно выбрать из SYS_REFCURSOR (по крайней мере,это то, что я нашел в Google).

Есть ли способ достичь этой цели?

edit: По запросу, вот несколько примеров:

Статические запросы:

SELECT a.*, ca.CUS_ID FROM adresses a INNER JOIN customer_adresses ca ON (ca.adr_id = a.adr_id);
SELECT p.*, cp.CUS_ID FROM persons p INNER JOIN customer_persons cp ON (cp.per_id = p.per_id);

Затем они динамически расширяются, как в следующих примерах:

-- Checks if there is an adress in the customer where the zip is null.
SELECT count(*) FROM <static adresses query> q WHERE q.cus_id = :param1 AND a.zip IS NULL;
-- Checks if there is at least one person in the customer.
SELECT count(*) FROM <static persons query> q WHERE q.cus_id = :param1;

Ответы [ 3 ]

2 голосов
/ 01 июня 2011

Извините, но зачем это нужно делать? Кажется, вы слишком усложняете ситуацию, введя функцию, которая будет возвращать различные типы данных / таблиц в зависимости от списка параметров. Очень запутанный ИМО. Кроме того, вы должны где-то выполнить работу, вы просто пытаетесь скрыть ее в этой функции (внутри, если param1 = this, затем x, если param1 = that, тогда y ...)

Кроме того, даже если бы вы реализовали функцию курсора (даже конвейерную), это было бы плохой идеей в этом случае, потому что вы заставляли бы Oracle выполнять работу, которую не обязательно делать (игнорировать все переключение контекста на данный момент). Чтобы просто подсчитать, нужно, чтобы Oracle брал каждый результат по каждой строке, а затем подсчитывал. Много раз Oracle может просто выполнить быстрое полное сканирование индекса, чтобы получить счетчик (в зависимости от запроса, конечно). И часто один и тот же запрос, запускаемый несколько раз, не обязательно будет выполнять всю работу каждый раз, когда блоки обнаруживаются в буферном кеше. Я бы посоветовал вам запустить подсчет несколько раз, используя прямой SQL против использования функции, возвращающей курсор. Вы можете быть удивлены. И, насколько мне известно (отметьте это), новый кэш результатов функции 11g не будет работать с конвейерными функциями или функцией, возвращающей курсор ref (наряду с другими проблемами, такими как аннулирование из-за использования таблиц).

Итак, что я говорю, почему бы просто не сделать: выберите count (1) в v_variable из ...;

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

1 голос
/ 31 мая 2011

Возможно, вы захотите открыть запрос в функции 1, а затем передать результаты в виде таблицы в функцию 2, которая затем добавит предложение where к этой «таблице»

В этом случае вы захотите переписать вашу функцию1 как конвейерную табличную функцию

v_stmt := 'SELECT count(*) FROM table(my_package.my_function(:param1, :param2)) WHERE <here some conditions>';
EXECUTE IMMEDIATE v_stmt
  USING v_param1, v_param2
  RETURNING INTO v_count;

CREATE TYPE object_row_type AS OBJECT (
  OWNER              VARCHAR2(30),
  OBJECT_TYPE        VARCHAR2(18),
  OBJECT_NAME        VARCHAR2(30),
  STATUS             VARCHAR2(7)
);


CREATE TYPE object_table_type AS TABLE OF object_row_type;


FUNCTION my_function(
  i_param1 IN VARCHAR2,
  i_param2 IN NUMBER
)
RETURN object_table_type PIPELINED AS
BEGIN 
0 голосов
/ 02 июня 2011

Вы можете проверять выражения во время компиляции с помощью Фильтр выражений Oracle .

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

...