|
ต้องบอกกล่าวก่อนว่าแอดมินไม่เคยนำวิธีการคิดแบบ Normalization มาใช้ในการอธิบาย แต่จะใช้ความสัมพันธ์ของตารางข้อมูลมาประกอบการพิจารณาการออกแบบเท่านั้น โดยแอดมินจะใช้แค่ 2 แบบ คือ One To One และ One To Many ...
ความสัมพันธ์ของตารางข้อมูลแบบ One To One ... เกิดจากการที่มีข้อมูลซ้ำๆขึ้นมาในตารางข้อมูล ตอนขณะที่ยังเป็นข้อมูลดิบอยู่ ดังนั้นเราต้องแยกฟิลด์ข้อมูลที่ซ้ำๆกันออกไปอยู่อีกตาราง เช่น บุคคล (หรือลูกค้า) อาศัยอยู่ในจังหวัดขอนแก่นได้หลายคน (แล้วจังหวัดอื่นๆอีกล่ะ) นักศึกษาเรียนสาขาดนตรีอยู่หลายคน (แล้วสาขาอื่นๆอีกล่ะ) ทั้งนี้ทั้งนั้นเมื่อมองย้อนกลับมาจะสังเกตได้ว่า บุคคล 1 คน จะอยู่ในจังหวัดใดจังหวัดหนึ่งตามบัตรประชาชนเท่านั้น (หรืออยู่อาศัยสักที่ใดที่หนึ่ง) และ นักศึกษา 1 คน สามารถเข้าศึกษาในสาขาใดสาขาหนึ่งเท่านั้น (เฉพาะสถานศึกษานั้นๆ) ความสัมพันธ์แบบนี้เราถึงได้เรียกว่า หนึ่งต่อหนึ่ง หรือ One To One ... (อ่านรายละเอียดเพิ่มเติม VB6+Access โปรแกรมระบบฐานข้อมูลครุภัณฑ์ ภาคตารางข้อมูล)
กระบวนการคิดในการออกแบบความสัมพันธ์ตารางข้อมูลแบบ One To One ...
- จะมี หลัก (Column) เราเรียกว่า ฟิลด์ข้อมูล (Field) ซึ่งแต่ละฟิลด์ที่ได้มานั้น ก็มาจากความต้องการในการจัดเก็บข้อมูลในแต่ละโปรเจค โดยนำเอา Field มาเป็นตัวหลักในการออกแบบตารางข้อมูล ทั้งการกำหนดชื่อฟิลด์ และชนิดของข้อมูล
- จะมี แถว (Row) เรียกว่า รายการ (Record) ซึ่งรวบรวมข้อมูลต่างมาจากฟิลด์ข้อมูล
- จากนั้นให้ป้อนข้อมูลดิบ (Raw Data) โดยให้มองว่านี่คือ ตารางหลัก (Master Table) จากนั้นมาพิจารณาว่า ฟิลด์ใดมีข้อมูลซ้ำๆกัน ให้จับฟิลด์นั้นแยกออกไปอยู่อีกตาราง เรียกว่า ตารางย่อย (Detail Table) แล้วเชื่อมความสัมพันธ์มันกลับเข้ามายังตารางหลัก ด้วยการใช้ตัวเลขจำนวนเต็ม
- ในตารางหลักจะมีกุญแจรอง Foreign Key หรือ FK เพื่อใช้ในการเชื่อมความสัมพันธ์ฟิลด์ข้อมูลเข้าไปหาตารางย่อยอื่นๆได้ ค่านี้ต้องเป็นตัวเลขเท่านั้น (หรือใครอยากจะใช้แบบอื่นก็ทำได้) ... นี่คือความสัมพันธ์แบบ One To One
- ในตารางหลัก (Master Table) จะมี Primary Key หรือ PK หรือ กุญแจหลัก เพื่อใช้ในการอ้างถึงข้อมูลที่ต้องการ โดยค่านี้ควรจะเป็นตัวเลขแบบจำนวนเต็ม (Number) เพื่อให้ง่าย สะดวก รวดเร็ว แม่นยำ ต่อการสร้าง ค่านี้จะต้องไม่เกิดค่าที่ซ้ำกัน และไม่สามารถเปลี่ยนค่าได้
- ในตารางหลัก (Master Table) จะมี ID หรือ IDentifier เพื่อระบุรหัสประจำตัวของคน หรือสิ่งของ เช่น รหัสลูกค้า (อาจจะยึดตามบัตรประชาชน หรือเลขที่ผู้เสียภาษี) รหัสนักศึกษา หรือรหัสสินค้า เป็นต้น ซึ่งค่า ID จะไม่สามารถมีค่าที่ซ้ำกันได้ แต่สามารถเปลี่ยนแปลงค่าได้ (อาจจะมาจากการคีย์ข้อมูลผิด หรือต้องการเปลี่ยนแปลงในภายหลัง) ซึ่งค่าที่เปลี่ยนไปนั้นต้องไม่ไปซ้ำกับค่าอื่นๆที่มีอยู่แล้ว ยกเว้นค่าเดิมของตัวมันเอง
- ในตารางย่อย (Detail Table) จะต้องมี Primary Key เพื่อเชื่อมความสัมพันธ์กลับไปยังตารางหลัก แต่ไม่จำเป็นต้องมี IDentifier เช่น รหัสจังหวัด รหัสหน่วยนับ หรือรหัสประเภทสินค้า
การทำ Query MS Access ...
การกำหนด Maximum Length หรือความยาวสูงสุดในการป้อนค่าลง TextBox Control ตามจำนวนจากการออกแบบตารางข้อมูล ...
มาดูโค้ดในส่วนสำคัญของรายการลูกค้า (frmCustomerMain.vb) ... ส่วนของการแสดงผล และทำการค้นหา
- Private Sub SearchData(Optional ByVal blnSearch As Boolean = False)
- ' ดักค่าว่างกรณีที่ต้องการให้ค้นหา (blnSearch = True)
- If blnSearch And Trim(txtSearch.Text) = "" Then Exit Sub
- '/ เคลียร์ค่าใน DataSet ... นั่นก็คือ ลบรายการออกจาก DataGridView
- DAS1.Clear()
- strSQL = _
- " SELECT tblCustomer.CustomerPK, tblCustomer.CustomerID, tblCustomer.CustomerName, " & _
- " tblCustomer.PostCode, tblCustomer.Telephone, " & _
- " tblCustomer.eMail, tblCustomer.LineID, tblCustomer.Facebook, tblProvince.ProvinceName " & _
- " FROM tblCustomer INNER JOIN tblProvince ON tblCustomer.ProvinceFK = tblProvince.ProvincePK "
- '/ การทำ Query เพื่อเลือกเฉพาะ Field ที่ต้องการค้นหามาเท่านั้น และแสดงผลฟิลด์หลัก
- '/ เนื่องจากมันเป็นการ Bound Data Control ดังนั้นเราจึงต้องซ่อนหลักบางตัวเอาไว้ แต่การค้นหาก็ยังทำได้เหมือนเดิม
- If blnSearch Then
- strSQL = strSQL & _
- " WHERE " & _
- " [CustomerID] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
- " [CustomerName] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
- " [ProvinceName] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
- " [PostCode] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
- " [Telephone] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
- " [eMail] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
- " [LineID] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
- " [Facebook] " & " Like '%" & Trim(txtSearch.Text) & "%'" & _
- " ORDER BY CustomerPK "
- Else
- strSQL = strSQL & " ORDER BY CustomerPK "
- End If
- '/ ยัดใส่ตารางที่มาจากการทำ Query ให้กับ DataAdapter
- DAP1 = New OleDb.OleDbDataAdapter(strSQL, Conn)
- DAP1.Fill(DAS1, "Customer")
- ' / --------------------------------------------------------------------------------
- ' / B O U N D --- D A T A --- C O N T R O L
- ' / เหมาะสำหรับการค้นหาข้อมูล เพื่อนำมาแสดงผล
- ' / Bind Data into DataGridView
- dgvData.DataSource = DAS1.Tables("Customer")
- ' / --------------------------------------------------------------------------------
- ' ตั้งค่าคุณสมบัติ DataGridView (จริงๆแยกออกไปเป็นโปรแกรมย่อยน่าจะดีกว่าครับ)
- With dgvData
- .RowHeadersVisible = False
- .AllowUserToAddRows = False
- .AllowUserToDeleteRows = False
- .AllowUserToResizeRows = False
- .MultiSelect = False
- .SelectionMode = DataGridViewSelectionMode.FullRowSelect
- .ReadOnly = True
- '// Data rows
- .Font = New Font("Tahoma", 10)
- .RowTemplate.MinimumHeight = 27
- .RowTemplate.Height = 27
- '// Column Header
- .ColumnHeadersHeight = 30
- .ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing
- '// Autosize Column
- .AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill
- '// Even-Odd Color
- .AlternatingRowsDefaultCellStyle.BackColor = Color.Honeydew
- ' ปรับรูปแบบ Header
- With .ColumnHeadersDefaultCellStyle
- .BackColor = Color.Navy
- .ForeColor = Color.White
- .Font = New Font(dgvData.Font, FontStyle.Bold)
- End With
- ' หลัก 0 ซึ่งผมจะนำไปซ่อนเอาไว้ไม่ให้ผู้ใช้เห็น และปกติจะเป็นค่า Primary Key
- .Columns("CustomerPK").Visible = False
- .Columns("CustomerID").HeaderText = "รหัสลูกค้า"
- .Columns("CustomerName").HeaderText = "ชื่อลูกค้า"
- .Columns("Telephone").HeaderText = "เบอร์โทรศัพท์"
- .Columns("ProvinceName").HeaderText = "จังหวัด"
- '/ พวกที่ซ่อนหลักไว้ แต่ยังนำมาเป็นคีย์เวิร์ดเพื่อค้นหาได้
- .Columns("PostCode").Visible = False
- .Columns("eMail").Visible = False
- .Columns("LineID").Visible = False
- .Columns("Facebook").Visible = False
- End With
- lblRecordCount.Text = "[จำนวน : " & dgvData.RowCount & " รายการ.]"
- txtSearch.Text = ""
- dgvData.Focus()
- End Sub
- Private Sub txtSearch_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles txtSearch.KeyPress
- If Trim(txtSearch.Text) = "" Or Len(Trim(txtSearch.Text)) = 0 Then Exit Sub
- If e.KeyChar = Chr(13) Then '// Press Enter
- '// No beep.
- e.Handled = True
- '/ ดักจับตัวอักขระอันไม่พึงปรารถนาสำหรับฐานข้อมูล เช่น ', * หรือ %
- txtSearch.Text = Trim(txtSearch.Text).Replace("*", "").Replace("'", "").Replace("%", "")
- '/ SearchData(True) หมายความว่า เป็นการค้นหาข้อมูล ดังนั้นจึงต้องใช้ True
- Call SearchData(True)
- End If
- End Sub
คัดลอกไปที่คลิปบอร์ด
มาดูโค้ดในส่วนสำคัญของรายละเอียดลูกค้า (frmCustomerDetail.vb) ... ส่วนของการแสดงผลข้อมูล
- ' / --------------------------------------------------------------------------------
- Private Sub frmCustomerDetail_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
- Me.CenterToScreen()
- Me.KeyPreview = True '/ สามารถใช้ KeyDown การกดปุ่มฟังค์ชั่นต่างๆบนฟอร์มได้ เช่น F8, F10
- ' / เคลียร์หน้าจอในการแสดงผล
- Call SetupScreen()
- ' / รับค่า Public มาใส่ในตัวแปรแบบ Local แทน (เผื่อมีการเรียกใช้งานของฟอร์มอื่น)
- NewData = blnNewData
- ' / โหลดรายชื่อจังหวัดเข้ามา (หรือตารางย่อยอื่นๆ)
- Call LoadProvince()
- ' /
- ' / การเพิ่มข้อมูล NewData = True
- If NewData Then
- '/ ------------------------------------------------------------------
- '/ กำหนด Tag เป็นค่าว่าง เพื่อนำไปเปรียบเทียบกับ Text ตอนก่อนบันทึกข้อมูล
- txtCustomerID.Tag = ""
- chkAutoID.Enabled = True
- chkAutoID.Checked = True
- '/ ------------------------------------------------------------------
- '/ NewData = False คือการแก้ไขข้อมูล
- Else
- '/ ------------------------------------------------------------------
- ' / อ่านค่า Primary Key จากการเลือกในตารางกริด โดยอ้างถึงจากฟอร์มที่เรียกมันมา คือ frmCustomerMain
- ' / โดยเราเอาข้อมูลหลักแรก (Index = 0) จาก DataGridView ที่เราซ่อนหลัก PK เอาไว้มาใช้งาน
- PK = frmCustomerMain.dgvData.Item(0, frmCustomerMain.dgvData.CurrentRow.Index).Value
- ' / นำข้อมูลมาแสดงผล
- Call RecordToScreen()
- chkAutoID.Enabled = False
- chkAutoID.Checked = False
- '/ ------------------------------------------------------------------
- End If
- End Sub
คัดลอกไปที่คลิปบอร์ด- ' / --------------------------------------------------------------------------------
- ' / โปรแกรมย่อยในการนำข้อมูลจาก DataBase มาแสดงผล
- ' / --------------------------------------------------------------------------------
- Sub RecordToScreen()
- strSQL = _
- " SELECT tblCustomer.*, tblProvince.ProvinceName " & _
- " FROM tblCustomer INNER JOIN tblProvince ON tblCustomer.ProvinceFK = tblProvince.ProvincePK " & _
- " WHERE CustomerPK = " & PK & _
- " ORDER BY CustomerPK "
- Try
- If Conn.State = ConnectionState.Closed Then Conn.Open()
- DAP1 = New OleDb.OleDbDataAdapter(strSQL, Conn)
- Dim DT As New DataTable
- DAP1.Fill(DT)
- txtCustomerID.Text = DT(0).Item("CustomerID").ToString()
- '/ ------------------------------------------------------------------
- '/ VERY IMPORTANT - ใจกลางความรู้สึกดีดี 5555+
- '/ นำค่าเดิมจาก CustomerID.Text ไปเก็บไว้ที่ CustomerID.Tag
- '/ โดยเราจะทำการเปรียบเทียบ 2 ค่าในเหตุการณ์ btnSave_Click
- txtCustomerID.Tag = txtCustomerID.Text
- '/ ------------------------------------------------------------------
- With DT(0)
- txtCustomerName.Text = .Item("CustomerName").ToString()
- txtAddress.Text = .Item("Address").ToString
- txtAmphur.Text = .Item("Amphur").ToString
- txtPostCode.Text = .Item("PostCode").ToString
- txtTelephone.Text = .Item("Telephone").ToString
- txtFacsimile.Text = .Item("Facsimile").ToString
- txtContactPerson.Text = .Item("ContactPerson").ToString
- txtEmail.Text = .Item("Email").ToString
- txtLineID.Text = .Item("LineID").ToString
- txtFacebook.Text = .Item("Facebook").ToString
- '/ คือเอาค่าชื่อจังหวัดในตารางข้อมูล มาเปรียบเทียบกับค่าที่มีอยู่ใน ComboBox (ส่วนของ DisplayMember)
- cmbProvince.Text = .Item("ProvinceName").ToString
- End With
- DAP1.Dispose()
- txtCustomerID.Focus()
- Catch ex As Exception
- MsgBox(ex.ToString())
- End Try
- End Sub
คัดลอกไปที่คลิปบอร์ด
มาดูโค้ดในส่วนสำคัญของรายละเอียดลูกค้า (frmCustomerDetail.vb) ... ส่วนของการบันทึกข้อมูล
- ' / --------------------------------------------------------------------------------
- ' / โปรแกรมย่อยในการบันทึกข้อมูล
- ' / --------------------------------------------------------------------------------
- Sub SaveData()
- Try
- '/ ------------------------------------------------------------------
- '/ กรณีเพิ่มข้อมูลใหม่ NewData = True ต้องทำการ INSERT เข้าไปใหม่ก่อน
- If NewData Then
- '/ ------------------------------------------------------------------
- '/ ต้องหาค่า Primary Key ใหม่อีกรอบ (เพื่อความมั่นใจ)
- '/ และตอนนี้ไม่ว่าจะเป็นการเพิ่มหรือแก้ไข ค่า PK จะเหมือนกันแล้ว
- strSQL = " SELECT MAX(tblCustomer.CustomerPK) AS MaxPK FROM tblCustomer "
- PK = SetupNewData(strSQL)
- '/ ------------------------------------------------------------------
- Using Cmd As New OleDb.OleDbCommand
- With Cmd
- .Connection = Conn
- .CommandType = CommandType.Text
- '/ ------------------------------------------------------------------
- '/ เลือกเฉพาะฟิลด์ที่สำคัญในการ INSERT มาก่อน ที่เหลือค่อย UPDATE ตามหลัง
- '/ เพื่อลดพารามิเตอร์ในการใช้งานทั้งการเพิ่ม และแก้ไขข้อมูลให้ใช้งานร่วมกันได้
- Cmd.CommandText = _
- " INSERT INTO tblCustomer (CustomerID, DateAdded, CustomerPK) VALUES (@CID, @DateAdded, @CPK)"
- With .Parameters
- .Add("@CID", OleDbType.VarChar).Value = txtCustomerID.Text
- .Add("@DateAdded", OleDbType.Date).Value = Now()
- '/ ------------------------------------------------------------------
- .Add("@CPK", OleDbType.Integer).Value = PK
- '/ ------------------------------------------------------------------
- End With
- '/ Insert new record.
- .ExecuteNonQuery()
- .Parameters.Clear()
- '/ ------------------------------------------------------------------
- End With
- End Using
- End If
- '/ ------------------------------------------------------------------
- '/ เริ่มต้นทำการ UPDATE โดยใช้ได้ทั้งการเพิ่มและแก้ไขข้อมูล
- '/ เหตุที่ผมเลือก Command Method มาใช้งาน
- '/ เพราะตอนใช้งานจริงๆต้องมีการตรวจสอบข้อมูลก่อนการบันทึกเสมอ
- '/ ซึ่งจะทำให้ง่ายในการดักค่าการตรวจสอบต่างๆครับผม
- '/ ------------------------------------------------------------------
- Using Cmd As New OleDb.OleDbCommand()
- With Cmd
- .Connection = Conn
- .CommandType = CommandType.Text
- .CommandText = _
- " UPDATE tblCustomer SET " & _
- " CustomerID=?, CustomerName=?, Address=?, Amphur=?, ProvinceFK=?, " & _
- " PostCode=?, Telephone=?, Facsimile=?, ContactPerson=?, EMail=?, LineID=?, Facebook=?, " & _
- " DateModified=? " & _
- " WHERE CustomerPK=? "
- With .Parameters
- .AddWithValue("@CID", txtCustomerID.Text.Trim)
- .AddWithValue("@CName", txtCustomerName.Text.Trim)
- .AddWithValue("@Address", txtAddress.Text.Trim)
- .AddWithValue("@Amphur", txtAmphur.Text.Trim)
- '/ ------------------------------------------------------------------
- ' / กรณีของ ComboBox ให้เอาค่า MemberValue มาใช้งาน (อันนี้ใน VB6 จะไม่มี)
- .AddWithValue("@ProvinceFK", cmbProvince.SelectedValue)
- '/ ------------------------------------------------------------------
- .AddWithValue("@PostCode", txtPostCode.Text.Trim)
- .AddWithValue("@Telephone", txtTelephone.Text.Trim)
- .AddWithValue("@Facsimile", txtFacsimile.Text.Trim)
- .AddWithValue("@ContactPerson", txtContactPerson.Text.Trim)
- .AddWithValue("@EMail", txtEmail.Text.Trim)
- .AddWithValue("@LineID", txtLineID.Text.Trim)
- .AddWithValue("@Facebook", txtFacebook.Text.Trim)
- .AddWithValue("@DateModified", FormatDateTime(Now(), DateFormat.GeneralDate))
- '/ ------------------------------------------------------------------
- .AddWithValue("@CPK", PK)
- '/ ------------------------------------------------------------------
- End With
- '/ UPDATE
- .ExecuteNonQuery()
- .Parameters.Clear()
- End With
- End Using
- MessageBox.Show("บันทึกข้อมูลเรียบร้อย.", "รายงานสถานะ", MessageBoxButtons.OK, MessageBoxIcon.Information)
- '/ ------------------------------------------------------------------
- ' ไม่ใช่ข้อมูลใหม่อีกต่อไป
- NewData = False
- '/ ส่งค่ากลับไปว่ามีการเปลี่ยนแปลงข้อมูล เพื่อสั่งให้รีเฟรชการแสดงผลใหม่ (จริงๆไม่จำเป็น)
- FormUpDate = True
- '/ ------------------------------------------------------------------
- '/ หากไม่ปิดหน้าจอนี้ต้องกำหนดค่า CustomerID.Tag ใหม่
- txtCustomerID.Tag = txtCustomerID.Text
- '/ ปิดหน้าจอนี้
- Me.Close()
- '/ ------------------------------------------------------------------
- Catch ex As Exception
- MessageBox.Show(ex.Message)
- End Try
- End Sub
คัดลอกไปที่คลิปบอร์ด
ดาวน์โหลดโค้ดต้นฉบับแบบเต็ม VB.NET (2010) ได้ที่นี่ ... |
ขออภัย! โพสต์นี้มีไฟล์แนบหรือรูปภาพที่ไม่ได้รับอนุญาตให้คุณเข้าถึง
คุณจำเป็นต้อง ลงชื่อเข้าใช้ เพื่อดาวน์โหลดหรือดูไฟล์แนบนี้ คุณยังไม่มีบัญชีใช่ไหม? ลงทะเบียน
x
|