2011-04-09 17 views
-1

讓我們有一個表:DBD :: SQLite,如何通過佔位符在查詢中傳遞數組?

sqlite> create table foo (foo int, bar int); 
sqlite> insert into foo (foo, bar) values (1,1); 
sqlite> insert into foo (foo, bar) values (1,2); 
sqlite> insert into foo (foo, bar) values (1,3); 

然後選擇一些數據:

sqlite> select * from foo where foo = 1 and bar in (1,2,3); 
1|1 
1|2 
1|3 

工作的很好。現在我試圖使用DBD :: SQLite 1.29:

my $sth = $dbh->prepare('select * from foo where foo = $1 and bar in ($2)'); 
$sth->execute(1,[1,2,3]); 

並且這給了我空結果。 DBI跟蹤顯示第二個佔位符綁定到數組,但沒有分數。如果我在一個字符串中的數組值並傳遞它,沒有結果。如果我壓扁數組,我會得到「用N個佔位符而不是2來調用」的可預測錯誤。

我有點不知所措。還有什麼要嘗試?

Upd:好的,下面是真實世界應用程序中的一個真實示例。

首先,設置:我有幾個表格填充統計數據,列數從10到700+不等。我正在討論的查詢選擇該數據的子集用於報告目的。不同的報告考慮不同的方面,因此運行不同的查詢,每個請求一個或多個。有200多個報告,即200-300個查詢。這種方法是爲Postgres開發的,現在我需要縮小它並使其與SQLite一起工作。考慮到所有這些與Postgres的配合良好,我無法證明我們可以通過查詢並重寫它們。維修不好。我可以並且確實使用就地查詢調整,例如用IN()替換= ANY(),這些都是次要方面。

所以,這裏是我的榜樣:2個查詢連續跑了一個報告:

SELECT SPLIT, syn(SPLIT), 
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND 
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 40), 
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND 
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 30), 
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND 
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 50), 
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND 
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 220), 
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND 
LOC_ID = ANY ($3) AND LOGID IS NOT NULL), 
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND 
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 20), 
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND 
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 80) 
FROM csplit WHERE ACD = $1 AND SPLIT = $2 

SELECT syn(LOGID), syn(LOC_ID), LOGID, EXTENSION, syn(ROLE), PERCENT, 
syn(AUXREASON), syn(AWORKMODE), syn(DIRECTION), WORKSKILL, syn(WORKSKLEVEL), 
AGTIME FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND LOC_ID = ANY ($3) AND 
LOGID IS NOT NULL 

這還不是最複雜的例子,因爲可以有任意數量的使用的輸入參數,在不同的地方重複使用查詢;將其替換爲通用的?佔位符並不是一項簡單的任務。對Postgres運行查詢的代碼如下所示(輸入清理後):

sub run_select { 
    my ($class, $dbh, $sql, @bind_values) = @_; 

    my $sth; 
    eval { 
    $sth = $dbh->prepare_cached($sql); 
    $sth->execute(@bind_values); 
    }; 
    [email protected] and die "Error executing query: [email protected]"; 

    my %types; 
    { 
    my $dbt = $dbh->type_info_all; 
    @types{ map { $_->[1] } @$dbt[1..$#$dbt] } = 
     map { $_->[0] } @$dbt[1..$#$dbt]; 
    }; 

    my @result; 

    while (my $row = $sth->fetchrow_arrayref) { 
    my $i = 0; 
    push @result, [ map { [ $types{${$sth->{TYPE}}[$i++]}, $_ ] } @$row ]; 
    }; 

    return \@result; 
}; 

我可以重寫查詢並直接注入值; SQL注入並不是太大的威脅,因爲所有的輸入在命中SQL引擎之前很久都沒有被正則表達式模仿。我不想動態地重寫查詢,原因有兩個:a)它可能會導致價值引用問題,並且b)它會殺死prepare_cached背後的全部原因。如果每次更改準備好的語句,SQL引擎都不能緩存它。

現在正如我所說的,上面的代碼與Postgres很好地協作。由於SQLite引擎本身顯然有可能使用數據集,所以我認爲這是DBD :: SQLite實現的一個缺陷。所以真正的問題聽起來像:有沒有辦法通過DBD :: SQLite在佔位符中傳遞數據集?不一定數組,儘管這是最合乎邏輯的。

+0

也許你應該發佈一個wha的實例,而不是張貼編寫的代碼t您從應用程序/庫中獲取的數據。 – MkV 2011-04-09 20:49:18

+0

-1這個問題的質量很差 – 2013-09-13 12:45:34

回答

3

試試這個:

my $sth = $dbh->prepare("select * from foo where foo = ? and bar in (?,?,?)"; 
$sth->execute(1,1,2,3); 

可以使用x重複操作符來生成所需數量的? S:

my $sql = sprintf "select ... and bar in (%s)", join ",", ('?')[email protected]; 
+0

不能。首先,查詢通過調用應用程序(我是一個庫)以及參數傳遞給我,而我不想解析它。它可以很容易比上面簡單的例子複雜得多。其次,這種方法有點破壞了整個佔位符的想法 - 如果我必須做解析,我最好直接在查詢文本中插入值以避免頭痛。 – 2011-04-09 18:29:25

+2

它根本不會破壞這個想法,因爲注入值需要恰當地逃避價值觀念,這是通過傳遞多個值而加劇的問題。逗號分隔值。插入多個?時,您沒有進行任何解析。 – MkV 2011-04-09 20:46:47

+0

@Alexander:當你搞砸SQL引用時發生的頭痛是非常現實的。 *不要自己做!*(如果你這樣做,你可能會遇到完全困擾PHP應用程序的SQL注入漏洞。) – 2011-04-09 21:13:34

1

使用SQL::Abstract,像這樣:

use strict; 
use warnings; 
use SQL::Abstract; 

my $sqla = SQL::Abstract->new; 
my %where = (
    foo => 1, 
    bar => { -in => [1,2,3] } 
); 

my ($sql, @params) = 
    $sqla->select('foo', '*', \%where); 

my $sth = $dbh->prepare($sql); 
$sth->execute(@params); 
+0

它看起來不夠抽象,不足以滿足我的需求。參見上面的例子。感謝您的建議,但我會在將來繼續關注該模塊。 – 2011-04-10 07:47:52