2013-10-17 17 views
1

我想在Perl腳本中使用參數化查詢來從Postgres數據庫中獲取一些時間戳。這是一個切割和乾燥的例子,僅用於教學目的。參數化Perl中PostgreSQL中的時間戳引用的問題

我定義$start_date$end_date的時間戳和間隔:

my $start_date = "current_timestamp - interval '6 hours'"; 
my $end_date = "current_timestamp"; 

我用下面提交到數據庫中,與$dbh早先定義:

my $sql = "SELECT cast(? as timestamp), cast(? as timestamp)"; 
my $sth = $dbh->prepare($sql); 
$sth->execute($start_date, $end_date); 

當我做到這一點,我收到了一個令人困惑的錯誤。

DBD::Pg::st execute failed: ERROR: date/time value "current" is no longer supported 

我明白current尚未在PG自7.2的支持,但我沒有使用。我正在使用current_timestamp,其中支持,AFACT。要曉得,我若進入psql

select (cast(current_timestamp - interval '6 hours' as timestamp), cast(current_timestamp as timestamp)); 

結果是我所期望的(兩個時間戳,前者6小時先於後者)。

我也可以使用now()而不是current_timestamp。我可以通過以下方式來使用它:

my $start_date = "now() - interval '6 hours'"; 
my $end_date = "now()"; 

當我嘗試運行在Perl查詢,我得到以下錯誤:

DBD::Pg::st execute failed: ERROR: invalid input syntax for type timestamp: "now() - interval '6 hours'" 

然而,查詢:

select (cast(now() - interval '6 hours' as timestamp), cast(now() as timestamp)); 

給了我預期的結果。

我很慌張。

+0

我認爲語法是錯誤的...正確的方式應該是這樣的$ sth-> execute($ sql); – Rudra

+0

語法沒有錯;我正在使用SQL和數據庫句柄來使用'prepare'創建一個語句句柄,然後我使用'execute'來運行適當的參數。 (我從代碼庫中的現有(工作)代碼以及[DBI文檔](http://search.cpan。org /〜timb/DBI-1.628/DBI.pm)。) – techstepper

+1

可能的重複[如何在引號中使用帶佔位符的查詢? (perl/postgresql)](http://stackoverflow.com/questions/10659737/how-can-i-use-a-query-with-placeholder-inside-quotes-perl-postgresql) – ThisSuitIsBlackNot

回答

5

問題是SQL佔位符並不代表一個表達式,而是一個單一的值。而且這個價值不可能是一個功能。你可以這樣做:

my $start_date = "6 hours"; 
my $sql = "SELECT current_timestamp - cast(? as interval), current_timestamp"; 
my $sth = $dbh->prepare($sql); 
$sth->execute($start_date); 

你在做什麼用Perl相當於這樣做在psql

select (cast('current_timestamp - interval ''6 hours''' as timestamp), cast('current_timestamp' as timestamp)); 
+0

謝謝你的回覆。我試過了,我得到了以下錯誤: 'DBD :: Pg :: st執行失敗:錯誤:語法錯誤處於或接近「$ 1」' 並且沒有提及'$ start_date'或其內容6小時),我通過了。嘗試通過設置'$ start_date ='''6小時'「;'產生相同的錯誤。 – techstepper

+0

顯然,PostgreSQL不會讓你說'INTERVAL?',你必須說'CAST(?AS INTERVAL)'。而外層是多餘的,因爲時間戳減去間隔已經是時間戳了。 – cjm

1

爲了讓您的查詢的窗口一個更靈活一點:

$sth = $dbh->prepare(<<__eosql); 
SELECT * FROM tbl 
WHERE ts BETWEEN current_timestamp - ? * CAST('1 ' || ? AS INTERVAL) 
        AND 
        current_timestamp; 
__eosql 

$sth->execute(6, 'hour'); 
$sth->execute(10, 'day'); 
$sth->execute(1, 'week'); 
# etc. 

當您引入固定時間點時,您可以執行一些非常巧妙的操作,如... WHERE COALESCE(?, current_timestamp) ...,並記住undef參數def到當前時間。不過,我可能會編寫並準備一個單獨的查詢。