บอร์ดเรียนรู้ Access สำหรับคนไทย
Thai Access Webboard => ห้อง MS Access => ข้อความที่เริ่มโดย: Tonwrp ที่ 28 มิ.ย. 62 , 17:22:45
-
คือผมจะต้องทำ Software ที่ใช้ในการ ยืม/คืน , ติดตามสถานะ ทรัพย์สินของบริษัทที่มาฝึกงาน
ปัญหาคือผมมีคือ Table Asset ซึ่งประกอบไปด้วย [Asset_ID(PK) ,Category_ID ,Asset Description ,Serial No ,Code No ,Quantity]
Table Category = [Category_ID(PK) ,Categories]
Table Employees = [Employee_ID(PK) ,Employee_Name ,Department ,Region]
Table Customers (Import มาจาก Excel) = [Customer_ID(PK) ,Customer_Name ,Customer_Address]
Table Loan = [Loan_No(PK) ,Loan_Date ,Employee_ID ,Customer_ID ,Asset_ID]
แล้วผมต้องการที่จะบันทึก Record ผมต้องสร้าง Table ใหม่ไหมครับเช่น Table_LoanedItems อะไรทำนองนี้หรือว่าสามารถทำได้ด้วยการสร้าง Query เพื่อให้เป็น Source ในการเก็บ Record (คือผมค่อนข้างมือใหม่และไม่มีความรู้เกี่ยวกับภาษาคอมพ์พิวเตอร์เลย) อยากสอบถามว่าหากต้องการเก็บ Record โดยที่เราจะสามารถรู้ชื่อพนักงาน ชื่อทรัพย์สิน ชื่อลูกค้า เราต้องลาก ตัว ID หรือว่าเราลากแค่ชื่อ หรือว่าลากทั้งสองครับผมยังไม่ค่อยเข้าใจเท่าไหร่ รบกวนด้วยครับ
-
ลองนึกถึงใบเสร็จเวลาเราไปซื้อของในห้าง ที่หัวกระดาษเขาจะพิมพ์ว่าแคชเชียร์คือใคร และเราซึ่งเป็นลูกค้าเป็นใคร ส่วนกลางกระดาษก็จะแสดงรายละเอียดสินค้าว่าเราซื้ออะไรบ้าง ซึ่งรองรับได้หลายๆรายการ ถ้าคุณต้องการลักษณะการเก็บแบบนี้ แปลว่านอกจากเทเบิล Loan ซึ่งเปรียบได้กับหัวกระดาษแล้ว ก็จะต้องมีเทเบิล LoanedItems ซึ่งเป็นรายละเอียดของที่ยืมเพิ่มเติมอีกครับ และอย่างน้อยก็จะมีฟิลด์ Loan_No (PK), Asset_ID(PK) ที่ย้ายมาจากเทเบิล Loan เอามาใส่ไว้ที่นี่แทน และจำนวนที่ยืมด้วยครับ และเราจะสร้างความสัมพันธ์ (Table Relationships) ระหว่างทั้ง 2 เทเบิลไว้ด้วย (ส่วนนี้หาวิธีทำใน Youtube ด้วยคำว่า microsoft access table relationships)
สำหรับการบันทึก ก็สร้างฟอร์มสำหรับเทเบิล Loan และ LoanedItems แล้วเราจะเอาฟอร์มของ LoanedItems มาใส่ในฟอร์มของ Loan อีกที (เราจะเรียกฟอร์มของ Loan ว่าเป็น main form และ LoanedItems ว่าเป็น sub form) เป็นการเชื่อม 2 ฟอร์มเข้าด้วยกันด้วย Loan_No ทีนี้ก็จะได้หน้าฟอร์มที่เหมือนมีหัวกระดาษและรายละเอียดรายการยืม/คืนอยู่ภายใน (ส่วนนี้หาวิธีทำใน Youtube ด้วยคำว่า microsoft access subform)
และเพื่อให้การแสดงดูเข้าใจมากขึ้น เราจะกำหนดคุณสมบัติต่างๆในแทป Lookup ของฟิลด์ Employee_ID, Customer_ID, Asset_ID ให้เชื่อมโยงไปยังเทเบิล Employees, Customers และ Asset เพื่อที่ว่าระบบจะดึงเอา Employees_Name, Customer_Name และ Asset Description มาแสดงแทนที่จะแสดงเป็น ID ซึ่งอาจไม่สื่อความหมายให้ผู้ใช้ได้เข้าใจอย่างชัดเจนสักเท่าไหร่ ดังนั้นเมื่อถึงเวลาสร้างคิวรี่ที่มีเทเบิล Loan และ LoanedItems เชื่อมระหว่างกัน และเลือกฟิลด์ Employee_ID, Customer_ID, Asset_ID มาแสดง Access ก็จะดึงเอา _Name และ _Description ของค่าเหล่านี้มาแสดงให้อย่างอัตโนมัติเช่นกัน (ส่วนนี้หาวิธีทำใน Youtube ด้วยคำว่า microsoft access table lookup field) แต่ถ้าหากเราไม่ได้สร้าง Lookup เอาไว้ในฟิลด์ข้างบน ตอนสร้างคิวรี่ เราก็ต้องดึงเอาเทเบิล Employees, Customers และ Asset เข้ามาในคิวรี่ด้วย แล้วสร้างความสัมพันธ์ในคิวรี่เองครับ
-
ลองนึกถึงใบเสร็จเวลาเราไปซื้อของในห้าง ที่หัวกระดาษเขาจะพิมพ์ว่าแคชเชียร์คือใคร และเราซึ่งเป็นลูกค้าเป็นใคร ส่วนกลางกระดาษก็จะแสดงรายละเอียดสินค้าว่าเราซื้ออะไรบ้าง ซึ่งรองรับได้หลายๆรายการ ถ้าคุณต้องการลักษณะการเก็บแบบนี้ แปลว่านอกจากเทเบิล Loan ซึ่งเปรียบได้กับหัวกระดาษแล้ว ก็จะต้องมีเทเบิล LoanedItems ซึ่งเป็นรายละเอียดของที่ยืมเพิ่มเติมอีกครับ และอย่างน้อยก็จะมีฟิลด์ Loan_No (PK), Asset_ID(PK) ที่ย้ายมาจากเทเบิล Loan เอามาใส่ไว้ที่นี่แทน และจำนวนที่ยืมด้วยครับ และเราจะสร้างความสัมพันธ์ (Table Relationships) ระหว่างทั้ง 2 เทเบิลไว้ด้วย (ส่วนนี้หาวิธีทำใน Youtube ด้วยคำว่า microsoft access table relationships)
สำหรับการบันทึก ก็สร้างฟอร์มสำหรับเทเบิล Loan และ LoanedItems แล้วเราจะเอาฟอร์มของ LoanedItems มาใส่ในฟอร์มของ Loan อีกที (เราจะเรียกฟอร์มของ Loan ว่าเป็น main form และ LoanedItems ว่าเป็น sub form) เป็นการเชื่อม 2 ฟอร์มเข้าด้วยกันด้วย Loan_No ทีนี้ก็จะได้หน้าฟอร์มที่เหมือนมีหัวกระดาษและรายละเอียดรายการยืม/คืนอยู่ภายใน (ส่วนนี้หาวิธีทำใน Youtube ด้วยคำว่า microsoft access subform)
และเพื่อให้การแสดงดูเข้าใจมากขึ้น เราจะกำหนดคุณสมบัติต่างๆในแทป Lookup ของฟิลด์ Employee_ID, Customer_ID, Asset_ID ให้เชื่อมโยงไปยังเทเบิล Employees, Customers และ Asset เพื่อที่ว่าระบบจะดึงเอา Employees_Name, Customer_Name และ Asset Description มาแสดงแทนที่จะแสดงเป็น ID ซึ่งอาจไม่สื่อความหมายให้ผู้ใช้ได้เข้าใจอย่างชัดเจนสักเท่าไหร่ ดังนั้นเมื่อถึงเวลาสร้างคิวรี่ที่มีเทเบิล Loan และ LoanedItems เชื่อมระหว่างกัน และเลือกฟิลด์ Employee_ID, Customer_ID, Asset_ID มาแสดง Access ก็จะดึงเอา _Name และ _Description ของค่าเหล่านี้มาแสดงให้อย่างอัตโนมัติเช่นกัน (ส่วนนี้หาวิธีทำใน Youtube ด้วยคำว่า microsoft access table lookup field) แต่ถ้าหากเราไม่ได้สร้าง Lookup เอาไว้ในฟิลด์ข้างบน ตอนสร้างคิวรี่ เราก็ต้องดึงเอาเทเบิล Employees, Customers และ Asset เข้ามาในคิวรี่ด้วย แล้วสร้างความสัมพันธ์ในคิวรี่เองครับ
ผมอยากทราบครับว่า Loan_No ที่เป็น Primary Key นั้นต้องอยู่ใน LoanedItems หรือว่า Loan ครับ เพราะอีก1ตัวที่เหลือก็จะเป็น FK ถูกไหมครับถ้าผมเข้าในไม่ผิดก็น่าจะเป็น Loan_No ที่อยู๋ใน Table Loan ใช่ไหมครับอาจารย์ แล้วก็หลังจากที่ผมสร้างฟอร์ม จาก Query แล้วเนี้ยมันมีปัญหาคือไม่สามารถเลือกค่าจาก Combobox ได้เกิดจากอะไรครับอาจารย์
-
ต้องมีทั้ง 2 เทเบิลครับ เพราะมันคือฟิลด์ที่ไว้เชื่อมความสัมพันธ์กันของทั้ง 2 เทเบิล ไม่อย่างนั้น เวลาเราอ่านเรคอร์ดจาก LoanedItems เราจะไม่สามารถโยงไปหาข้อมูลของการยืมเดียวกันนี้ในเทเบิล Loan ได้เลย
จากวิธีที่ผมบอกไป เราไม่ได้สร้างฟอร์มจากคิวรี่ครับ แต่ละฟอร์มสร้างจากแต่ละเทเบิลครับ
ส่วนคิวรี่ ผมเขียนเอาไว้เพื่ออธิบายถึงประโยชน์ของการสร้าง Lookup ในฟิลด์ต่างๆ เพื่อที่เราจะได้ไม่ต้องดึงเอาเทเบิล Employees, Customers และ Asset เข้ามาในคิวรี่ด้วย ทำให้ดูสะอาดตา ไม่รกรุงรัง ยกเว้นว่าเราต้องการฟิลด์อื่นๆที่นอกเหนือไปจาก _Name และ _Description จาก 3 เทเบิลที่ว่านี้ ซึ่งแน่นอนว่าเราต้องดึงเอาเทเบิลนั้นๆมาใส่ในคิวรี่ด้วยครับ
-
ผมอยากทราบว่าการที่ผมจะให้ combobox ของ Asset ในฟอร์มการยืมเนี่ย แสดงเฉพาะ Asset ที่ยังเหลืออยู่ (ตัวที่ถูกยืมจะไม่แสดงใน dropdown) สามารถทำได้อย่างไรบ้างครับ
แล้วถ้าสมมติว่ามีการ Return Asset แล้วให้กลับมาแสดงใน combobox ด้วยหรือว่าผมต้องสร้าง Field ใน Tb_Asset และกำหนด Data Type เป็น Yes/No เพิ่มลงไปแล้วทำการเขียนโค้ดว่าหากถูกยืมจะเป็น No แล้วก็ในฟอร์มการคืนก็เขียนไว้ว่าหากคืนแล้วก็ให้ Status กลายเป็น Yes แล้วเขียนให้ Combobox แสดงแต่ตัวที่เป็น Yes อย่างนี้ได้รึเปล่าครับอาจารย์
-
สามารถทำได้ครับ โดยใน Row Source property ก็เขียนคำสั่ง SQL SELECT statement เพื่อคิวรี่เอามาเฉพาะที่ของยังไม่ได้ยืมหรือคืนของมาแล้ว ทีนี้ก็ขึ้นอยู่กับการออกแบบระบบของเราเองว่า จะรู้ได้ยังไงว่า คืนของมาแล้ว อย่างวิธีการที่คุณบอกมาก็เป็นวิธีนึง หรืออาจจะไม่ต้องสร้างฟิลด์ที่ว่านั้น ถ้าคิวรี่จากเทเบิลยืม/คืนมาเปรียบเทียบกันโดยตรงได้เลย (กรณีที่การหาสถานะนั้นซับซ้อนเกินกว่าเขียนทั้งหมดใน SQL เราสามารถเขียนโค้ดเป็น Public Function ใน Module เพื่อหาสถานะ แล้วให้ SQL ก็เรียกใช้ฟังก์ชั่นนี้ก็ได้)
แต่ไม่ว่าจะวิธีไหน เราต้องสั่ง ชื่อคอมโบบ็อกส์.Requery เพื่อให้ SQL ที่เขียนไว้นั้นทำงานอ่านสถานะตอนนั้นมาใหม่ ถ้าชัวร์สุดก็สั่งใน On Enter event ของคอมโบบ็อกส์ครับ
-
ตอนนี้ผมเริ่มเข้าในเรื่อง SQL แล้วครับ
แต่อยากทราบการเขียน Module น่ะครับเผื่อจะนำไปประยุกต์ใช้ได้ ก่อนหน้านี้ที่ผมบอกอาจารย์ว่า ถ้าถูกยืมจะให้ตรง Status เป็น No ใช่มั้ยครับ
แล้วผมจะต้องเขียนอย่างไรเพื่อให้มันเป็น No เท่าที่ผมคิดไว้ตอนนี้คือจะให้โค้ดมันไปอยู่ใน OnClick ของ Save Button
แล้วเราจะเขียนโค้ดอย่างไรครับว่า ตอนที่เรากด คลิกปุ่ม Save ไปแล้ว ให้ Status ของ Asset_ID นั้นๆกลายเป็น No
ผมจะได้นำไปเขียนใน Form ของการคืนด้วยว่าหลังจากที่ Save ไปแล้วจะให้มันกลับไปเป็น Yes
ซึ่งตอนนี้ผมว่าผมเข้าใจเรื่องของให้ combobox แสดงค่า Asset_ID ที่มันเป็น Yes แล้วครับรบกวรอาจารย์สันติสุขช่วยชี้แนะด้วยครับ
-
1. หน้าฟอร์มที่บันทักการยืมนั้น ตกลงมี sub form หรือไม่
2. โค้ดที่มีใน OnClick ของปุ่ม Save ตอนนี้มีอะไรอยู่บ้าง แล้วออกแบบไว้ว่าจะให้มันทำอะไรบ้าง
3. ปุ่ม Save อยู่บน main form หรือ sub form
4. ถ้าไม่กดปุ่ม Save แล้วรายการที่ป้อนไปจะเป็นยังไง ? บันทึกไม่ได้ หรือบันทึกได้แต่ถือว่าไม่เอาไปใช้อ้างอิงในระบบ
-
1. หน้าฟอร์มที่บันทักการยืมนั้น ตกลงมี sub form หรือไม่
2. โค้ดที่มีใน OnClick ของปุ่ม Save ตอนนี้มีอะไรอยู่บ้าง แล้วออกแบบไว้ว่าจะให้มันทำอะไรบ้าง
3. ปุ่ม Save อยู่บน main form หรือ sub form
4. ถ้าไม่กดปุ่ม Save แล้วรายการที่ป้อนไปจะเป็นยังไง ? บันทึกไม่ได้ หรือบันทึกได้แต่ถือว่าไม่เอาไปใช้อ้างอิงในระบบ
1.ไม่มี Sub form ครับเนื่องจากทรัพย์สินนั้นจะเป็น Set เป็นกล่องจึงคิดว่าไม่ต้องทำ Sub form
2. Code ที่มีในปุ่ม Save ตอนนี้มีแค่
Private Sub bt_SaveLoan_Click()
Dim SavRec As Boolean
If IsNull(Loan_No) Then
Msgbox ("Please Put Loan No")
Elseif IsNull(Loan_Date) Then
Msgbox ("Please Put Loan Date")
Elseif IsNull(Employee_ID) Then
Msgbox ("Please Put Employee Name")
Else
DoCmd.RunCommand (acCmdSaveRecord)
SavRec = True
End If
End Sub
นี่ครับที่มีในปุ่มตอนนี้ ที่ผมออกแบบไว้คือ ให้มันSave ลงในตารางหลังจากกดปุ่ม save แต่ถ้าไม่กดก็ไม่ต้องใส่ลงในตารางแล้วก็หลังจากที่เซฟเนี่ยให้มันขึ้น Record ใหม่เลยแล้วก็หลังจากที่เซฟเนี่ยให้สถานะของ Asset เนี่ยกลายเป็น No ครับเพื่อที่จะให้ combobox ดึงมาแค่ Asset ที่เป็น Yes
3.ปุ่ม Save อยู่บน MainForm ครับเพราะไม่มี Sub Form
4.ถ้าไม่กดปุ่มเซฟจะไม่สามารถบันทึกได้ครับ
รบกวนอาจารย์ชี้แนะด้วยครับผม
-
Private Sub bt_SaveLoan_Click()
Dim SavRec As Boolean
If IsNull(Loan_No) Then
MsgBox ("Please Put Loan No")
ElseIf IsNull(Loan_Date) Then
MsgBox ("Please Put Loan Date")
ElseIf IsNull(Employee_ID) Then
MsgBox ("Please Put Employee Name")
Else
DoCmd.RunCommand (acCmdSaveRecord)
DoCmd.RunSQL "UPDATE Asset SET Status = FALSE WHERE Asset_ID = " & CStr(Me.Asset_ID)
SavRec = True
End If
End Sub
ก็แค่เพิ่มมาบรรทัดเดียวเอง เป็นการสั่ง SQL statement ให้ทำงาน (จริงๆการทำอะไรกับข้อมูลในเทเบิล สามารถทำได้หลายวิธี แต่เพื่อให้ง่าย ก็เลยเลือกใช้ .RunSQL นี้ก่อนครับ) แต่ถ้าฟิลด์ Asset_ID ในเทเบิล Loan มีประเภทข้อมูลเป็น Text ให้เปลี่ยนข้างท้ายของบรรทัดเป็น Asset_ID = '" & Me.Asset_ID & "'" และถ้าฟิลด์สถานะของ Asset ไม่ใช่ชื่อ Status ก็เปลี่ยนให้ถูกต้องด้วยนะครับ
อีกอย่างที่สังเกตเห็นก็คือ ลืมเช็คว่าผู้ใช้ได้เลือก Asset แล้วหรือเปล่า
เพิ่งคิดได้ว่าถ้าคุณเลื่อนกลับไปเรคอร์ดยืมเก่าๆที่ยังไม่คืน คุณอาจจะมองไม่เห็น Asset บนฟอร์ม เพราะก่อนหน้านี้ คอมโบบ็อกซ์อาจถูกกำหนดให้แสดงเฉพาะตัวที่ยังมีให้ยืมได้เท่านั้น ดังนั้นควรเปลี่ยนไปใช้ Form Current เพื่อกำหนด Row Source ให้กับคอมโบบ็อกซ์ โดยต้องรวมเอา Asset ของการยืมนั้นๆเข้ามารวมกับ Asset ที่ยังมีให้ยืมด้วย แต่ถ้าเป็นการยืมรายการใหม่ ก็ให้มีเฉพาะ Asset ที่ยังมีให้ยืมเท่านั้น โค้ดก็จะมีลักษณะเป็น
Private Sub Form_Current()
If Me.NewRecord Then
Me.Asset_ID.RowSource = "SELECt ฟิลด์ต่างๆ FROM Asset WHERE Status = True"
Else
Me.Asset_ID.RowSource = "SELECt ฟิลด์ต่างๆ FROM Asset WHERE Asset_ID = " & CStr(Me.Asset_ID) & " OR Status = True"
End If
End Sub
-
ตอนนี้เข้าใจเลยครับอาจารย์ขอบพระคุณมากที่ชี้แนะครับผม
-
(https://www.img.in.th/images/3ab0f485902d5d83f978a64a4c922e3f.png)
เกิด Error แบบนี้ครับอาจารย์
-
(https://www.img.in.th/images/3ab0f485902d5d83f978a64a4c922e3f.png)
เกิด Error แบบนี้ครับอาจารย์
แก้ได้แล้วครับตอนนี้ติดปัญหาตรงที่ใน combobox ยังโชว์ asset ที่ Status เป็น No ครับทำให้ผมงงเล็กน้อยและก่อนบันทึกจะมี Msgbox ของโปรแกรมเตือนมาว่าต้องการที่จะบันทึกใช่มั้ยจะ undo ไม่ได้นะอะไรแบบนี้อ่ะครับไม่ทราบว่าเกิดจากอะไรครับ
-
- Asset_ID มี Data Type เป็นอะไรครับ แลัวเอาโค้ดที่เขียนใน Form_Current event procedure มาแสดงหน่อยครับ
- เมื่อดูผ่านเทเบิล Asset โดยตรง เห็นมีเรคอร์ดที่ Status เป็น No แน่ๆแล้วใช่ไหมครับ
ส่วนการยกเลิกแสดงข้อความเตือน ให้สั่ง DoCmd.SetWarnings = False/True คร่อมเอาไว้ครับ
DoCmd.SetWarnings = False
DoCmd.RunSQL "UPDATE Asset ....
DoCmd.SetWarnings = True
-
- Asset_ID มี Data Type เป็นอะไรครับ แลัวเอาโค้ดที่เขียนใน Form_Current event procedure มาแสดงหน่อยครับ
- เมื่อดูผ่านเทเบิล Asset โดยตรง เห็นมีเรคอร์ดที่ Status เป็น No แน่ๆแล้วใช่ไหมครับ
ส่วนการยกเลิกแสดงข้อความเตือน ให้สั่ง DoCmd.SetWarnings = False/True คร่อมเอาไว้ครับ
DoCmd.SetWarnings = False
DoCmd.RunSQL "UPDATE Asset ....
DoCmd.SetWarnings = True
Asset_ID Data Type เป็น AutoNumber ครับเนื่องจาก Import มาจาก Excel
ส่วน Code มีตามนี้ครับ Private Sub Form_frm_Loan_Current()
If Me.NewRecord Then
Me.Asset_ID.RowSource = "SELECT Asset_ID, FROM Tb_Assets WHERE ASSET_Status = YES"
Else
Me.Asset_ID.RowSource = "SELECT Asset_ID, FROM Tb_Assets WHERE Asset_ID = " & CSrt(Me.Asset_ID) & " OR Asset_Status = YES"
End If
End Sub
ใช่ครับใน Table Status เป็น No แล้วแต่หลังจากที่ขึ้น Record ใหม่ใน combobox ก็ยังมีตัวที่ Status เป็น No นะครับ
On Enter Event Procedure ของ combobox ก็ใส่Code ไปว่า
Me.cbo_Asset.Requery แล้วเช่นกันครับเลยสงสัยว่าทำอะไรผิดรึเปล่าหรือต้องไปเขียน SQL เป็น Subquery ใน Row Source Property ของ Combobox อีกรึเปล่าครับ
-
Me.Asset_ID.RowSource = "SELECT Asset_ID, FROM <---- มี , เกินมาครับ เอาออกด้วย
CSrt(Me.Asset_ID) ผิดนะครับ ต้องเป็น CStr(Me.Asset_ID)
Me.Asset_ID.RowSource เปลี่ยนเป็น Me.cboAsset_ID.RowSource
ทำไมชื่อ procedure เป็น Form_frm_Loan_Current ปกติมันจะเป็น Form_Current เท่านั้น ???
-
Me.Asset_ID.RowSource = "SELECT Asset_ID, FROM <---- มี , เกินมาครับ เอาออกด้วย
CSrt(Me.Asset_ID) ผิดนะครับ ต้องเป็น CStr(Me.Asset_ID)
Me.Asset_ID.RowSource เปลี่ยนเป็น Me.cboAsset_ID.RowSource
ทำไมชื่อ procedure เป็น Form_frm_Loan_Current ปกติมันจะเป็น Form_Current เท่านั้น ???
คราวนี้เป็น Status เป็น No หมดทุกตัวเลยครับอาจารย์
-
คราวนี้เป็น Status เป็น No หมดทุกตัวเลยครับอาจารย์
หมายถึงคอมโบบ็อกส์แสดงพวกที่เป็น No หมด หรือว่ายังไง
เอาโค้ดที่เขียนมาให้ดูอีกทีครับ
และลองทดสอบโดยการก็อปปี้ข้อความในส่วนสีแดง (จากโค้ดคุณเองนะ ไม่ใช่ก็อปจากคำตอบนี้) แล้วไปเปิด Query ใหม่สักตัว เลือกแสดงเป็น SQL View แปะข้อความที่ก็อปปี้ลงไป แล้วเปิดเป็น Datasheet View ดูว่าผลเป็นอย่างไร
Me.Asset_ID.RowSource = " SELECT Asset_ID FROM Tb_Assets WHERE ASSET_Status = YES "
-
คราวนี้เป็น Status เป็น No หมดทุกตัวเลยครับอาจารย์
หมายถึงคอมโบบ็อกส์แสดงพวกที่เป็น No หมด หรือว่ายังไง
เอาโค้ดที่เขียนมาให้ดูอีกทีครับ
และลองทดสอบโดยการก็อปปี้ข้อความในส่วนสีแดง (จากโค้ดคุณเองนะ ไม่ใช่ก็อปจากคำตอบนี้) แล้วไปเปิด Query ใหม่สักตัว เลือกแสดงเป็น SQL View แปะข้อความที่ก็อปปี้ลงไป แล้วเปิดเป็น Datasheet View ดูว่าผลเป็นอย่างไร
Me.Asset_ID.RowSource = " SELECT Asset_ID FROM Tb_Assets WHERE ASSET_Status = YES "
ขึ้นว่า Invalid SQL Statement ครับ ส่วนใน Tb_Assets นั้นตรงฟิลด์ที่เป็น Yes/No มันเอาติ๊กออกหมดเลยครับกลายเป็น No หมดเลย จึงไม่แสดงใน combobox เลยครับ
ส่วนพอเปิด Form ขึ้นมามันแจ้งว่าเกิด Debug ใน Code ตรงนี้ครับ
Me.cbo_Asset.RowSource = "SELECT Asset_ID FROM Tb_Assets WHERE Asset_ID = " & CStr(Me.Asset_ID) & " OR Asset_Status = YES"
-
ขึ้นว่า Invalid SQL Statement ครับ
แปลว่า SELECT statement นั้นมีอะไรผิดแล้ว เช็คดีๆครับว่าชื่อฟิลด์ ชื่อเทเบิล ถูกต้องหรือไม่
ใน Tb_Assets นั้นตรงฟิลด์ที่เป็น Yes/No มันเอาติ๊กออกหมดเลยครับกลายเป็น No หมดเลย
ในคำสั่ง DoCmd.RunSQL "UPDATE ... เช็คชื่อฟิลด์ เทเบิล และคอมโบบ็อกส์ว่าถูกต้องหรือไม่
มันแจ้งว่าเกิด Debug ใน Code
มันบอกว่า error อะไรครับ เอาข้อความมาแสดงหน่อย หรือว่าคุณแบ่งโค้ดเป็น 2 บรรทัด ถ้าใช่ ให้แก้ไขให้อยู่ในบรรทัดเดียวกันนะครับ
สุดท้าย ให้คลิกที่เมนู Debug และ Compile ดูว่ามีข้อผิดพลาดอะไรแสดงออกมาหรือไม่
ปล.คงอีกหลายชั่วโมงผมถึงเข้ามาตอบอีกทีครับ
-
ตอนนี้ผมทำได้แล้วครับ
เพราะผมเข้าไปแก้ Code ในส่วนของ Form_Current()
จาก " SELECT Asset_ID FROM Tb_Assets WHERE Asset_Status = Yes "
ผมเปลี่ยนเป็น " SELECT * FROM Tb_Assetes WHERE Asset_Status = Yes " แทนครับ
เนื่องจากหน้าฟอร์มของผมต้องให้ค่าของ Column2 , Column3 ไปแสดงยัง Textbox ด้วยครับเป็น Serial No กับ Code No ของ Asset
ตอนนี้เกิด Compile Error ครับเป็นเรื่องของ
Argument not optional ครับมันไปเกิดตรงปุ่ม Save ได้ยังไงก็ไม่ทราบครับตอนนี้มึนมากๆ
ตัว Code ของปุ่ม save ยังเป็นตัวเดิมคือ
Private Sub bt_SaveLoan_Click()
Dim SavRec As Boolean
If IsNull(Loan_No) Then
MsgBox ("Please Put Loan No")
ElseIf IsNull(Loan_Date) Then
MsgBox ("Please Put Loan Date")
ElseIf IsNull(Employee_ID) Then
MsgBox ("Plse Put Employee Name")
Else
DoCmd.RunCommand (acCmdSaveRecord)
DoCmd.RunSQL "UPDATE Tb_Assets SET Asset_Status = FALSE WHERE Asset_ID = " & CStr(Me.Asset_ID)
SavRec = True
End If
End Sub
เหมือนเดิมตามที่อาจารย์บอกเลยครับหรือว่าผมต้องเพิ่มเงื่อนไขไปอีกครับขอบคุณอาจารย์สันติสุขมากๆครับ
-
ให้ลองเปลี่ยนชื่อ Loan_No, Loan_Date, Employee_ID, Asset_ID ไปเป็น Me.ชื่อเท็กซ์บ็อกซ์หรือคอมโบบ็อกส์ ที่ตรงกันบนฟอร์มดูครับ ถ้ายังไม่ได้อีก ก็ให้ลอง Compact and Repair Database ครับ
-
ให้ลองเปลี่ยนชื่อ Loan_No, Loan_Date, Employee_ID, Asset_ID ไปเป็น Me.ชื่อเท็กซ์บ็อกซ์หรือคอมโบบ็อกส์ ที่ตรงกันบนฟอร์มดูครับ ถ้ายังไม่ได้อีก ก็ให้ลอง Compact and Repair Database ครับ
ยังไม่ได้เช่นเดิมครับอาจารย์สันติสุขใน Form การคืนของก็เป็นที่จุดเดียวกันครับ ทั้งเปลี่ยนเป็น Me.txt_.....และ Compact and Repair Database ก็แล้วยังขึ้นเหมือนเดิมเลยครับ
ไม่ทราบว่าผมเขียนอะไรผิดรึเปล่าครับ แบบคำสั่งมันขัดแย้งกันภายในตัวเองรึเปล่า
(https://www.img.in.th/images/8ce0772a02f3af11e68282693c87d120.png)
-
ถ้าได้ก็อัพโหลดโปรแกรมมาครับ หรือไม่ก็ค่อยๆ comment หรือตัดไปทีละบรรทัด ดูว่าเมื่อไหร่ไม่มี error ก็แปลว่าบรรทัดนั้นเป็นปัญหาครับ
-
รบกวนอาจารย์ช่วยเช็คให้ผมทีครับ
-
แย่แล้วผม ทำไมผิดอย่างนี้ DoCmd.SetWarnings False หรือ True ไม่ต้องมีเครื่องหมาย = ครับ
-
ขอบคุณอาจารย์สันติสุขมากๆเลยครับตอนนี้รันได้ปกติ
ตอนนี้ผมมีคำถามที่อาจจะฟังดูตลกสักหน่อยนะครับ
ถ้าเกิดผมนำโปรแกรมไปใช้จริงเนี่ยและต้องการให้คนที่เขามายืมของกรอกข้อมูลเสร็จแล้วกดปุ่มบันทึกแล้วปิดฟอร์มเรียบร้อยแล้ว
พอคนที่จะมาใช้คนต่อไปเขาจะเห็น Record ก่อนหน้าจาก Form การยืมไหมครับถ้าเกิดเห็นแล้วมีวิธีไหนที่เวลาเปิดฟอร์มมามันจะเป็น Record ใหม่เสมอไหมครับ
ผมเหลือแค่ตรงนี้โปรแกรมก็สามารถนำไปใช้ได้แล้วครับรบกวนอาจารย์ชี้แนะเรื่องเล็กน้อยนี้ด้วยครับ
-
เลือก Data Entry property ของฟอร์มเป็น Yes เท่านี้เองครับ
-
ขอบคุณอาจารย์สันติสุขมากๆครับที่คอยชี้แนะคนที่ไม่เคยทำอย่างผมมาก่อนขอบคุณจริงๆครับ
-
(https://www.thai-access.com/tiupld/images/pu5u4j-bc3a89.jpg)
รบกวนอาจารย์สันติสุขอีกหนึ่งรอบครับ
เนื่องจากผมต้องการที่จะให้มันโชว์ รูปของ Asset ด้วย
จึงได้เขียน Code ลงไปใน Event Procedure Afterupdate ของ combobox ที่เลือก Asset
แต่หลังจากที่กดบันทึกมันกลับขึ้นว่า Object doesn't support this property or method
และรูปไม่ขึ้นด้วยมันเกิดจากอะไรอ่ะครับ
-
คุณจะดึงฟิลด์ Attachment มาจากคิวรี่แล้วมากำหนดให้ Attachment Control โดยตรงอย่างนี้ไม่ได้ แต่เราสามารถเอามาเป็น Control Source ของ Attachment Control ได้ ดังนั้นเราจะสร้าง Attachment Control ตัวนึง (ชื่อว่า A) ใส่ลงฟอร์มเปล่า (ชื่อว่าฟอร์ม F) แล้วเอา F มาใส่เป็น Sub Form control (ชื่อ SF) ใน ฟอร์มการยืมอีกที ทีนี้เมื่อใดที่มีการเลือก Asset เราก็จะสร้าง Record Source ของ SF เป็น SQL SELECT statement ที่ดึงเอาเฉพาะฟิลด์ Attachment ที่เก็บรูปภาพตาม Asset_ID ที่เลือก ดังนั้นภาพก็จะแสดงออกมา เท่านี้เองครับ
โค้ดให้ใส่ไว้ใน Private Sub cbo_Loanasset_AfterUpdate() , ใน Private Sub Form_Current() และใน Private Sub Form_Undo(Cancel As Integer) ด้วย
If IsNull(Me.cbo_Loanasset) Then
Me.SF.Form.RecordSource = ""
Else
Me.SF.Form.RecordSource = "select Asset_Photo from tb_Asset where Asset_ID = " & Cstr(Me.cbo_Loanasset)
End If
นอกจากนี้ให้กำหนด property เหล่านี้เอาไว้ด้วย
สำหรับ A
Control Source เป็น Asset_Photo
Enabled No
สำหรับ F
Record Selectors No
Naviagtion Buttons No
Dividing Lines No
Scroll Bars Neither
Recordset Type Snapshot
ที่เหลือก็ปรับขนาดกรอบรูปเท่านั้นครับ
-
ได้แล้วครับขอบคุณอาจารย์สันติสุขมากๆเลยครับ
-
รบกวนอาจารย์สันติสุขอีกรอบครับ คือผมได้ทำการสร้างคิวรี่ เพื่อ tracking Loaned Asset Status ครับ โดยเขียน sql ประมาณนี้ครับ
SELECT Tb_Assets.[Serial No], Tb_Loan.Loan_No, Tb_Employees.Employee_Name, Tb_Loan.Machine_Description, Tb_Loan.Machine_Serial_No, Tb_Loan.[Expired Date], IIf([Expired Date]<Date(),"EXPIRED",IIf([Expired Date]>Date(),"PENDING")) AS Status, Tb_Loan.Loan_Date
FROM Tb_Employees INNER JOIN (Tb_Assets INNER JOIN Tb_Loan ON Tb_Assets.Asset_ID = Tb_Loan.Asset_ID) ON Tb_Employees.Employee_ID = Tb_Loan.Employee_ID
WHERE (((Tb_Assets.Asset_Status)=False));
แต่ปัญหาคือหลังจากที่ผม return asset ตัวนั้นไปแล้วและมีคนมายืมไปอีกรอบ มันจะขึ้นใน query tracking ด้วยครับ เช่น Asset_A ถูกยืมใน loan_No ที่ ABC00001 และได้ทำการคืนแล้วทำให้ status มันเป็น True แต่ Asset_A ถูกยืมอีกรอบนึงใน loan_No ที่ ABC00005 ทีนี้มันขึ้นทั้งสองอันเลยครับคือทั้ง ABC00001 กับ ABC00005 คือผมอยากให้มันขึ้นเฉพาะ หมายเลขล่าสุดที่ยืมไปอ่ะครับไม่ทราบว่าจะต้องเขียนอย่างไรดีครับผมนึกไม่ออกจริงๆ
-
ผมแนะนำว่าควรมีการบันทึกในใบคืนของ ว่าเป็นการคืนมาจากการยืมเลขที่อะไรด้วย (สมมุติชื่อฟิลด์คือ Loan_no) ถ้าเพิ่มได้ คิวรี่นี้ก็เปลี่ยนเงื่อนไขที่ WHERE เป็น (Asset ถูกยืมไปแล้ว) และ (ไม่พบใบคืนที่มีเลขที่ยืมอันเดียวกันนี้ปรากฏอยู่)
WHERE ((Tb_Assets.Asset_Status=False)
AND NOT EXISTS (SELECT Tb_Return.Loan_No FROM Tb_Return WHERE Tb_Return.Loan_No = Tb_Loan.Loan_No);
หรือไม่ต้องเช็คสถานะของ Asset เลยก็ได้ เป็น
WHERE NOT EXISTS (SELECT Tb_Return.Loan_No FROM Tb_Return WHERE Tb_Return.Loan_No = Tb_Loan.Loan_No);
เพราะตามตรรกะของระบบแล้ว แน่นอนว่าของที่มีใบยืม แต่ไม่มีเลขที่ใบยืมอยู่ในใบคืนใดๆเลย ก็คือของที่ Asset_Status เป็น False นั่นเอง แต่มัน **อาจจะ** ทำให้การค้นหาช้าลงเมื่อมีข้อมูลการยืม/คืนอยู่เป็นจำนวนมาก (ที่บอกว่า อาจจะ เพราะถ้าอยากทราบจริงๆ ต้องขุดลึกลงไปว่าตัวจัดการคิวรี่ (Query Optimizer) มันฉลาดหรือทื่อๆแค่ไหนกับคำสั่งคิวรี่นั้นๆ)
-
ผมแนะนำว่าควรมีการบันทึกในใบคืนของ ว่าเป็นการคืนมาจากการยืมเลขที่อะไรด้วย (สมมุติชื่อฟิลด์คือ Loan_no) ถ้าเพิ่มได้ คิวรี่นี้ก็เปลี่ยนเงื่อนไขที่ WHERE เป็น (Asset ถูกยืมไปแล้ว) และ (ไม่พบใบคืนที่มีเลขที่ยืมอันเดียวกันนี้ปรากฏอยู่)
WHERE ((Tb_Assets.Asset_Status=False)
AND NOT EXISTS (SELECT Tb_Return.Loan_No FROM Tb_Return WHERE Tb_Return.Loan_No = Tb_Loan.Loan_No);
หรือไม่ต้องเช็คสถานะของ Asset เลยก็ได้ เป็น
WHERE NOT EXISTS (SELECT Tb_Return.Loan_No FROM Tb_Return WHERE Tb_Return.Loan_No = Tb_Loan.Loan_No);
เพราะตามตรรกะของระบบแล้ว แน่นอนว่าของที่มีใบยืม แต่ไม่มีเลขที่ใบยืมอยู่ในใบคืนใดๆเลย ก็คือของที่ Asset_Status เป็น False นั่นเอง แต่มัน **อาจจะ** ทำให้การค้นหาช้าลงเมื่อมีข้อมูลการยืม/คืนอยู่เป็นจำนวนมาก (ที่บอกว่า อาจจะ เพราะถ้าอยากทราบจริงๆ ต้องขุดลึกลงไปว่าตัวจัดการคิวรี่ (Query Optimizer) มันฉลาดหรือทื่อๆแค่ไหนกับคำสั่งคิวรี่นั้นๆ)
แล้วเราจะต้องสร้าง Relationship loan_No ของทั้งสองตารางมั้ยครับหรือว่าผมทำ lookup wizard ไปเลยในฟอร์มของการคืน
-
ได้แล้วครับอาจารย์ ผมทำการเพิ่ม Field Loan_No เข้าไปในตารางการคืนแล้วก็ดึงลงมาเป็น textbox ใน ฟอร์มการคืน หลังจากนั้นก็ลองไปเขียน query ตามที่อาจารย์บอกครับ ขอบคุณมากๆเลยครับผม
-
สร้างเป็น Relationship ด้วยครับ เพราะจะได้ประโยชน์โดยอัตโนมัติคือ จะป้อนเลขที่ใบยืมลงในใบคืนได้ เลขยืมนั้นต้องมีอยู่จริงแล้ว และจะไม่สามารถลบใบยืมได้ถ้าเลขที่ใบยืมยังปรากฏอยู่ในใบคืนด้วย
-
สอบถามอาจารย์ครับ ตอนนี้เกิดปัญหาเวลาที่มี User เข้าฟอร์ม Loan พร้อมกันแล้วกดปุ่มใส่ Loan_No แบบที่ผมเซ็นให้เป็น auto run เพิ่มไปเรื่อยๆ ปัญหาที่พบคือถ้าหากเข้าฟอร์มการ Loan พร้อมกันแล้วกดปุ่มนี้มันจะขึ้นเป็น Loan_No เดียวกันเลยครับไม่ทราบว่าสามารถทำให้มันไม่ซ้ำกันได้มั้ยครับ
-
auto run ของคุณคือฟิลด์ที่มีข้อมูลเป็นประเภท Autonumber หรือว่าคุณเขียนโค้ดเพื่อสร้างเลข Loan No. เอาเองครับ
-
เขียนโค้ดเอาเองครับ พอจะมีทางแก้จุดบอดตรงนี้มั้ยครับ
-
ไม่ทราบโค้ดนี้เขียนใน event ไหนของฟอร์ม ปกติผมก็จะเขียนใน Form_BeforeUpdate event พอได้เลขก็เอากลับไปเช็คในเทเบิลทันทีเลยว่า มีเรคอร์ดของเลขนั้นอยู่ในเทเบิลแล้วหรือเปล่า ถ้ามีก็จะหาเลขถัดไปใหม่อีกครั้งแล้วก็เช็คเทเบิลอีกที วนอย่างนี้ไปเรื่อยๆจนครบกี่ครั้งตามที่เรากำหนด จึงแจ้งข้อความว่าซ้ำ ให้ผู้ใช้ลองทำใหม่อีกทีเท่านั้นเองครับ โอกาสที่จะต้องเตือนผู้ใช้ให้ทำอีกครั้ง ก็เป็นไปได้ แต่น่าจะน้อยมากๆครับ
-
ไม่ทราบโค้ดนี้เขียนใน event ไหนของฟอร์ม ปกติผมก็จะเขียนใน Form_BeforeUpdate event พอได้เลขก็เอากลับไปเช็คในเทเบิลทันทีเลยว่า มีเรคอร์ดของเลขนั้นอยู่ในเทเบิลแล้วหรือเปล่า ถ้ามีก็จะหาเลขถัดไปใหม่อีกครั้งแล้วก็เช็คเทเบิลอีกที วนอย่างนี้ไปเรื่อยๆจนครบกี่ครั้งตามที่เรากำหนด จึงแจ้งข้อความว่าซ้ำ ให้ผู้ใช้ลองทำใหม่อีกทีเท่านั้นเองครับ โอกาสที่จะต้องเตือนผู้ใช้ให้ทำอีกครั้ง ก็เป็นไปได้ แต่น่าจะน้อยมากๆครับ
ผมเขียนไว้ที่ After Update ครับผมสงสัยคงต้องไปเขียนใน before update แทนแต่ตอนนี้เกิดปัญหาครับเนื่องจาก code autorun หมายเลขมันไม่ยอมบวกให้ครับแต่จะเริ่มนับ 1 ใหม่เมื่อขึ้นเดือนใหม่ครับอยากรบกวนอาจารย์ช่วยติโค้ดหน่อยได้มั้ยครับ
txt_LoanNo = "TSD" & Format([txt_LoanDate], "yymm") & Right("000" & DCount("[Loan_No]", "[Tb_Loan]", "Left([Loan_No],4) = Format([txt_LoanDate],'yymm')") + 1, 3)
ตอนแรกยังบวกอยู่เลยครับแต่อยู่ดีๆก็ไม่บวกเพิ่มไปเฉยๆซะอย่างนั้นเลย
-
txt_LoanNo = "TSD" & Format([txt_LoanDate], "yymm") & Right("000" & DCount("[Loan_No]", "[Tb_Loan]", "Left([Loan_No],4) = Format([txt_LoanDate],'yymm')") + 1, 3)
ใช่ครับ โค้ดข้างบนนี้จะเริ่มให้ค่า 1 เมื่อเป็นเดือนใหม่ ถ้าต้องการให้ +1 ในปีนั้นไปเรื่อยๆจนกว่าจะเป็นปีใหม่ ก็ต้องเป็น
txt_LoanNo = "TSD" & Format([txt_LoanDate], "yymm") & Right("000" & DCount("[Loan_No]", "[Tb_Loan]", "Left([Loan_No],2) = Format([txt_LoanDate],'yy')") + 1, 3)
แต่ผมว่าดูแปลกๆไม่สัมพันธ์กับเลขหลัง TSD หรือเปล่า เพราะพอเดือนใหม่ เลขหลัง TSD ก็เป็น ปีและเดือน ถัดไป แต่เลขรันนิ่งยังเป็นเลขต่อจากเดือนที่แล้ว เช่น ในเดือน 8 มีเลขรันจาก TSD1908001 ถึง TSD1908123 แต่เลขแรกของเดือน 9 เป็นเลข TSD1909124 แต่ไม่ว่ายังไงก็ตาม การใช้ DCount ค่อนข้างไม่ถูกต้อง เพราะมันเป็นการนับจำนวน อย่างเดือน 8 มีเลขที่สมมุติข้างบน ถ้าเรายังอยู่ในเดือน 8 และมี 1 เรคอร์ดที่ถูกลบไป พอสร้างเลขต่อไป Dcount ก็จะนับได้ทั้งหมดเหลือ 122 เลข พอบวก 1 ก็จะได้เลขถัดไปเป็น TSD1908123 ซึ่งซ้ำกับเลขเดิมแน่ๆ
จริงๆ รูปแบบที่ต้องการเป็นอย่างไร และพอขึ้นเดือนต่อไปจะได้เป็นอะไร ยกตัวอย่างให้เห็นเลยครับ
-
ผมอยากได้แค่ให้มันเป็น TSD-1908001 พอเปลี่ยนเดือนใหม่ก็รันเป็น 001 ใหม่พอเปลี่ยนปีก็เป็น TSD-2001001 อะไรทำนองนี้เองครับแต่ผมไม่ค่อยเข้าใจฟังก์ชั่น Dcount เท่าไหร่เลยงงๆครับแหะๆ
-
Dim TSDYYMM As String
Dim MaxRunNo As Integer
TSDYYMM = "TSD-" & Format([txt_LoanDate], "yymm")
MaxRunNo = Val( Right( Nz(DMax("[Loan_No]", "[Tb_Loan]", "[Loan_No] Like '" & TSDYYMM & "*'"),"0"),3 ) )
txt_LoanNo = TSDYYMM & Format(MaxRunNo + 1, "000")
-
ขอบคุณอาจารย์สันติสุขมากๆครับผม