2014-01-21 50 views
3

我們有一個兒童營的班級註冊表。我被要求確保註冊僅限於可用的席位。努力防止班級註冊申請過度註冊

當提交表格時,我有代碼檢查所有提交的類是否可用座位。如果座位可用,則註冊將被輸入到註冊表中。如果沒有,則用戶返回到班級列表以作出新的選擇。

我附上了檢查CFLOCK中的可用性的代碼。我的理解是,CFLOCK一次只允許一次提交訪問鎖內的代碼。我以前沒有使用過CFLOCK,所以我可能會不恰當地使用它。

我的問題是同時請求最後一個可用座位。我已經多次對最後一個席位的5個同時請求進行測試,並且每次5個請求中的3個都成功完成。

我使用ColdFusion 9和MySQL 5(MyISAM表)

任何想法或建議將是有益的。

這裏是檢查座位可用代碼:

<!--- 3E Check Class availability and Submit or Re-Select ---> 
<!--- Class 1 availability ---> 

<cfif IsDefined('Class1') and Class1 neq ''> 
    <cflock name="ClassAvail" timeout="10" throwontimeout="no" type="exclusive"> 
     <!--- Check number of registrations for Class 1 ---> 
     <cfquery datasource="#datasource#" name="Registrations1"> 
      Select Count(*) as Count From Registrations 
      Where Class_ID = #Class1# 
     </cfquery> 
     <!--- Check number of seats for class 1 ---> 
     <cfquery datasource="#datasource#" name="ClassAvail1"> 
      Select Class, Class_Size from Classes 
      Where ID = #Class1# 
     </cfquery> 
     <!--- Check for seat availability ---> 
     <cfif Registrations1.count GTE ClassAvail1.Class_size> 
      <cfset Class1_Avail = 'False'> 
     <cfelse> 
      <cfset Class1_Avail = 'True'> 
     </cfif> 

     <!--- Class 2 availability ---> 
     <cfif IsDefined('Class2') and Class2 neq ''> 
      <!--- Check number of registrations for Class 2 ---> 
      <cfquery datasource="#datasource#" name="Registrations2"> 
       Select Count(*) as Count From Registrations 
       Where Class_ID = #Class2# 
      </cfquery> 
      <!--- Check number of seats for class 2 ---> 
      <cfquery datasource="#datasource#" name="ClassAvail2"> 
       Select Class, Class_Size from Classes 
       Where ID = #Class2# 
      </cfquery> 
      <!--- Check for seat availability ---> 
      <cfif Registrations2.count GTE ClassAvail2.Class_size> 
       <cfset Class2_Avail = 'False'> 
      <cfelse> 
       <cfset Class2_Avail = 'True'> 
      </cfif> 
     </cfif> 

     <!--- If Classes Available, Insert in Registrations ---> 
     <cfif (IsDefined('Class1_Avail') and Class1_Avail eq 'True') and (IsDefined('Class2_Avail') and Class2_Avail eq 'True')> 
      <cfloop from="1" to="2" index="LoopCount"> 
       <cfif ISDEFINED("class#loopcount#")> 
        <cfset Class_ID = #ListGetAt(ClassChoice, #loopcount#)#> 
        <cfquery datasource="#datasource#" name="AddRegistration"> 
         Insert into Registrations (Child_ID, Class_ID) Values(#Session.Child_ID#, #Class_ID#) 
        </cfquery> 
       </cfif> 
      </cfloop> 
      <cflocation addtoken="no" url="Thanks.cfm"> 
     </cfif> 
    </cflock> 

    <!--- Classes not available ---> 
    <cfif (IsDefined('Class1_Avail') and Class1_Avail eq 'False') or (IsDefined('Class2_Avail') and Class2_Avail eq 'False')> 
     <cfset ClassChoice = ""> 
     <cfset Class1 = ""> 
     <cfset Class2 = ""> 
     <div style="display: block; z-index: 1000; border: #F00 thick solid; background-color: #FFF; position: absolute; top: 220px; box-shadow: 10px 10px 3px 5px #999999; padding:20px"> 
      We're sorry, but one or more of your choices are no longer available.<br /> 
    Please make your selections again.<br /> 
     </div> 
     <cfif Class1_Avail eq 'False'> 
      <h3><cfoutput>#classavail1.class# is no longer available.</cfoutput></h3> 
      <br/> 
     </cfif> 
     <cfif Class2_Avail eq 'False'> 
      <h3><cfoutput>#classavail2.class# is no longer available.</cfoutput></h3> 
      <br/> 
     </cfif> 
    </cfif> 
</cfif> 
+0

我注意到在您的檢查中「插入註冊」的類可用性,您檢查'Class1_Avail' **和**'Class2_Avail'都設置爲true。那是對的嗎?或者是否只需要在其中一個類中提供可用性,因此您應該使用**或**代替? –

+2

你應該使用'cfqueryparam',你也可以使用''將它設置爲布爾值而不是字符串。 –

+0

什麼是所有會話的自動提交設置?什麼是所有會話的隔離級別? –

回答

0

CFQUERY的結果屬性可以使這個整體變得簡單許多。在插入查詢中,recordcount屬性將告訴您插入了多少行。所以,你只需要做這樣的事情:

<cfquery datasource="something" result="insertResult"> 
Insert into Registrations 
(Child_ID, Class_ID) 
select distinct #Session.Child_ID#, #Class_ID# 
from registrations 
where (select count(*) 
from registrations 
where class_id = #Class_id#) <= #variable for max allowed goes here# 
</cfquery 

<cfif insertResult.recordcount is 1> 
code for successful registration 
<cfelse> 
code for full class 
<cfif> 

你必須在子查詢作爲最低使用cfqueryparam。如果mysql允許,也可以在select子句中使用它。

+0

註冊中可能尚不存在Child_ID和Class_ID。此外,如果我正確理解了您的建議,則必須在兩個課程中都有座位才能插入註冊。 – user3220394

+0

這些是細節。這個答案顯示了總體思路。 –

+0

我可能錯過了一些東西......它總是細節。如果在插入前兩個類都必須可用,您的解決方案將如何實現? – user3220394

0

您將需要使用MySQL事務性語義才能正確使用。 <CFLOCK>很有幫助,但並不是你需要的。

首先,你將需要InnoDB。

其次,你將需要一個包含「可用牀」或任何其他表的列。當該列的值達到零時,您已滿。如果有人取消,你就增加它。

三,你的SQL將需要這樣做:

BEGIN TRANSACTION; 

SELECT available_slots 
    FROM vacancy 
    WHERE classname IN ('baskets', 'archery') 
    FOR UPDATE; 

    /* check the values of available_slots. If all are > 0, 
    immediately do the sql to book the reservations. 
    if any are <= 0, no vacancy; do ROLLBACK; 
    */ 

UPDATE available_slots 
    SET vacancy=vacancy-1 
    WHERE classname IN ('baskets', 'archery') 


COMMIT; 

這裏的關鍵概念是,有一個單一的,可識別的,每一行對應一個稀缺的資源(在這種情況下,在射箭和籃子名額類)。在同時檢查其值時鎖定所需的更新行是避免過度提交稀缺資源的一種簡單且可靠的方法。

您可以只有一個行表,併爲每個事務鎖定一行。

+1

與給定班級的座位數量相比,如何優先進行註冊計數? – user3220394

+0

我不確定按照。這些語義如何防止經典的讀寫競爭條件?計數在一個單獨的語句中遞減,所以我不確定我看到'..for update'在這裏如何幫助。你可以解釋嗎? – Leigh