หากมีคำถาม ขอให้ไปโพสต์ลง เว็บบอร์ดจีทูจีเน็ตดอตคอม ตัวใหม่แทนน่ะครับ

หรือติดต่อเข้ามาทาง Inbox ที่ เฟซบุ๊ค ผมครับ

หน้าหลัก
ข่าวสาร - บทความ ทั้งหมด
VB 6/VB.Net
ASP/ASP.Net
จับฉ่ายคอมพิวเตอร์
เรียนรู้ผ่าน Flash Movie
บทความที่มีผู้ตอบล่าสุด  
 RSS Feeds
 ดาวน์โหลดโปรแกรม RSS Reader ได้ที่นี่ ...   Download โปรแกรม RSS Reader

Forum - www.g2gnet.com
Webmaster - www.g2gnet.com
Visitors - Session views
 5 2 6 1 6 1 0

7 ธันวาคม พ.ศ.2549
12 Users On-Line.
Visitors - Page views
 8 5 8 8 2 9 3
1 กุมภาพันธ์ พ.ศ.2551

Google   
เว็บ g2gnet.com
ขนาดตัวอักษร:  

การเขียนโปรแกรมโดยไม่ให้ค่า ID มีค่าซ้ำกันได้เลย - Source Code Visual Basic 6

Category »  VB 6/VB.Net
โดย : Webmaster เมื่อ 10/11/2552   เวลา: 01:51
(อ่าน : 37595) 
สำหรับหัวข้อนี้ มีหลายคนมากที่ถามผมมาตลอดเวลา ทั้งทางเมล์ หรือ MSN ... แหม ... ไอ้ตัวกระผมก็ไม่รู้จะตอบให้ยังไงดีล่ะครับ เพราะคุณใช้หนังสือ อ้างอิงตามตำรา หรือ ตามที่ครูบาอาจารย์ของท่านสอนมา ส่วนผมน่ะออกแบบฐานข้อมูล และ ก็เขียนโค้ดไปในอีกแบบนึง ... อย่างแรกผมใช้ Primary Key เป็นกุญแจหลักจริงๆ (และนำไปซ่อนไว้ ไม่ให้ผู้ใช้เห็น ... แต่เราผู้เขียนต้องใช้ในการอ้างอิงข้อมูล) และ ไม่ได้ใช้ตัวเลขแบบ Autonumber ... ส่วนรหัสลูกค้า รหัสสินค้า หรือ อื่นๆ ซึ่งจะมีชื่อฟิลด์ตามด้วย ID เช่น CustomerID, ProductID เหล่านี้ เพื่อให้ผู้ใช้งานใช้อ้างอิงถึงได้ ... ซึ่งฟิลด์เหล่านี้ไม่ใช่ Primary Key น่ะครับ ถึงแม้ว่ามันจะซ้ำกันไม่ได้ก็ตาม เพราะอะไร ... ก็เพราะว่า Primary Key จะไม่สามารถเปลี่ยนค่าได้ครับ หากเปลี่ยนไปแล้วจะมีผลกระทบต่อตารางอื่นๆได้ ส่วนพวกรหัส ID เหล่านี้คุณสามารถเปลี่ยนแปลงค่าได้ เพราะว่าผมไม่ได้เอารหัส ID เหล่านี้ไปเชื่อมโยงกับตารางข้อมูลตัวอื่น ... การเชื่อมโยงความสัมพันธ์ของตารางผมจะใช้ Primary Key เท่านั้นไงล่ะครับ ... ทีนี้เห็นความแตกต่างของแนวคิดหรือยังล่ะครับ ...
ประทานโทษครับ ... Field อ่านว่า "ฟิลด์" ครับ ไม่ได้อ่านว่า "ฟิวส์" เลยน่ะครับ ...

หลักการหมูน้อยอู๊ดๆ ... ก็อาศัยคุณสมบัติ Tag ของ TextBox (หรือจะใช้ตัวแปรแทนก็ได้) มารับค่าเดิมของรหัสลูกค้า รหัสสินค้า จำพวก ID ต่างๆนั่นแหละครับเอาไว้ก่อน ก่อนที่จะทำการบันทึกข้อมูล ก็นำไปเปรียบเทียบกับค่าใหม่ ... จบ ... เหอๆๆๆๆ
    ตรวจสอบการซ้ำกันของรหัสลูกค้า - CustomerID ... มันมีโอกาสเป็นได้ 2 กรณี คือ
  • เพิ่มข้อมูลใหม่ - ทำให้ txtCustomerID.Text จะไม่ตรงกันกับ txtCustomerID.Tag (เพราะค่านี้เป็นค่าว่าง)
  • แก้ไขข้อมูล - มีโอกาสเป็นไปได้ 2 ทาง คือ
    • ไม่มีการแก้ไขค่าใน txtCustomerID.Text จะทำให้ txtCustomerID.Text = txtCustomerID.Tag
      ดังนั้นไม่ต้องไปเสียเวลาทำการเปรียบเทียบค่าเดิมในฐานข้อมูล
    • มีการแก้ไขค่าใน txtCustomerID.Text ดังนั้น txtCustomerID.Text <> txtCustomerID.Tag
      ทำให้ต้องนำค่าไปตรวจสอบว่ามีค่า txtCustomerID.Text (ที่เปลี่ยนไป) ไปซ้ำกับค่าเดิมในฐานข้อมูลหรือไม่
    คุณสมบัติ Tag ของ TextBox มันเป็นเรื่องลี้ลับมากมาย ปิดบังกันมานานมากแล้ว ... โดนเปิดเผยซ่ะที ... 55555+
    เขียน VB มานับ 10 ปี ... เทคนิคง่ายๆนี้ ผมก็ยังใช้งานได้ไม่เปลี่ยนแปลงทั้ง VB6 หรือ VB.Net
ดาวน์โหลด
ดาวน์โหลด Source Code สำหรับ MS Visual Basic 6.0 - Service Pack 6
 ดาวน์โหลด Visual Basic 6.0 SP5: Run-Time Redistribution Pack
 ดาวน์โหลด Microsoft Data Access Object (MDAC) และ Jet 4.0 Update
 ดาวน์โหลด Microsoft Visual Basic Service Pack 6
ข้อมูลเพิ่มเติม
แจกฟรี Source Code VB6+Access โปรแกรมระบบฐานข้อมูลครุภัณฑ์ ภาคตารางข้อมูล
แจกฟรี Source Code VB6+Access โปรแกรมระบบฐานข้อมูลครุภัณฑ์ ภาคเขียนโปรแกรม


Project --> References ... เลือก MS ActiveX Data Objects 2.8 Library


หน้าจอในการเลือกทดสอบ


ตัวอย่างหน้าจอการเพิ่มข้อมูล


พิจารณา CustomerPK ซึ่งเป็น Primary Key ด้วยครับว่า ผมไม่ใช้ Autonumber
การสร้าง Primary Key ขึ้นมาใหม่ ให้ไปดูที่โปรแกรมย่อย SetupNewData()

ขอย้ำก่อนว่า ... ฟอร์มในการเพิ่ม หรือ แก้ไข มันจะต้องใช้ฟอร์มเดียวกัน โดยใช้เงื่อนไข IF .. THEN .. ELSE ที่อยู่ใน Form_Load ... แต่บทความนี้ผมแยกการทำงานให้เห็นอย่างชัดเจนเท่านั้นน่ะครับ

ตัวแปรแบบ Global ที่สำคัญๆ อยู่ใน gMyDB.bas

Global ConnDB As New ADODB.Connection
' ใช้ RecordSet เพียงแค่ 2 หรือ 3 ตัวก็เหลือเฟือแล้วครับ ... จะประกาศอะไรกันนักกันหนาหลายๆตัว ... 55555+
Global RS As New ADODB.Recordset
Global DS As New ADODB.Recordset
' นี่ก็เหมือนกันครับ ใช้ SQL Statement หรือ Query นั่นแหละ ... แค่ 2 ตัวก็พอแล้ว
Global Statement As String
Global SQLStmt As String

' ตัวแปรแบบที่มองเห็นกันหมดทั้ง Project มีไว้เพื่อส่งค่า Primary Key ข้ามฟอร์ม
Global gPK As Long
'
' กำหนดว่าเป็นการเพิ่ม หรือ แก้ไขข้อมูล
Global blnNewData As Boolean
กรณีของการเพิ่มข้อมูลใหม่ - จากฟอร์มหลัก

Private Sub cmdNewData_Click()

    ' ไม่จำเป็นต้องมี gPK เพราะจะเกิดการสร้างใหม่ภายหลัง
    
    ' ปกติฟอร์มในการเพิ่มและแก้ไขต้องใช้ตัวเดียวกัน ดังนั้นต้องระบุว่าเป็นการเพิ่มใหม่ หรือ แก้ไข
    ' กรณีนี้เป็นการเพิ่มข้อมูลเข้าไปใหม่  จึงกำหนดเป็น Dtac เอ้ย True
    blnNewData = True
    
    ' vbModal คือ การสั่งให้ฟอร์มที่ระบุ ให้อยู่เหนือฟอร์มอื่นๆ
    ' เปิดฟอร์มเพิ่มข้อมูลใหม่
    frmNewData.Show vbModal
    
End Sub
ฟอร์มการเพิ่มข้อมูล - frmNewData

Option Explicit
' ======================================================
' ตัวแปร 2 ตัวนี้เป็นตัวแปรที่สามารถมองเห็นได้ทั่วทั้งฟอร์ม ...

' ตัวแปรแบบ Local เพื่อรับค่า Primary Key ที่ส่งมา ... ทำไมต้องมีตัวรับเอาไว้ เพราะ ...
' ลดความผิดพลาดลง หากใช้ตัวแปรแบบ Global ตัวเดียวกันซ้ำไปซ้ำมา
Dim PK As Long

' ตัวแปรแบบ Local เพื่อรับค่าสถานะของการเพิ่ม หรือ แก้ไข เหตุผล เช่นเดียวกับด้านบน
' ลองไปดูที่ gMyDB.bas จะเห็นว่าตัวแปรนี้ผมมีเพียงตัวเดียว
' ดังนั้นจึงต้องแชร์กันใช้งานร่วมกับฟอร์มอื่นๆด้วย ... ส่วนหนึ่งที่เรียกว่า Optimized ... ลดการประกาศตัวแปร
Dim NewData As Boolean
' ======================================================

Private Sub Form_Load()
    txtCustomerID.Text = ""
    
    ' รับค่าจากตัวแปร Global
    NewData = blnNewData
    
    ' เวลาใช้งานจริงๆมันจะต้องเป็นฟอร์มเดียวกัน ผมจึงต้องเขียนเงื่อนไขไว้ดักรอ ...
    ' แต่ตอนนี้ผมแยกเป็นส่วนๆให้เห็นกันชัดๆ จะๆ ...
    
    ' กรณีของการเพิ่ม
    If blnNewData Then
        ' เพื่อให้แน่ใจ ควรกำหนดให้ Tag เป็นค่าว่างด้วย (ปกติมันว่างอยู่แล้วแหละครับ)
        txtCustomerID.Tag = ""
        
    ' กรณีของการแก้ไข
    Else
        ' ส่วนี้ทำการแก้ไข
    End If
    
End Sub

Private Sub cmdSave_Click()
    ' ก่อนทำการบันทึกข้อมูล ต้องตรวจสอบค่าที่จำเป็นต้องบันทึกก่อนเสมอ
    If Trim(txtCustomerID.Text) = "" Or Len(txtCustomerID.Text) = 0 Then
        MsgBox "กรุณาป้อนข้อมูลรหัสลูกค้าให้เรียบร้อยก่อนด้วย.", vbOKOnly + vbExclamation, "รายงานสถานะ"
        txtCustomerID.SetFocus
        Exit Sub
    End If
    
    ' ตรวจสอบการซ้ำกันของรหัสลูกค้า
    ' =================================================================
    ' เพิ่มข้อมูลใหม่ - ทำให้ txtCustomerID.Text จะไม่ตรงกันกับ txtCustomerID.Tag (ค่านี้จะต้องว่าง)
    ' =================================================================
    
    If txtCustomerID.Text <> txtCustomerID.Tag Then
        ' กระโดดไปฟังค์ชั่นในการตรวจสอบการซ้ำกันของ CustomerID
        If CheckExistID > 0 Then
            MsgBox "มีรหัสลูกค้า: " & Trim(txtCustomerID.Text) & " เรียบร้อยแล้ว กรุณาแก้ไขใหม่ด้วย.", _
                                vbOKOnly + vbExclamation, "รายงานสถานะ"
            txtCustomerID.SetFocus
            Exit Sub
        End If
    End If
    ' ================================
    ' ไปบันทึกข้อมูลได้เลย
    Call SaveData
    ' ================================

End Sub

' ฟังค์ชั่นตรวจสอบการซ้ำกันของรหัสลูกค้า - CustomerID
' จากนั้นส่งค่ากลับ หากเป็น 0 แสดงว่าไปไม่เกิดการซ้ำกันของข้อมูล
' ค่าส่งกลับมากกว่า 0 ... เกิดการซ้ำกัน จะต้องบังคับไม่สามารถเพิ่ม หรือ แก้ไขข้อมูลได้
Function CheckExistID() As Long
    
    ' สังเกตว่าตัวรอง ผมเลือกใช้  DS เป็น RecordSet
    ' คิดดูเอาครับว่า ผมประกาศ RecordSet ไว้แค่ 2 ตัวเท่านั้นเอง (RS/DS)
    Set DS = New Recordset
    ' ใช้คำสั่ง Count ของ SQL แทนก็ได้ ... โอ้ย ... เยอะแยะมีหลายวิธีครับ ลองไปคิดดูเล่นๆ
    SQLStmt = "SELECT * FROM tblCustomer  WHERE [CustomerID] = " & "'" & Trim(txtCustomerID.Text) & "'" & _
                            " ORDER BY [CustomerPK] "
    
    ' หากไม่ระบุเป็น adUseClient จะใช้ค่าเดิมที่ตั้งต้น (Default) เป็นแบบ adUseServer
    ' การใช้แบบ adUseClient เพื่อต้องการให้ใช้เมธอดของการนับ Record ได้ นั่นคือ
    ' DS.RecordCount
    DS.CursorLocation = adUseClient
    DS.Open SQLStmt, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
    ' ส่งค่านับที่ได้กลับคืน หากเป็น 0 แสดงว่าไม่ซ้ำ หากมากกว่า 0 แสดงว่าซ้ำ ... จบ
    CheckExistID = DS.RecordCount
    DS.Close:    Set DS = Nothing
End Function

' ===========================================
' โปรแกรมย่อยเพื่อทำการจัดเก็บข้อมูลลงสู่ตาราง
Sub SaveData()
' ===========================================

Set RS = New ADODB.Recordset
    ' เมื่อ NewData = True เป็นการเพิ่มข้อมูล
    If NewData = True Then
        ' ก่อนอื่นต้องทำการหาค่า Primary Key ตัวใหม่เสียก่อน โดยเรียกไปยังโปรแกรมย่อย SetupNewData
        Call SetupNewData
        Statement = "SELECT * FROM tblCustomer ORDER BY CustomerPK"
        
        ' นี่คือการสั่งให้เขียนข้อมูลลงไปได้ในตารางข้อมูล adOpenKeyset
        RS.Open Statement, ConnDB, adOpenKeyset, adLockOptimistic, adCmdText
        ' วิธีการของผม คือ แบบโบราณดั้งเดิม ตั้งแต่ยุค ODBC มา DAO และ ADO ในปัจจุบัน ... ของหายาก ... 55555+
        RS.AddNew
        ' ส่วนนี้จะบันทึกเฉพาะส่วนที่สำคัญๆ เช่น Primary Key หรือ วันที่เริ่มเก็บข้อมูล
        RS("CustomerPK") = PK
    
    Else
        
        ' ส่วนของการแก้ไขจะค้นหาข้อมูลด้วย PK ที่ถูกส่งมาก่อนล่วงหน้า
    
    End If
    
    ' กรณีของการเพิ่ม หรือ แก้ไข ก็จะใช้งานร่วมกัน ... ไม่ได้เปลี่ยนแปลงเลย
    ' ผมว่าผมเขียนโค้ดแบบดั้งเดิมเนี่ย มันอ่านง่าย แก้ไข หาจุดบกพร่องได้ง่ายง่ายกว่า INSERT, UPDATE เป็นไหนๆ
    RS("CustomerID") = "" & Trim(txtCustomerID.Text)
    
    ' สั่ง Update DataBase
    RS.Update
    
    MsgBox "บันทึกการเพิ่มข้อมูลเรียบร้อย", vbOKOnly + vbInformation, "รายงานสถานะ"
    RS.Close:    Set RS = Nothing
    Set frmNewData = Nothing
    
    ' ปิดหน้าจอกลับเลย
    Unload Me
End Sub

' ===================== สร้าง Record ใหม่ ==========================
' ต้องคำนวณหาค่า Primary Key ให้เรียบร้อยก่อน
' นี่คือเหตุผลที่ผมไม่ยอมใช้ Autonumber เมื่อไรก็ตามที่ผมจะย้ายไปใช้ DB ตัวอื่นๆ ...
' นอกจากนี้ยังสะดวกต่อการทดสอบความสัมพันธ์ของตารางข้อมูลที่ออกแบบเอาไว้ด้วย
Sub SetupNewData()
' =============================================================

Set DS = New Recordset
    ' นำข้อมูลจากตารางมาคำนวณหาค่าสูงสุด ด้วยคำสั่ง MAX ของ SQL
    SQLStmt = "SELECT Max(CustomerPK) As MaxPK FROM tblCustomer "
    
    ' กำหนดให้อ่านเดินหน้าอย่างเดียว เพื่อความรวดเร็ว  adOpenForwardOnly
    ' โปรแกรมที่คุณเขียนออกมามันทำงานช้า หรือ เร็ว ก็อยู่ที่ตัวนี้ด้วยแหละครับ
    DS.Open SQLStmt, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
    
    ' หากฐานข้อมูลไม่มีข้อมูลใดๆ จะทำให้เกิดค่า Null ขึ้นมา ดังนั้นต้องดัก Error ไว้ก่อน
    ' หรือเกิด Run Time Error 94
        
If IsNull(DS("MaxPK")) Then PK = 1 Else ' เมื่อได้ค่าสูงสุดมาแล้ว ให้บวกเพิ่มขึ้นอีก 1 ก็จะได้ Primary Key ตัวใหม่ทันที PK = DS("MaxPK") + 1 End If DS.Close: Set DS = Nothing End Sub
กรณีของการแก้ไขข้อมูลเดิม - จากฟอร์มหลัก

Private Sub cmdEditData_Click()
    ' ปกติในการแก้ไขข้อมูล จะเกิดจากการดับเบิ้ลคลิ๊กในแถวของจำพวกตารางกริดทั้งหลาย
    ' ซึ่งจะส่งค่า Primary Key ไป ... ซึ่งผมเคยอธิบายไว้แล้วว่าค่านี้จะถูกซ่อนเอาไว้ในหลักใดหลักหนึ่งของตารางกริด
    ' และไม่ใช่เป็นการส่งค่า CustomerID ไปน่ะครับ ... ตัวอย่างนี้ส่งค่า 1 ไป
    gPK = 1
    
    ' ปกติฟอร์มในการเพิ่มและแก้ไขต้องใช้ตัวเดียวกัน ดังนั้นต้องระบุว่าเป็นการเพิ่มใหม่ หรือ แก้ไข
    ' กรณีนี้เป็นการแก้ไขข้อมูล จึงกำหนดเป็น False
    blnNewData = False
    
    
    ' vbModal คือ การสั่งให้ฟอร์มที่ระบุ ให้อยู่เหนือฟอร์มอื่นๆ
    ' เปิดฟอร์มแก้ไขข้อมูล
    frmEditData.Show vbModal
    
End Sub
ฟอร์มการแก้ไขข้อมูลเดิม - frmEditData

Option Explicit
' ======================================================
' ตัวแปร 2 ตัวนี้เป็นตัวแปรที่สามารถมองเห็นได้ทั่วทั้งฟอร์ม ...

' ตัวแปรแบบ Local เพื่อรับค่า Primary Key ที่ส่งมา ... ทำไมต้องมีตัวรับเอาไว้ เพราะ ...
' ลดความผิดพลาดลง หากใช้ตัวแปรแบบ Global ตัวเดียวกันซ้ำไปซ้ำมา
Dim PK As Long

' ตัวแปรแบบ Local เพื่อรับค่าสถานะของการเพิ่ม หรือ แก้ไข เหตุผล เช่นเดียวกับด้านบน
' ลองไปดูที่ gMyDB.bas จะเห็นว่าตัวแปรนี้ผมมีเพียงตัวเดียว
' ดังนั้นจึงต้องแชร์กันใช้งานร่วมกับฟอร์มอื่นๆด้วย ... ส่วนหนึ่งที่เรียกว่า Optimized ... ลดการประกาศตัวแปร
Dim NewData As Boolean
' ======================================================

Private Sub Form_Load()
    txtCustomerID.Text = ""
    
    ' รับค่าจากตัวแปร Global
    NewData = blnNewData
    
    ' เวลาใช้งานจริงๆมันจะต้องเป็นฟอร์มเดียวกัน ผมจึงต้องเขียนเงื่อนไขไว้ดักรอ ...
    ' แต่ตอนนี้ผมแยกเป็นส่วนๆให้เห็นกันชัดๆ จะๆ ...
    
    ' กรณีของการเพิ่ม
    If blnNewData Then
        '
        '
    ' กรณีของการแก้ไข
    Else
        ' รับค่า Primary Key จากตัวแปร Global
        PK = gPK
        
        ' แสดงผลข้อมูลตามที่ได้ส่ง Primary Key มา
        Call RecordToScreen
        
    End If
    
End Sub

Sub RecordToScreen()
    ' โหลดข้อมูลเข้าสู่หน้าจอ ซึ่งปกติเราจะใช้ Primary Key เป็นเงื่อนไขในการค้นหา เพราะ
    ' 1. Primary Key คือ ค่าที่ไม่ซ้ำกัน หากไม่เอามาใช้แล้วจะมีไว้เพื่อทำ ... อะไรล่ะครับ
    ' 2. ตัวเลข จะสามารถทำงานได้เร็วกว่าการเปรียบเทียบแบบชุดตัวอักขระ
    
    ' สมมุติใช้ Primary Key = 1 ซึ่ง CustomerID จะเท่ากับ "CUS00001"
    ' ตัวแปร PK ได้ทำการรับค่ามาจากตัวแปร gPK (แบบ Global) ใน Form_Load แล้ว
    
    ' สังเกตว่าตัวหลักๆ ผมเลือกใช้  RS เป็น RecordSet (หรือตารางเทียม)
    ' คิดดูเอาครับว่า ผมประกาศ RecordSet ไว้แค่ 2 ตัวเท่านั้นเอง (RS/DS)
    Set RS = New Recordset
    Statement = "SELECT tblCustomer.* " & _
                            " FROM tblCustomer  " & _
                            " WHERE [CustomerPK] = " & PK
    
    ' อ่านข้อมูลอย่างเดียว adOpenForwardOnly จะทำให้เข้าถึงข้อมูลได้เร็ว
    RS.Open Statement, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
    
    ' การนำเอา Double Quote "" มาขวางไว้ก่อนเพื่อป้องกันค่า Null ซึ่งทำให้เกิด Run Time Error ได้ครับ
    txtCustomerID.Text = "" & RS("CustomerID")
    ' คัดลอกรหัสลูกค้า (ID) เอาไว้ใน Tag ก่อน
    txtCustomerID.Tag = "" & RS("CustomerID")
    
    ' ปิดตาราง
    RS.Close:    Set RS = Nothing
    
End Sub

Private Sub cmdSave_Click()
    ' ก่อนทำการบันทึกข้อมูล ต้องตรวจสอบค่าที่จำเป็นต้องบันทึกก่อนเสมอ
    If Trim(txtCustomerID.Text) = "" Or Len(txtCustomerID.Text) = 0 Then
        MsgBox "กรุณาป้อนข้อมูลรหัสลูกค้าให้เรียบร้อยก่อนด้วย.", vbOKOnly + vbExclamation, "รายงานสถานะ"
        txtCustomerID.SetFocus
        Exit Sub
    End If
    
    ' ตรวจสอบการซ้ำกันของรหัสลูกค้า
    ' =================================================================
    ' แก้ไขข้อมูล - มีโอกาสได้ 2 ทาง คือ
    '      1. ไม่มีการแก้ไขค่าใน txtCustomerID.Text จะทำให้ txtCustomerID.Text = txtCustomerID.Tag
    '           ดังนั้นไม่ต้องไปเสียเวลาทำการเปรียบเทียบค่าเดิมในฐานข้อมูล
    '
    '      2. มีการแก้ไขค่าใน txtCustomerID.Text ดังนั้น txtCustomerID.Text <> txtCustomerID.Tag ทำให้
    '          ต้องนำค่าไปตรวจสอบว่ามีค่า txtCustomerID.Text (ที่เปลี่ยนไป) ไปซ้ำกับค่าเดิมในฐานข้อมูลหรือไม่
    ' =================================================================
    
    If txtCustomerID.Text <> txtCustomerID.Tag Then
        ' กระโดดไปฟังค์ชั่นในการตรวจสอบการซ้ำกันของ CustomerID
        If CheckExistID > 0 Then
            MsgBox "มีรหัสลูกค้า: " & Trim(txtCustomerID.Text) & " เรียบร้อยแล้ว กรุณาแก้ไขใหม่ด้วย.", _
                                vbOKOnly + vbExclamation, "รายงานสถานะ"
            txtCustomerID.SetFocus
            Exit Sub
        End If
    End If
    ' ================================
    ' ไปบันทึกข้อมูลได้เลย
    Call SaveData
    ' ================================

End Sub

' ฟังค์ชั่นตรวจสอบการซ้ำกันของรหัสลูกค้า - CustomerID
' จากนั้นส่งค่ากลับ หากเป็น 0 แสดงว่าไปไม่เกิดการซ้ำกันของข้อมูล
' ค่าส่งกลับมากกว่า 0 ... เกิดการซ้ำกัน จะต้องบังคับไม่สามารถเพิ่ม หรือ แก้ไขข้อมูลได้
Function CheckExistID() As Long
    
    ' สังเกตว่าตัวรอง ผมเลือกใช้  DS เป็น RecordSet
    ' คิดดูเอาครับว่า ผมประกาศ RecordSet ไว้แค่ 2 ตัวเท่านั้นเอง (RS/DS)
    Set DS = New Recordset
    ' ใช้คำสั่ง Count ของ SQL แทนก็ได้ ... โอ้ย ... เยอะแยะมีหลายวิธีครับ ลองไปคิดดูเล่นๆ
    SQLStmt = "SELECT * FROM tblCustomer  WHERE [CustomerID] = " & "'" & Trim(txtCustomerID.Text) & "'" & _
                            " ORDER BY [CustomerPK] "
    
    ' หากไม่ระบุเป็น adUseClient จะใช้ค่าเดิมที่ตั้งต้น (Default) เป็นแบบ adUseServer
    ' การใช้แบบ adUseClient เพื่อต้องการให้ใช้เมธอดของการนับ Record ได้ นั่นคือ
    ' DS.RecordCount
    DS.CursorLocation = adUseClient
    DS.Open SQLStmt, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
    ' ส่งค่านับที่ได้กลับคืน หากเป็น 0 แสดงว่าไม่ซ้ำ หากมากกว่า 0 แสดงว่าซ้ำ ... จบ
    CheckExistID = DS.RecordCount
    DS.Close:    Set DS = Nothing
End Function

' ===========================================
' โปรแกรมย่อยเพื่อทำการจัดเก็บข้อมูลลงสู่ตาราง
Sub SaveData()
' ===========================================

Set RS = New ADODB.Recordset
    ' ตัวอย่างนี้ผมจะข้ามการเพิ่มข้อมูล เพราะ NewData  = False
    If NewData = True Then
        
        '
    
    Else
        Statement = "SELECT tblCustomer.* " & _
                                " FROM tblCustomer " & _
                                " WHERE [CustomerPK] = " & PK
        ' นี่คือการสั่งให้เขียนข้อมูลลงไปได้ในตารางข้อมูล adOpenKeyset
        RS.Open Statement, ConnDB, adOpenKeyset, adLockOptimistic, adCmdText
    
    End If
    
    ' กรณีของการเพิ่ม หรือ แก้ไข ก็จะใช้งานร่วมกัน ... ไม่ได้เปลี่ยนแปลงเลย
    ' ผมว่าผมเขียนโค้ดแบบดั้งเดิมเนี่ย มันอ่านง่าย แก้ไข หาจุดบกพร่องได้ง่ายง่ายกว่า INSERT, UPDATE เป็นไหนๆ
    RS("CustomerID") = "" & Trim(txtCustomerID.Text)
    
    ' สั่ง Update DataBase
    RS.Update
    
    MsgBox "บันทึกการแก้ไขข้อมูลเรียบร้อย", vbOKOnly + vbInformation, "รายงานสถานะ"
    RS.Close:    Set RS = Nothing
    Set frmEditData = Nothing
    
    ' ปิดหน้าจอกลับเลย
    Unload Me
End Sub
Conclusion:
ให้ลองสังเกตตรงโปรแกรมย่อย SaveData เพื่อทำการบันทึกข้อมูลลงตารางน่ะครับ ... ด้วยวิธีเก๋าๆแบบโบราณ จึงทำให้ผมสามารถเพิ่ม หรือ แก้ไขข้อมูลได้ ในโปรแกรมย่อยตัวเดียวกัน และไม่ต้องทำ Transaction เพราะการสั่ง UPDATE จะอยู่ล่างสุดเลย หากเกิดปัญหาใดๆขึ้นมา ก่อนถึงคำสั่ง UPDATE ก็จะไม่มีการบันทึกข้อมูลลงไปได้เลยครับ อีกทั้งการคอยดัก Bug ก็ง่ายกว่า ... การเรียนรู้ของผมใช้สไตล์แบบ "เรียนรู้นอกระบบ รบนอกตำรา" ... ดังนั้นพี่น้องจึงไม่เคยเห็นวิธีการเช่นนี้อยู่ที่ใดๆตามหนังสือ ตามเว็บไซต์ หรือที่อื่นๆ ยกเว้นก็แต่ที่นี่ ... แต่สิ่งที่ผมถ่ายทอดออกมา ด้วยแนวคิดแบบนี้ ... พี่น้องยังสามารถนำไปใช้กับ VB.NET หรือ ภาษาตัวอื่นๆได้อย่างสบายๆ โดยไม่ต้องเปลี่ยนแปลงกระบวนการใดๆเลยก็ได้ครับ ...

ย้ำจุดยืนเดิม ... โค้ดไทย ซอฟท์แวร์ไทย ... ไม่ไปนอก (โว้ย) ...


จี ทู จี เน็ต ดอต คอม - g2gNet Dot Com
เลขทะเบียนพาณิชย์อิเล็กทรอนิกส์ 0407314800231
CopyLeft © 2004 - 2099 g2gNet.Com All rights reserved.
Email: [email protected] หรือ โทร. 08-6862-6560