2014-02-06 67 views
0

我有一個表,它看起來像觸發/約束限制插入或更新

MyTable (
Student ID, 
Exam) 

我想將觸發/約束描述:

學生證可以出現很多次,不必須是獨一無二的。但是,這對(學生證,「法國」)只能出現一次。所以每個學生只能有一個「法國」條目。法國是硬編碼

 
ID Exam 

0001 German 
0001 History 
0001 French 
0001 French <-- This insert should fail. 

Attemting更新「德國」字段法國人也應該會失敗

到目前爲止,我已經試過

CREATE OR REPLACE TRIGGER MyTrigger BEFORE INSERT OR UPDATE ON MyTable 
FOR EACH ROW 
DECLARE 
    rowsCount INTEGER; 
BEGIN 
    select count(*) 
    INTO rowsCount 
    from MyTable sc 
    where SC.SC_TYPE = 'FRENCH' and :new.StudentID = sc.StudentID; 
IF rowsCount > 1  
THEN 
    raise_application_error('-20098','You cannot have more than one French record per student.'); 
END IF; 
end; 

這雖然拋出一個變異的錯誤。無論如何,這與完整性有關,所以我相信一個限制會更好,但是會對如何實現這一點提供一些建議。

+0

那麼對'French'的限制是特定的,他們應該被允許與'German'有多行? –

回答

3

是的,法國人每個學生只能出現一次,但其他一切都可以出現多次。

我在想也許這是最好的解決索引?類似:Oracle SQL Constraint where clause

CREATE UNIQUE INDEX MyIndex ON MyTable(
    CASE WHEN Exam= 'French' 
     THEN StudentID 
     ELSE NULL 
    END 
); 
+0

這也行不通。如果任何學生多次進行相同的考試,則第二次插入將失敗。即使您將索引更改爲'(StudentID,CASE WHEN EXAM ='French'THEN StudentID ELSE NULL END)',它仍然會在第二次針對給定學生插入非「法語」語言時失敗,至少在Oracle 11.1。 [SQLFiddle here](http://sqlfiddle.com/#!4/f8cdd9/1) –

+1

只有索引定義中的case語句應該沒問題。如果你從sqlfiddle中的索引中刪除student_id列,那麼一切都很好。 –

1

由於限制的特殊性質的(只有法國限制)我不覺得做這個可以用約束來實現(雖然我很樂意去學習其他)。你可以解決它的方法之一是使用語句觸發器:

CREATE OR REPLACE TRIGGER NO_DUPLICATE_FRENCH_TRIGGER 
    BEFORE INSERT OR UPDATE 
    ON MY_TABLE 
    -- NOTE: NO 'FOR EACH ROW', which means this is fired once for each 
    -- statement executed, rather than once for each row modified. 
DECLARE 
    nMax_count NUMBER; 
BEGIN 
    SELECT MAX(COUNT_VAL) 
    INTO nMax_count 
    FROM (SELECT STUDENTID, COUNT(*) AS COUNT_VAL 
      FROM MY_TABLE 
      WHERE EXAM = 'FRENCH' 
      GROUP BY STUDENTID); 

    IF nMax_count > 1 THEN 
    RAISE SOME_EXCEPTION; 
    END IF; 
END NO_DUPLICATE_FRENCH_TRIGGER; 

聲明觸發器的優點在於,他們不受制於「變異表」問題,因爲是行觸發器。然而,這是一個大問題,引入了全表掃描,如果表很大可能是性能問題,但至少它是一個解決方案。

分享和享受。

0

我建議你創建3個觸發器和一個包來處理這個驗證。

  • 1包含一個PL/SQL表和將由您的觸發器調用的過程。
  • 1語句觸發前清除PL/SQL表。
  • 1在每行觸發器之前(或之後)觸發器將要插入/更新的記錄添加到PL/SQL表中。
  • 1語句觸發後,根據您填充的PL/SQL表執行驗證。

您可以添加到PL/SQL表中只有考試='法國'的記錄,並檢查學生是否已經對數據庫進行了法語考試。

0

這個怎麼樣?

alter table MyTable添加約束條件std_exam unique(Student ID,Exam);