2010-10-21 45 views
2

假設你有如下表(注:這是一個人爲/簡體爲例):在SQL where子句中需要參數?

CREATE TABLE foo ( 
    book_id number, 
    page number, 
    -- [a bunch of other columns describing a single page in a book] 
); 

ALTER TABLE foo 
ADD (CONSTRAINT foo_pk PRIMARY KEY(book_id, page)); 

雖然(book_id,頁)對是唯一的,在同一頁面數量將書籍之間重複(很多書會有一頁1)。因此,如果SQL查詢未指定book_id,則可能會選擇/更新/刪除錯誤的頁面。我們所有的查詢一次只能處理一本書,但是我發現了一些錯誤,其中book_id參數被忽略。

是否有一種編程方式來強制每個select,insert,update等查詢在where子句中指定book_id?

我們爲查詢動態生成SQL代碼並使用Spring的JdbcTemplate執行它們。數據庫是Oracle。使用自動化測試來檢查許多可能的查詢(加上將來添加的新查詢!)不會被重複的page_id絆倒是非常棘手的。我可以覆蓋JdbcTemplate代碼,以確保sql查詢始終包含book_id參數,但涉及到手動解析SQL代碼(尤其是使用子查詢來處理棘手問題)並且看起來很詭異。是否有更強大的解決方案來執行此操作?一些觸發器,存儲過程,約束?

+0

什麼數據庫?您可能可以通過PostgreSQL規則系統實現這些目標。 – 2010-10-21 21:22:01

+0

感謝您的建議。剛剛補充說「Oracle」是原始問題的DB。 – 2010-10-21 21:23:34

+0

如果有人想運行不需要book_id的查詢,例如「找到頁數最多的書」 - 這樣的查詢在book_id上不會有謂詞。 – 2010-10-22 02:24:26

回答

2

您可以使用函數或存儲過程,而不是直接使用UPDATE。該過程需要2個參數,如果其中任一個爲空,則會引發錯誤。

另一種選擇是確保您生成的查詢始終具有book_id約束。我希望你不是將整個SQL語句創建爲字符串,而是使用參數化查詢。如果你不是,那麼使用參數化查詢是確保你始終傳遞book_id的一個好方法(如果你沒有設置參數,查詢就不會運行)。此外,如果您在使用參數化查詢時不清理輸入內容,則不會有風險。

+0

使用存儲過程而不是查詢將需要更改很多代碼,但是我想我可以做到這一點,如果沒有其他解決方案是可能的。 – 2010-10-21 21:37:50

+0

噢,我們正在使用參數化查詢,問題在於它們中有很多,新增加了所有時間,偶爾會有人忘記where子句中的book_id參數。換句話說,我正在尋找一種解決方案,以幫助程序員無法正確編寫查詢。 – 2010-10-21 21:39:34

3

保護數據庫免受程序員錯誤的常見方法是要求應用程序使用存儲過程。 (有時候這可以通過權限來完成。)

檢查你的proc是否比ad hoc查詢更合適。

0

我能想到的唯一方法是將表中的book_id和page列替換爲存儲這兩個信息的單個列,比如(book_id * 10000 + page),如果您想在整數列中或「book_id-page」作爲字符串列。

從正確性的角度來看,這是一個壞主意(存儲在一列中的兩個屬性),但會強制程序員使用這兩個屬性與表進行交互。如果這對你來說是一個足夠大的關注,你可能會考慮它。

1

首先,這確實是一個測試問題 - 不是用戶會犯這個錯誤,它是開發人員,他們的錯誤應該在之前被抓到

說了這麼多,你可以通過觸發器的組合捕獲這樣的更新:

  • 語句級BEFORE觸發器初始化一個包變量g_book_id爲null
  • 行級觸發器(一)檢查正在更新的book_id是否與包變量中的值匹配(如果不爲null),並且(b)如果package變量爲null,則調用它。

一個簡單的例子:

SQL> create table t1 (id int, col2 int); 

Table created. 

SQL> insert into t1 values(1, null); 

1 row created. 

SQL> insert into t1 values(2, null); 

1 row created. 

SQL> create package p1 is g_id integer; end; 
    2/

Package created. 

SQL> create trigger t1_bus 
    2 before update on t1 
    3 begin 
    4 p1.g_id := null; 
    5* end; 
SQL>/

Trigger created. 

SQL> create trigger t1_bir 
    2 before update on t1 
    3 for each row 
    4 begin 
    5  if :new.id != p1.g_id then 
    6  raise_application_error(-20000,'You can only update 1 ID at a time'); 
    7  end if; 
    8  p1.g_id := :new.id; 
    9 end; 
10/

Trigger created. 

SQL> update t1 set col2=1 where id=1; 

1 row updated. 

SQL> update t1 set col2=2 where id=2; 

1 row updated. 

SQL> update t1 set col2=3; -- ID not specified 
update t1 set col2=3 
     * 
ERROR at line 1: 
ORA-20000: You can only update 1 ID at a time 
+0

他希望這個用於'SELECT',以及... – egrunin 2010-10-22 13:37:18

+0

沒錯,這隻能解決更新和刪除問題,另一種方法將需要選擇。但我看不出如何將選擇一次限制在一本書中 - 用戶如何從列表中選擇一本書?或者統計某個主題上有多少本書?或者......呃,你明白了。 – 2010-10-22 13:45:45

+0

有趣的方法,謝謝。我無法想象有一種基於觸發器的方法可以用於'SELECT'嗎?正如我在上面的評論中所說的,實際的域不涉及書籍(爲便於討論我修改了表/列名),而在真實域中,跨多個「書籍」的查詢沒有意義。 – 2010-10-22 17:31:48