Заставить базу данных обрабатывать строку как SQL - PullRequest
0 голосов
/ 07 октября 2011

Есть ли способ заставить мои dbms (оракул) обрабатывать строку как код SQL?

Например, в запросе select num from numbers where num between '5 and 7' я бы хотел, чтобы '5 and 7' оценивалось как SQL.

Edit:

Вот так выглядит мой запрос:

select num from tbl_1 
where num between (select min(num) from tbl_2) 
                    and 
                  (select max(num) from tbl_2);

Мне интересно, есть ли способ сделать это только с одним подзапросом.

Ответы [ 5 ]

5 голосов
/ 07 октября 2011

Не. Просто. Не. Вы действительно хотите принести в мир больше чудовищ , таких как ?

2 голосов
/ 07 октября 2011

Невозможно перевести строку в критерии в SQL (по уважительным причинам).

Однако, если вы не можете сократить количество подзапросов, следующий запрос будет эквивалентен вашему.

SELECT num
FROM        tbl_1 t1
       JOIN (SELECT MIN(num) min_num, MAX(num) max_num FROM tbl_2) t2
         ON t1.num BETWEEN t2.min_num AND t2.max_num

Однако, даже если tbl_2.num не проиндексирован, улучшение производительности настолько мало, что не стоит потери читабельности.Я поместил 10 000 последовательных значений в tbl_2 и 100 000 последовательных значений в tbl_1 и выполнил каждый запрос 1000 раз.Разница в общем времени выполнения составляла менее 5 миллисекунд (с точностью до погрешности).

Мой тест:

CREATE TABLE tbl_1 (num NUMBER)
/

CREATE TABLE tbl_2 (num NUMBER)
/

INSERT INTO tbl_1
   SELECT     LEVEL
   FROM       DUAL
   CONNECT BY LEVEL <= 100000
   /

INSERT INTO tbl_2
   SELECT     LEVEL
   FROM       DUAL
   CONNECT BY LEVEL <= 10000
   /

ANALYZE TABLE tbl_1 COMPUTE STATISTICS
/
ANALYZE TABLE tbl_2 COMPUTE STATISTICS
/

DECLARE
   v_repititions CONSTANT PLS_INTEGER := 1000;

   CURSOR cur_old IS
      SELECT num
      FROM   tbl_1
      WHERE  num BETWEEN (SELECT MIN(num) FROM tbl_2) 
                     AND (SELECT MAX(num) FROM tbl_2);

   r_old         cur_old%ROWTYPE;

   CURSOR cur_new IS
      SELECT num
      FROM        tbl_1 t1
             JOIN (SELECT MIN(num) min_num, MAX(num) max_num FROM tbl_2) t2
               ON t1.num BETWEEN t2.min_num AND t2.max_num;

   r_new         cur_new%ROWTYPE;
   i             PLS_INTEGER;
   v_start_time  timestamp;
   v_end_time    timestamp;
BEGIN
   v_start_time   := SYSTIMESTAMP;

   FOR i IN 1 .. v_repititions LOOP
      FOR r_old IN cur_old LOOP
         NULL;
      END LOOP;
   END LOOP;

   v_end_time     := SYSTIMESTAMP;
   DBMS_OUTPUT.put_line('Old Query: ' || TO_CHAR(v_end_time - v_start_time));
   v_start_time   := SYSTIMESTAMP;

   FOR i IN 1 .. v_repititions LOOP
      FOR r_new IN cur_new LOOP
         NULL;
      END LOOP;
   END LOOP;

   v_end_time     := SYSTIMESTAMP;
   DBMS_OUTPUT.put_line('New Query: ' || TO_CHAR(v_end_time - v_start_time));
END;
/
0 голосов
/ 07 октября 2011

двумя способами:

Не предпочитается
1) Динамический SQL: создайте одну строку, объединяющую все, что EXECUTE это. Обратите внимание, что это уязвимо для инъекций

Preferred
2) Разобрать входную строку для двух параметров 5 и 7 по разные стороны от разделителя «и», а затем использовать их в качестве параметров в

 SELECT... WHERE NUM BETWEEN @pMin AND @pMax
0 голосов
/ 07 октября 2011

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

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

0 голосов
/ 07 октября 2011

Я не думаю, что Oracle может делать то, что вы хотите, но вы можете написать

select num
from numbers
where num between &1 and &2;

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


Исходя из вашего комментария, я предполагаю, что у вас есть имя столбца range_vals, в котором хранятся такие значения, как '1 AND 2'

select num
from numbers,
  (select instr(range_vals,' ',1,1) as low_pos, instr(range_vals,' ',2,1) as hi_pos, range_vals
  from table_of_range_vals
  where [some condition]) range_subq
where numbers.num between to_number(substr(range_subq.range_vals,1,low_pos)) --parse out the low end of the range
                  and     to_number(substr(range_subq.range_vals,hi_pos,length(range_subq.range_vals))) --parse out the high-end of the range.
;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...