2013-08-02 2 views
1

패키지 내에 2 개의 절차가 있습니다. 하나의 프로 시저를 호출하여 쉼표로 구분 된 사용자 ID 목록을 가져옵니다.NUMBER 열의 IN 절에서 쉼표로 구분 된 값을 사용합니다.

결과를 VARCHAR 변수에 저장합니다. 내가 구분 된 목록이 쉼표를 사용하고 때 "ORA-01722:INVALID NUMBER" 예외를 던지고에서 지금 IN 절 안에 넣어.

을 이것은 내 변수가 어떻게 보이는지 이것은

l_userIds VARCHAR2(4000) := null; 

처럼 내가 값

을 할당하고 어디에
l_userIds := getUserIds(deptId); -- this returns a comma separated list 

그리고 내 두 번째 쿼리는 같다 -

select * from users_Table where user_id in (l_userIds); 

이 쿼리를 실행하면 INVALID NUMBER 오류가 발생합니다.

누군가 도움을 줄 수 있습니까?

+0

, 선택은 plsql (결과 집합을 변수에 저장해야 함) 전체 쿼리를 표시하십시오 – Sebas

+0

users_id에서 select *를 선택하십시오. 여기서 user_id는 (select l_userIds); – maSTAShuFu

답변

1

문제는 oracle이 일련의 숫자로 전달하는 VARCHAR2 문자열을 보간하지 않는다는 것입니다. 단지 문자열 일뿐입니다.

해결책은 전체 쿼리 문자열 (VARCHAR2)를 만든 다음 그렇게 엔진이 그 내용 번역이 알고 실행하는 것입니다

DECLARE 
    TYPE T_UT IS TABLE OF users_Table%ROWTYPE; 
    aVar T_UT; 
BEGIN 
    EXECUTE IMMEDIATE 'select * from users_Table where user_id in (' || l_userIds || ')' INTO aVar; 
... 

END; 

을 좀 더 복잡한뿐만 아니라 우아한 솔루션을 분할하는 것 문자열을 테이블 TYPE에 넣고 쿼리에 직접 형 변환합니다. 무엇을 참조하십시오 Tom thinks about it.

8

정말 쉼표로 구분 된 목록을 반환해야합니까?

CREATE OR REPLACE FUNCTION get_nums 
    RETURN num_table 
IS 
    l_nums num_table := num_table(); 
BEGIN 
    for i in 1 .. 10 
    loop 
    l_nums.extend; 
    l_nums(i) := i*2; 
    end loop; 
END; 

다음 쿼리에서 해당 모음을 사용 일반적으로이 컬렉션의 인스턴스를 반환하는 함수를 선언 컬렉션 유형

CREATE TYPE num_table 
    AS TABLE OF NUMBER; 

를 선언하는 것이 훨씬 더 좋을 것이다

SELECT * 
    FROM users_table 
WHERE user_id IN (SELECT * FROM TABLE(l_nums)); 

@Sebas가 보여주는 동적 SQL을 사용할 수도 있습니다. 그러나 단점은 프로 시저를 호출 할 때마다 실행되기 전에 다시 구문 분석되어야하는 새 SQL 문을 생성한다는 것입니다. 또한 오라클이 다른 많은 재사용 가능한 SQL 문을 제거하여 많은 다른 성능 문제를 야기 할 수있는 라이브러리 캐시를 압박합니다.

+0

이런 식으로 컬렉션/테이블을 사용할 때 IN 절의 1000 개 값 제한이 적용됩니까? 감사. –

+1

@BobJarvis - 아니에요. 1000 값 제한은 쿼리가 아닌 리터럴 또는 개별 바인드 변수에만 적용됩니다 (테이블 또는 콜렉션에 대한 것인지 여부). –

+0

안녕하세요, @ JustinCave가 이것을 시도했지만, 두 가지 수정이 필요합니다. 'return l_nums; '내부에서 함수를 사용하고 쿼리에서 컬렉션을 사용하는 중 - ... select * from table (get_nums) ' –

2

당신은 in 대신 like를 사용하여 목록을 검색 할 수 있습니다

select * 
from users_Table 
where ','||l_userIds||',' like '%,'||cast(user_id as varchar2(255))||',%'; 

이 단순성의 미덕 (추가 기능 또는 동적 SQL)를 갖는다. 그러나 user_id에 대한 인덱스 사용을 배제합니다. 작은 테이블의 경우 이것은 문제가되지 않습니다.

-1

이 솔루션을 사용하지 마십시오!

첫째로, 나는 그것을 삭제하고 싶었지 만, 누군가가 그런 나쁜 해결책을 발견하는 것이 유익 할 것이라고 생각합니다. 이와 같이 동적 SQL을 사용하면 여러 실행 계획이 생성됩니다. IN 절에서 1 세트의 데이터 세트 당 1 개의 실행 계획이 사용됩니다. 바인딩이 사용되지 않으며 DB에 대해 모든 쿼리가 다릅니다 (SGA는 매우 유사한 실행으로 가득차 있습니다 계획, 다른 매개 변수로 쿼리를 실행할 때마다 더 많은 메모리가 SGA에서 불필요하게 사용됨).

(바인딩 변수를 사용하여) 동적 SQL을 사용하여 다른 대답을 쓰고 싶었지만 Justin Cave's 답이 가장 좋습니다.

또한 싶어 REF 커서를 시도 할 수 있습니다 는 (정확한 코드 나 자신이, 몇 가지 작은 개조하면 되겠 어해야 할 수도 있음을 시도하지 않은) :

DECLARE 
    deptId     NUMBER := 2; 
    l_userIds    VARCHAR2(2000) := getUserIds(deptId); 
    TYPE t_my_ref_cursor IS REF CURSOR; 
    c_cursor    t_my_ref_cursor; 
    l_row     users_Table%ROWTYPE; 
    l_query     VARCHAR2(5000); 
BEGIN 
    l_query := 'SELECT * FROM users_Table WHERE user_id IN ('|| l_userIds ||')'; 
    OPEN c_cursor FOR l_query; 

    FETCH c_cursor INTO l_row; 
    WHILE c_cursor%FOUND 
    LOOP 
     -- do something with your row 
     FETCH c_cursor INTO l_row; 
    END LOOP; 

END; 
/

자체 쿼리가 불완전
+1

이처럼 동적 SQL을 수행하면 효과가 있습니다 (@Sebas가 몇 년 전에 제안한 내용). 그러나 공유 풀에 많은 부담을 줄 수있는 명확하고 공유가 불가능한 SQL 문을 생성하고 있습니다. 이 코드를 자주 실행하려면 시스템에 성능 문제를 일으킬 수 있습니다. –

+0

귀하의 의견에 진심으로 감사드립니다. 실제로 동적 SQL을 사용하면 성능면에서 더 좋습니다. – AndrewMcCoist

관련 문제