2011-08-12 94 views
2

我無法分解簡單的SQL查詢。我使用PostgreSQL,但我的問題也與其他RDBMS有關。PostgreSQL查詢分解

考慮下面的例子。我們有臺訂單,我們想找到第一個訂單後,其總金額超出了一些限制:

drop table if exists orders cascade; 

/** 
Table with clients' orders 
*/ 
create table orders(
date timestamp, 
amount integer 
/** 
Other columns omitted 
*/ 
); 

/** 
Populate with test data 
*/ 
insert into orders(date,amount) 
values 
('2011-01-01',50), 
('2011-01-02',49), 
('2011-01-03',2), 
('2011-01-04',1000); 

/** 
Selects first order that caused exceeding of limit 
*/ 
create view first_limit_exceed 
as 
select min(date) from 
(
    select o1.date 
    from orders o1, 
     orders o2 
    where o2.date<=o1.date 
    group by o1.date 
    having sum(o2.amount) > 100 
) limit_exceed; 

/** 
returns "2011-01-03 00:00:00" 
*/ 
select * from first_limit_exceed; 

現在讓我們把這個問題有點困難。考慮我們只想找到滿足某些謂詞的行的總金額。我們有很多這樣的謂詞,並創建單獨的視圖first_limit_exceed將是可怕的代碼重複。所以我們需要一些方法來創建參數化視圖,並傳遞過濾的一組行或謂詞本身。 在Postgres中,我們可以使用查詢語言函數作爲參數化視圖。但是Postgres不允許函數作爲參數既不是行也不是另一個函數。 我仍然可以在客戶端或plpgsql函數中使用字符串插值,但它很容易出錯並且很難測試和調試。 有什麼建議嗎?

+1

用於發佈表格腳本。所有這些都是@dvv做的! – Quassnoi

+0

也許這是愚蠢的,但你可以有一個視圖的列是謂詞的結果(所以每列類型是布爾值)。那麼添加一個新的謂詞將涉及:(1)用新的謂詞擴展視圖;(2)更改代碼以在新列名上選擇。這使查詢邏輯非常簡單,但將謂詞放在sql中。 –

回答

1

PostgreSQL 8.4及更高版本:

SELECT * 
FROM (
     SELECT *, 
       SUM(amount) OVER (ORDER BY date) AS psum 
     FROM orders 
     ) q 
WHERE psum > 100 
ORDER BY 
     date 
LIMIT 1 

添加任何你想要的謂詞入內查詢:

SELECT * 
FROM (
     SELECT *, 
       SUM(amount) OVER (ORDER BY date) AS psum 
     FROM orders 
     WHERE date >= '2011-01-03' 
     ) q 
WHERE psum > 100 
ORDER BY 
     date 
LIMIT 1 
+0

感謝您的快速回復,但我認爲您誤解了我的問題。我主要關心的是代碼重複。考慮你有10個謂詞。您可以複製10次查詢語句並手動替換謂詞。當你的查詢很小時,它並不是那麼糟糕,但如果它的數量是幾百行呢?複製百行十次會使你的代碼難以維護。 – dvv

+0

@dvv:既不是集合也不是謂詞是SQL中的第一個類對象。您不能將它們作爲參數傳遞給靜態查詢。你可能想編寫一個動態查詢(這會增加解析開銷),但是,如果可維護性比速度更重要,那將是一個很好的解決方案。 – Quassnoi

+0

@dvv:在'PostgreSQL'中,您可以將集合序列化爲表元組數組或者用於定義的記錄或字符串,並在函數之間傳遞它們。但是,解析查詢會更加昂貴,而且這需要您的集合處於預定義的格式。 – Quassnoi

-1

這聽起來有點像你試圖把太多的代碼插入到數據庫。如果您對滿足特定謂詞的特定關係的行感興趣,只需在客戶端代碼中使用合適的where子句執行select語句。將謂詞作爲參數的觀點正在重塑sql已經很好地解決的問題。

另一方面,我可以看到將查詢本身存儲在數據庫中的一個參數,以便它們可以組成更大的報告。這兩個更好地由應用程序代碼處理。我可能會使用一個擅長動態sql生成的庫(例如sqlalchemy)來處理這樣的問題,然後將查詢表示(sqlalchemy表達式對象是'pickleable')作爲數據庫中的blob存儲。

換句話說,數據庫是事實的代表,你在其中存儲知識。應用程序有責任根據用戶請求採取行動當您發現自己定義數據轉換時,這實際上更多是預測和實現實際用戶的請求,而不僅僅是忠實地保留知識。

當模式不可避免地發生變化時,最好使用視圖,因此您可以讓舊的應用程序不需要知道處於工作狀態的新模式。