Объявление нескольких значений в переменных Oracle BIND - PullRequest
3 голосов
/ 09 июля 2011

Я пытаюсь передать несколько значений около 3000 в переменную BIND в командной строке Oracle SQL PLUS, например ..

SELECT JOB
  FROM EMP 
 WHERE JOB IN :JOB -- bind variable value

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


Поскольку это производственная среда, я не могу создавать таблицы, у меня есть разрешение на предложение SELECT.

Нужна дополнительная информация о том, как именно он выполняется, когда я выполняю тот же запрос из среды UNIX-SQL PLUS.

Будет ли запрашиваться ввод значений переменных BIND, или я могу сослаться на файл со значениями как ... : JOB1: = 'МЕНЕДЖЕР' : JOB2: = 'КЛЕРК' : JOB3: = 'БУХГАЛТЕР'

Ответы [ 6 ]

4 голосов
/ 09 июля 2011

Переменные связывания Oracle являются отношением один-к-одному, поэтому вам нужно, чтобы они были определены для каждого значения, которое вы намерены включить в предложение IN:

SELECT JOB
  FROM EMP 
 WHERE JOB IN (:JOB1, :JOB2, :JOB3, ..., :JOB3000)

Вам также нужно знать, что Oracle IN поддерживает максимум 1000 значений, иначе вы получите:

ORA-01795: максимальное количество выражений в списке 1000

Лучшая альтернатива - создать таблицу (производную, временную, фактическую или представление) и присоединиться к ней, чтобы получить нужные значения. IE:

SELECT a.job
  FROM EMP a
  JOIN (SELECT :JOB1 AS col FROM DUAL
        UNION ALL
        SELECT :JOB2 FROM DUAL
        UNION ALL
        SELECT :JOB3 FROM DUAL
        UNION ALL 
        ...
        UNION ALL 
        SELECT :JOB3000 FROM DUAL) b ON b.col = a.job
1 голос
/ 12 июля 2011

Один из способов сделать это в 10g и выше - с использованием подзапроса.

Предположим, :JOB это список значений через запятую. Следующее будет работать:

with job_list as
(select trim(substr(job_list,
                    instr(job_list, ',', 1, level) + 1,
                    instr(job_list, ',', 1, level + 1)
                      - instr (job_list, ',', 1, level) - 1
                   )
            ) as job
  from (select 
               -- this is so it parses right
               ','|| :JOB ||',' job_list
         from dual)
connect by level <= length(:JOB)
                     - length (replace (:JOB, ',', '') ) + 1
)
select * from emp
 where job in (select * from job_list);

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

(Я не придумал это сам по себе - первоначальный титул переходит к вопросу о задании).


:JOB - это переменная связывания, которую необходимо объявить и заполнить, прежде чем ее можно будет использовать. Приведенные ниже инструкции демонстрируют, как это сделать с помощью SQL * Plus.

SQL> variable JOB varchar2(4000);

SQL> exec :JOB := '10, 20';
0 голосов
/ 24 июля 2017

Наша команда только что столкнулась с этой проблемой, и этот запрос очень прост для передачи нескольких значений состояния. Каждое значение разделяется только запятой. Я могу пройти все 52 состояния, если требуется:

SELECT county_code,state_code FROM WMS__ASSET_COUNTY_STATE 
WHERE STATE_CODE IN
(SELECT regexp_substr(:bindstateocde, '[^,]+', 1, LEVEL) token
            FROM dual
            CONNECT BY LEVEL <= length(:bindstateocde) - length(REPLACE(:bindstateocde, ',', '')) + 1) ;
0 голосов
/ 21 сентября 2011

Столкнувшись с подобной проблемой, я придумал грязное решение:

select * from my_table where ',param_1,param_2,param_3,param_4,' LIKE '%,'||my_column||',%'
0 голосов
/ 09 июля 2011

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

SELECT JOB
  FROM EMP
 WHERE JOB IN (SELECT something FROM some_other_table WHERE ... )

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

Теоретически возможно делать то, что вы хотите. Существуют различные способы разработки запроса с большим количеством переменных связывания. В качестве примера я напишу скрипт для запроса представления словаря данных all_objects с использованием 3000 переменных связывания. Я не собираюсь писать сценарий SQL * Plus с 3000 переменными связывания, поэтому вместо этого я написал сценарий Python для создания этого сценария SQL * Plus. Вот оно:

ns = range(1, 9001, 3) # = 1, 4, 7, ..., 8998

# This gets rid of a lot of lines saying 'PL/SQL procedure successfully completed'.
print "SET FEEDBACK OFF;"
print

# Declare the bind variables and give them values.
for i, n in enumerate(ns):
    print "VARIABLE X%04d NUMBER;" % i
    print "EXEC :X%04d := %d;" % (i, n)
    print

query = "SELECT object_name FROM all_objects WHERE"

# Break up the query into lines to avoid SQL*Plus' limit of 2500 characters per line.
chunk_size = 100
for i in range(0, len(ns), chunk_size):
    query += "OR object_id IN (" + ",".join( ":X%04d" % j for j in range(i, i + chunk_size) ) + ")\n"

query = query.replace("WHEREOR", "WHERE") + ";\n"
print query

Затем я смог запустить этот скрипт, перенаправить его вывод в файл .sql, а затем запустить этот файл .sql в SQL * Plus.

Вы можете заметить выше, что я написал « Теоретически это возможно ...». Я положил туда в теории предложение по уважительной причине. Похоже, что запрос действителен, и я не знаю причины, по которой он не должен выполняться. Однако, когда я запустил его на своем экземпляре Oracle (XE 11g Beta), я получил следующий вывод:

SQL> @genquery.sql
SELECT object_name FROM all_objects WHERE object_id IN (:X0000,:X0001,:X0002,:X0
003,:X0004,:X0005,:X0006,:X0007,:X0008,:X0009,:X0010,:X0011,:X0012,:X0013,:X0014
,:X0015,:X0016,:X0017,:X0018,:X0019,:X0020,:X0021,:X0022,:X0023,:X0024,:X0025,:X
0026,:X0027,:X0028,:X0029,:X0030,:X0031,:X0032,:X0033,:X0034,:X0035,:X0036,:X003
7,:X0038,:X0039,:X0040,:X0041,:X0042,:X0043,:X0044,:X0045,:X0046,:X0047,:X0048,:
X0049,:X0050,:X0051,:X0052,:X0053,:X0054,:X0055,:X0056,:X0057,:X0058,:X0059,:X00
60,:X0061,:X0062,:X0063,:X0064,:X0065,:X0066,:X0067,:X0068,:X0069,:X0070,:X0071,
:X0072,:X0073,:X0074,:X0075,:X0076,:X0077,:X0078,:X0079,:X0080,:X0081,:X0082,:X0
083,:X0084,:X0085,:X0086,:X0087,:X0088,:X0089,:X0090,:X0091,:X0092,:X0093,:X0094
,:X0095,:X0096,:X0097,:X0098,:X0099)
*
ERROR at line 1:
ORA-03113: end-of-file on communication channel
Process ID: 556
Session ID: 137 Serial number: 29

Ошибка ORA-03113 указывает на сбой процесса на сервере.

Я попробовал несколько вариантов этого:

  • вообще не использует переменные связывания (т. Е. Непосредственно вводит значения)
  • не использует IN списки, то есть пишет SELECT ... FROM all_objects WHERE object_id=:X0000 OR object_id=:X0001 OR ...,
  • с использованием подхода OMG Ponies,
  • используя подход OMG Ponies без использования переменных связывания,
  • копирование данных из all_objects в таблицу и запрос их вместо этого.

Все вышеперечисленные подходы вызвали ошибку ORA-03113.

Конечно, я не знаю, пострадают ли другие выпуски Oracle от этих сбоев (у меня нет доступа к другим выпускам), но это не сулит ничего хорошего.

РЕДАКТИРОВАТЬ: Вы спрашиваете, можете ли вы достичь что-то вроде SELECT JOB FROM EMP WHERE JOB IN (:JOB). Краткий ответ на этот вопрос - нет. Сообщение об использовании SQL * Plus для команды VARIABLE выглядит следующим образом:

Usage: VAR[IABLE] [  [ NUMBER | CHAR | CHAR (n [CHAR|BYTE]) |
                    VARCHAR2 (n [CHAR|BYTE]) | NCHAR | NCHAR (n) |
                    NVARCHAR2 (n) | CLOB | NCLOB | BLOB | BFILE
                    REFCURSOR | BINARY_FLOAT | BINARY_DOUBLE ] ]

Все вышеперечисленные типы являются отдельными значениями данных, за исключением REFCURSOR, но, похоже, SQL * Plus по-прежнему рассматривает это как одно значение. Я не могу найти способ запроса данных, возвращенных REFCURSOR таким образом.

Итак, в общем, то, что вы пытаетесь достичь, почти наверняка невозможно. Я не знаю, какова ваша конечная цель, но я не думаю, что вы сможете сделать это с помощью одного запроса в SQL * Plus.

0 голосов
/ 09 июля 2011

Взгляните на Ugly-Delimited-String-Approach (tm) .

То есть связать строку и преобразовать ее в список в SQL. Ужасно, это так.

...