2011-08-12 113 views
7

我正在開發Oracle APEX中的應用程序。我有一個用戶ID的一個字符串,它是逗號deliminated它看起來像這樣,PL/SQL查詢逗號分隔字符串

45,4932,20,19 

此字符串作爲

:P5_USER_ID_LIST 

我想要一個查詢,會發現此列表中的所有用戶都儲存在我查詢看起來像這樣

SELECT * FROM users u WHERE u.user_id IN (:P5_USER_ID_LIST); 

我不斷收到Oracle錯誤:無效的數字。但是,如果我將字符串硬編碼到查詢中,它就起作用了。像這樣:

SELECT * FROM users u WHERE u.user_id IN (45,4932,20,19); 

任何人都知道爲什麼這可能是一個問題?

回答

-1

您需要將其作爲動態SQL運行。

創建整個字符串,然後動態運行它。

+1

不要這樣做 - 首先,如果查詢頻繁地運行多個user_id,它將使共享池氾濫許多不可用的遊標,並且很快整個數據庫將有一個硬解析問題。如果你不是非常小心的話,還有一個重要的SQL注入風險。 –

0

創建一個本地查詢,而不是使用 「的createQuery/createNamedQuery」

2

更簡單的解決方案是使用instr

SELECT * FROM users u 
WHERE instr(',' || :P5_USER_ID_LIST ||',' ,',' || u.user_id|| ',', 1) !=0; 

招數:

',' || :P5_USER_ID_LIST ||',' 

,使您的字符串,45,4932,20,19,

',' || u.user_id|| ',' 

具有即,32,,避免選擇32,4932,

9

綁定變量結合一個值是,在這種情況下,字符串'45,4932,20,19' 。您可以使用Randy建議的動態SQL和串聯,但您需要非常小心,以防止用戶修改此值,否則您會遇到SQL注入問題。

一個比較安全的方法是把ID添加到頂點集合在PL/SQL過程:

declare 
    array apex_application_global.vc_arr2; 
begin 
    array := apex_util.string_to_table (:P5_USER_ID_LIST, ','); 
    apex_collection.create_or_truncate_collection ('P5_ID_COLL'); 
    apex_collection.add_members ('P5_ID_COLL', array); 
end; 

然後將查詢更改爲:

SELECT * FROM users u WHERE u.user_id IN 
(SELECT c001 FROM apex_collections 
WHERE collection_name = 'P5_ID_COLL') 
+0

'apex_collection.create_or_truncate_collection'給我'不能將NULL插入(「APEX_040000」。「WWV_FLOW_COLLECTIONS $」。「SESSION_ID」)' –

+0

@OleksandrPapchenko您只能從APEX會話中調用'apex_collection'包。 –

0

的原因,這是一個問題就是你不能僅僅按照你想要的方式綁定一個列表,並且幾乎每個人在學習Oracle(也可能是SQL!)時至少犯了一次這個錯誤。

當您綁定的字符串'32,64,128' ,它實際上就變成一個查詢關鍵詞,比如:

select ... 
from t 
where t.c1 in ('32,64,128') 

到Oracle,這是完全不同的,以:

select ... 
from t 
where t.c1 in (32,64,128) 

第一個例子有一個單一的字符串值在列表中,第二個列表中有3個數字。你得到一個無效數字錯誤的原因是因爲Oracle試圖將字符串'32,64,128'轉換爲一個數字,由於字符串中的逗號,它不能這樣做。

這個「我該如何綁定列表」的問題的一個變種最近出現在這裏不止一次。

一般地,並沒有訴諸任何PLSQL,擔心SQL注入或不正確綁定查詢,你可以使用這一招:

with bound_inlist 
    as 
    (
    select 
    substr(txt, 
      instr (txt, ',', 1, level ) + 1, 
      instr (txt, ',', 1, level+1) - instr (txt, ',', 1, level) -1) 
      as token 
    from (select ','||:txt||',' txt from dual) 
    connect by level <= length(:txt)-length(replace(:txt,',',''))+1 
) 
    select * 
from bound_inlist a, users u 
where a.token = u.id; 
1

我也遇到這種情況好幾次,這裏是我」已經使用:

SELECT * 
    FROM users u 
WHERE ','||to_char(:P5_USER_ID_LIST)||',' like '%,'||to_char(u.user_id)||',%' 

香港專業教育學院使用的類似操作,但你必須是一個方面,這裏的一點點小心:您的項目P5_USER_ID_LIST必須「 45,4932,20,19,」讓喜歡將與比較確切數字「',45,'」。

當使用像這樣的選擇不會出錯讓說:5 15,155,55

試試吧,讓我知道如何去;)

乾杯,亞歷克斯

+0

您不需要或不需要TO_CHAR:P5_USER_ID_LIST –

0

如果可能的話,最好的辦法可能是不把你的用戶id存儲在csv中!把它們放在一個表格中或者失敗的數組等等。你不能將一個csv字段綁定爲一個數字。

0

請不要使用:WHERE','|| to_char(:P5_USER_ID_LIST)||','like'%,'|| to_char(u.user_id)||',%',因爲您會強制完整表掃描,儘管用戶表可能沒有那麼多,所以影響會很小,但是在企業環境中的其他表中,這是一個問題。

編輯:我已經放在一起的腳本來演示正則表達式方法和通配符類似的方法之間的差異。正則表達式不僅更快,而且更強大。

-- Create table 
create table CSV_TEST 
(
    NUM NUMBER not null, 
    STR VARCHAR2(20) 
); 


create sequence csv_test_seq; 

begin 
    for j in 1..10 loop 
    for i in 1..500000 loop 
    insert into csv_test(num, str) values (csv_test_seq.nextval, to_char(csv_test_seq.nextval)); 
    end loop; 
    commit; 
    end loop; 
end; 
/

-- Create/Recreate primary, unique and foreign key constraints 
alter table CSV_TEST 
    add constraint CSV_TEST_PK primary key (NUM) 
    using index ; 
alter table CSV_TEST 
    add constraint CSV_TEST_FK unique (STR) 
    using index; 

select sysdate from dual; 
select * 
from csv_test t 
where t.num in (Select Regexp_Substr('100001, 100002, 100003 ,  100004, 100005','[^,]+', 1, Level) From Dual 
       Connect By Regexp_Substr('100001, 100002,100003, 100004, 100005', '[^,]+', 1, Level) Is Not Null); 
select sysdate from dual; 

select * 
from csv_test t 
where ('%,' || '100001,100002, 100003, 100004 ,100005' || ',%') like '%,' || num || ',%'; 
select sysdate from dual; 
select * 
from csv_test t 
where t.num in (Select Regexp_Substr('100001, 100002, 100003 ,  100004, 100005','[^,]+', 1, Level) From Dual 
       Connect By Regexp_Substr('100001, 100002,100003, 100004, 100005', '[^,]+', 1, Level) Is Not Null); 
select sysdate from dual; 

select * 
from csv_test t 
where ('%,' || '100001,100002, 100003, 100004 ,100005' || ',%') like '%,' || num || ',%'; 
select sysdate from dual; 

drop table csv_test; 
drop sequence csv_test_seq; 
0

託尼安德魯斯的解決方案爲我工作。該過程應添加到「頁面處理」>>「提交後」>>「過程」。

相關問題