2017-06-18 47 views
2

所以我正在處理2個表格:人員和婚姻。 人擁有很多屬性,他的PK是person_id。 婚姻法的定義那樣:將用戶定義的約束添加到SQL表中

CREATE TABLE Marriage(Person_id NUMBER(6) PRIMARY KEY REFERENCES Persons(Person_id) 
, Relative_id NUMBER(4) REFERENCES Persons(Person_id) 
, Relationship_type VARCHAR(20) NOT NULL CHECK (Relationship_type IN('Wife', 'Husband', 'Child')) 
) 

我想約束添加到此表,以便香港專業教育學院添加了這個(TODO:Parent.age> Child.age):當我

CREATE FUNCTION fn_OlderThanSon (
    @Parent NUMBER 
    @Child NUMBER 
) 
RETURNS VARCHAR(10) 
AS 
BEGIN 
    IF EXISTS (SELECT Person_id FROM Persons WHERE Person_id = @Parent) 
AND EXISTS (SELECT Person_id FROM Persons WHERE Person_id = @Child) 
     return 'True' 
    return 'False' 
END 

現在米試圖將它連接到婚姻表這樣做:

ALTER TABLE Marriage ADD CONSTRAINT CK_OlderThan 
CHECK (fn_OlderThanSon(Person_id,Relative_id) = 'True'); 

我得到以下錯誤:

java.sql.SQLSyntaxErrorException: ORA-01735: invalid ALTER TABLE option 

    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:450) 
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:399) 
    at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1059) 
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:522) 
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:257) 
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:587) 
    at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:210) 
    at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:30) 
    at oracle.jdbc.driver.T4CStatement.executeForRows(T4CStatement.java:931) 
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1150) 
    at oracle.jdbc.driver.OracleStatement.executeQuery(OracleStatement.java:1309) 
    at oracle.jdbc.driver.OracleStatementWrapper.executeQuery(OracleStatementWrapper.java:422) 
    at com.company.Main.main(Main.java:93) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:498) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) 

那麼,我做錯了什麼?是否有可能做這樣的邏輯約束?

+0

函數的DDL看起來像SQL Server的T-SQL,而不是Oracle的PL/SQL。你確定你在使用Oracle嗎?爲什麼你不只是使用外鍵約束? –

+0

好吧,我是,因爲我是新手,所以我可能會修改一些不相關的代碼。外鍵約束是什麼意思?是否可以添加邏輯語句if(Person_id.age> Relative_id.age)返回true。 ? –

+0

您的全班在Stack Overflow上發佈這個作業嗎?我昨天回答了完全相同的問題(由另一張海報發佈) - 我提供了一個只需要標準SQL並且不需要觸發器的解決方案。 https://stackoverflow.com/questions/44601943/oracle-sql-check-constraint-between-2-tables/44604201#44604201 – mathguy

回答

1

要使用函數fn_OlderThanSon()確保「父」類型的記錄比「child」類型的記錄舊:if (Person_id.age>Relative_id.age)

這樣的業務規則需要一個斷言 - 一個聲明性的多行約束 - 而不是一個簡單的檢查約束。目前Oracle不支持CREATE ASSERTION(雖然有an initiative to make this happen)。

現在,要執行這樣的規則,需要過程邏輯,可以通過Persons表中的PL/SQL API直接調用,或通過觸發器間接調用。觸發器會很複雜,因爲你必須解決變異表問題(你需要查詢你正在改變的表中的記錄),但是很多人不喜歡屏蔽PL/SQL過程背後的表。

+1

「強制執行這樣的規則**需要程序邏輯**」是不正確的。我昨天提供了一個基於物化視圖的檢查約束的解決方案(當不同海報發佈完全相同的問題時)。 https://stackoverflow.com/questions/44601943/oracle-sql-check-constraint-between-2-tables/44604201#44604201 – mathguy

0

不支持您希望它工作的方式。你有幾個選項,但:

  1. 你可以寫一個觸發器,插入/更新之前觸發,並做這個檢查。如果結果不理想,則發出錯誤。

  2. 如果根據函數一切都正常,你可以在存儲過程中進行插入。並通過該存儲過程進行插入。

  3. 您可以在應用程序級別進行檢查,只在一切正常時才調用插入。

從純科學的角度來看,第二個和第三個選項的組合是可取的。如果你需要緊急做一些事情,第一種選擇會讓你頭疼,因爲知道結果會好的。

但是,您還需要應對不同的情況。如果插入/更新孩子,除了比較他的年齡和他的孩子的年齡以外,還需要檢查孩子是否比他/她的父母年齡大。此外,您可能需要合法更新父母和孩子的年齡,但這樣訂單就不可交換。

例如:父母年滿40歲,孩子20歲。您需要將父母更新爲61,將孩子更新爲41。如果您以錯誤的方式執行此操作,即將父母的年齡從40歲更改爲41歲,則根據您當前的業務規則,通過觸發器/存儲過程進行的更新將不會成功。