แสดงกระทู้

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - สันติสุข

หน้า: 1 ... 19 20 21 [22] 23 24 25 ... 29
379
ห้อง MS Access / : ลบไฟล์ที่เลือก
« เมื่อ: 27 ส.ค. 61 , 13:17:51 »
รายละเอียดเพิ่มเติมด้วยครับ ไดอะล็อกบ๊อกซ์มาจากไหน เกี่ยวกับอะไร โค้ดที่ใช้คืออะไร และลบไฟล์ ณ ขั้นตอนไหน ลบในตัวไดอะล็อกบ๊อกซ์เองเลย หรือมาลบที่ไหน

380
ไม่เคยทำนะครับ แต่ค้นมาให้ น่าจะเป็นการใช้ .AddAttachment method ครับ

https://stackoverflow.com/questions/34964651/vba-send-mail-with-gmail

381
ส่วนตัวไม่เคยใช้ Office 64 bits ครับ และไม่คิดจะใช้เพื่อตัดปัญหาเพราะยังไม่เห็นประโยชน์อะไรที่จะใช้ ลองอ่านบทความข้างล่างนี้ดูครับ

เลือกระหว่าง Office 32 หรือ 64 bits
support.office.com/en-us/article/choose-between-the-64-bit-or-32-bit-version-of-office-2dee7807-8f95-4d0c-b5fe-6c6f49b8d261

ความแตกต่างของโค้ด VBA สำหรับ Office 32 หรือ 64 bits
docs.microsoft.com/en-us/office/vba/Language/Concepts/Getting-Started/64-bit-visual-basic-for-applications-overview

อีกอย่างคือถ้าจะใช้ไฟล์ .accde บน Office 64 bits  ก็จะต้องสร้าง .accde ด้วย Office 64 bits ด้วย

ส่วนที่ถามว่า "และรุ่นที่ใหม่กว่า" อันนี้ microsoft ไม่มีบอกนะครับ และคิดว่าคงไม่มีใครบอกได้เช่นกัน

382
ไม่ก็ Import ข้อมูลจาก ODBC Linked-Table มาลงเทเบิลชั่วคราวบน Access ที่กำหนดฟิลด์ที่รับข้อมูลจาก Memo ฟิลด์ให้มี Data Type เป็น Text แล้วใช้เทเบิลชั่วคราวนี้แทน

383
ตัว ODBC Driver จะเป็นตัวจับคู่ว่า Data Type ของฐานข้อมูลต้นทางคืออะไร เทียบเท่ากับ Data Type ของ ODBC ประเภทอะไร จากนั้นตัว JET/ACE Engine จะจับคู่อีกทีว่า Data Type ประเภทนั้นของ ODBC จะเป็นประเภทอะไรของ JET/ACE Engine  ดังนั้น

1. ไปเปลี่ยน Data Type ของฟิลด์ในเทเบิลต้นทางให้เป็นประเภท Text ไม่เกิน 255 ตัวอักษร ซึ่งน่าจะเทียบเท่ากับ Data Type ประเภท Text บน JET/ACE ด้วยเช่นกัน

2. ส่วนอีกวิธีที่ไม่รู้ว่าจะทำได้หรือไม่ ก็คือสร้างคิวรี่ใน Access จาก ODBC Linked-Table นั้น และสร้าง Calculated Field : Left$(ฟิลด์ต้นทาง, nnn) เพิ่มขึ้นมา (nnn = ความยาวของฟิลด์ไม่เกิน 255 ตัวอักษรซึ่งเมื่อตัดมาแล้ว ไม่ทำให้ข้อมูลหาย) แล้วนำเอาคิวรี่นี้ไป JOIN กับเทเบิลอื่นต่อไป

384
ถ้าคุณไม่รู้เกี่ยวกับเรื่อง การออกแบบฐานข้อมูล (Database Design) ผมแนะนำว่าให้ศึกษาด้านนี้ดูก่อน มันไม่ได้เกี่ยวกับการเขียนโปรแกรมอะไรเท่าไหร่ เป็นการออกแบบว่าระบบของเราต้องมีตารางที่เก็บข้อมูล(เทเบิล)อะไรบ้าง มีข้อมูล(ฟิลด์)อะไรในตารางนั้นบ้าง และออกแบบตรงตามหลักการวิชาการ เพื่อการเก็บข้อมูลจะได้ไม่ซ้ำซ้อน โปรแกรมที่เขียนมาจัดการก็จะง่ายขึ้น ที่ถามมามันโยงกันได้หมดแหล่ะครับ แต่ต้องบอกเลยว่าไม่ง่ายสำหรับมือใหม่ ต้องใช้เวลา เรื่องการออกแบบก็ทำในกระดาษนี่ก็ได้ ไม่ต้องใช้โปรแกรม ทำพื้นฐานตรงนี้ให้ได้ก่อนแล้วค่อยไปเลือกว่าจะใช้ Access หรือยี่ห้ออื่นๆในการพัฒนาระบบต่อไป

385
ถ้าเอาแบบง่ายๆเร็วๆไม่เช็คมาก ก็ใช้ On Error Resume Next มาช่วย คำสั่งนี้จะบอกว่าถึงแม้โปรแกรมทำอะไรแล้วเกิด error ไม่ว่าจากอะไรก็ตาม (ไม่ใช่เฉพาะไม่มีเทเบิลต้นทาง) ก็ให้ข้ามไปทำคำสั่งถัดไปได้เลยโดยที่ไม่ต้องแสดง error ออกมา แต่ถ้าต้องการจะเช็คอย่างในกรณี Tbl_A ว่ามีก่อนหรือไม่ ก็ตรวจสอบกับ Err.Number ได้ว่าเกิด error หรือไม่ ถ้าเกิด มันจะไม่คืนค่า 0 ออกมาครับ

On Error Resume Next จะมีผลต่อคำสั่งไปเรื่อยจนกว่าจะกำหนด On Error เป็นอย่างอื่น ซึ่งท้ายของโค้ดนี้สั่งเป็น On Error Goto 0 ซึ่งหมายถึงให้ Access แสดง error ออกมาได้ตามปกติ พร้อมถามผู้ใช้ว่าจะให้ Debug โค้ดหรือไม่

โค๊ด: [Select]
    DoCmd.SetWarnings False
    On Error Resume Next
    DoCmd.RunSQL "insert into Tbl_A select * from Tbl_A in 'C:\Sample.mde'"
    If Err.Number = 0 Then
        DoCmd.RunSQL "insert into Tbl_B select * from Tbl_B in 'C:\Sample.mde'"
        DoCmd.RunSQL "insert into Tbl_C select * from Tbl_C in 'C:\Sample.mde'"
        DoCmd.RunSQL "insert into Tbl_D select * from Tbl_D in 'C:\Sample.mde'"
        DoCmd.RunSQL "insert into Tbl_E select * from Tbl_E in 'C:\Sample.mde'"
        DoCmd.RunSQL "insert into Tbl_F select * from Tbl_F in 'C:\Sample.mde'"
    End If
    DoCmd.SetWarnings True
    On Error GoTo 0

386
มีคนเจอปัญหานี้หลายคน ลองดูวิธีแก้ในนี้ครับ เขาบอกว่าต้องย้อนกลับไปเวอร์ชั่นก่อนหน้า https://social.msdn.microsoft.com/Forums/en-US/b977efe7-dc8c-4685-99d3-8624a8d90e77/vba-access-problem-vba-project-cannot-be-read?forum=accessdev

389
ในคิวรี่เอง ไม่ได้ครับ

390
เอาคิวรี่ไปสร้างเป็นฟอร์ม ในฟิลด์ก็กำหนดชิดขอบตามที่ต้องการ แล้วเปิดมุมมองแบบ Data Sheet ครับ

ปล.ในกระทู้ก่อนหน้า ผมมีเขียนอธิบายเพิ่มเติม ลองไปอ่านดูครับ

391
ผมเขียนตัวอย่างสมมุติของ Class Recordset เพื่อให้เห็นภาพว่าเมื่อเราอ่านหรือกำหนดค่าให้ Property ของ Object ทำงาน มันจะไปสัมพันธ์กับโค้ดส่วนไหนใน Class นั้นๆ หวังว่าพอจะมีประโยชน์บ้างครับ แต่เอาไปรันจริงไม่ได้นะครับ แค่ตัวอย่างเท่านั้น

โค้ดข้างล่างนี้จะเขียนอยู่ใน Class Module และบันทึกในชื่อ Recordset
โค๊ด: [Select]
Option Compare Database
Option Explicit

Type RecordStructure                ' โครงสร้างของ Recordset Object
    RSField1    As Variant
    RSField2    As Variant
End Type

Dim Ptr         As Integer          ' เก็บตำแหน่งเรคอร์ดปัจจุบันของ Recordset นี้
Dim RC()        As RecordStructure  ' ตัวแปร Recordset Object

Private Sub Class_Initialize()
    ' Procedure จะทำงานโดยอัตโนมัติเมื่อ object ถูกสร้าง
    ' ตย.โค้ดเช่น เพิ่ม object นี้ลงใน Recordsets Collection (Collection คือ อ็อปเจ็คของกลุ่ม Object อีกที)
End Sub

Private Sub Class_Terminate()
    ' Procedure จะทำงานโดยอัตโนมัติเมื่อ object ถูกเคลียร์ให้เป็น Nothing
    ' ตย.โค้ดเช่น นำ object นี้ ออกจาก Recordsets Collection
End Sub

Public Property Let Bookmark(NewPosition As Integer)
    ' กำหนดตำแหน่งเรคอร์ดปัจจุบันของ Recordset
    If NewPosition <= UBound(RC) Then
        Ptr = NewPosition
    Else
        Err.Raise 1234, "Invalid Bookmark"
    End If
End Property

Public Property Get RecordCount() As Long
    ' คืนค่าจำนวนเรคอร์ดทั้งหมดใน Recordset
    RecordCount = UBound(RC)
End Property

Public Property Get Fields(Name As String) As Variant
    ' คืนค่าฟิลด์ตามชื่อฟิลด์ที่ส่งมาในตัวแปร Name
    Select Case Name
        Case "CustomerID"
            Fields = RC(Ptr).RSField1
        Case "CustomerName"
            Fields = RC(Ptr).RSField2
        Case Else
            Err.Raise 2589, "Invalid Field Name"
    End Select
End Property

Public Property Get EOF() As Boolean
    ' คืนค่า True เมื่อตำแหน่งเรคอร์ดปัจจุบันเลยเรคอร์ดสุดท้ายไปแล้ว
    If Ptr > UBound(RC) Then
        EOF = True
    Else
        EOF = False
    End If
End Property

Public Sub MoveNext()
    ' เลื่อนไปยังเรคอร์ดถัดไปใน Recordset
    If Ptr <= UBound(RC) Then
        Ptr = Ptr + 1
    Else
        Err.Raise 7654, "End of file"
    End If
End Sub

ส่วนข้างล่างนี้เป็นโค้ดที่อยู่ใน Module ธรรมดาๆ ที่เรียกใช้ Object, Method และ Property ต่างๆใน Class Module Recordset ครับ
โค๊ด: [Select]
Private Sub Example()
    ' โค้ดพิมพ์ CustomerID และ CustomerName จากเทเบิล Customer ตั้งแต่เรคอร์ดที่ 10 เป็นต้นไป
   
    Dim RS  As DAO.Recordset
       
    Set RS = CurrentDb.OpenRecordset("Customer")
    ' เมื่อ Object ถูกสร้าง จะไปเรียก Class_Initialize procedure มาทำงาน
   
    If RS.RecordCount > 10 Then     ' โค้ดใน Property Get RecordCount จะถูกทำงาน
        RS.Bookmark = 10            ' โค้ดใน Property Let Bookmark จะถูกทำงาน เป็นการนำค่าเข้าไปใน Property Bookmark
    End If
    Do Until RS.EOF                 ' โค้ดใน Property Get EOF จะถูกทำงาน
        Debug.Print RS.Fields("CustomerID") & ";" _
                  & RS.Fields("CustomerName") ' โค้ดใน Property Get Fields จะถูกทำงาน
        RS.MoveNext                 ' โค้ดใน Sub MoveNext จะถูกทำงาน
    Loop
   
    Set RS = Nothing
    ' เมื่อ Object ถูกเคลียร์ จะไปเรียกโค้ดใน Class_Initialize procedure มาทำงาน
End Sub

392
Has Module หมายถึงฟอร์มนี้ได้มีการเขียนโค้ดลงใน Event อะไรของฟอร์มหรือของ Control หรือไม่ ถ้าเป็น Yes ตัว Access จะมองฟอร์มนี้เป็น Class ด้วย  (ซึ่งทำให้สามารถมอง sub procedure ของฟอร์มเป็น Method และ function procedure ของฟอร์มเป็น Property)  พอเราเลือกเป็น No โค้ดต่างๆในฟอร์มนี้จะถูกลบทิ้งครับ  ผมคิดว่าไม่น่ามีส่วนเกี่ยวข้องกับปัญหาที่เจอนะครับ  ยังคิดไม่ออกว่าเพราะอะไร ลองทำ Compile แล้วตามด้วย Compact and Repair Database อาจช่วยได้ ?!?

393
Class Module ใช้เขียนโค้ดเพื่อสร้างตัว Object ของเราเอง เทียบอย่างเช่นเมื่อเราเขียน Dim RS as DAO.Recordset ตัว Recordset ก็คือคลาสจากไลบรารี่ DAO   ตัว RS ก็คือตัวแปร Object จากคลาส Recordset

Class และ Object เป็นแนวคิดการเขียนโปรแกรมเชิงวัตถุ (Object Oriented Programming, OOP) ซึ่งมีคุณสมบัติพิเศษมากมาย แต่ VBA มีแค่ส่วนนึงเท่านั้น จุดประสงค์หนึ่งก็เพื่อให้คนอื่นมาเรียกใช้โค้ดของเราอย่างที่ผมขอเรียกว่า "เข้าตามตรอก ออกตามประตู" ใครจะเรียกใช้ให้โค้ดเราทำอะไร ก็ต้องเรียกผ่านโค้ดที่เรากำหนด (ใน OOP เรียกว่า Method) หรือกำหนด/อ่านค่าอะไรของโค้ดเรา ก็ต้องส่ง/รับผ่าน Property เท่านั้น ไม่ว่าจะเป็น Method หรือ Property ก็จะเป็น Procedure ดังนั้นโค้ดใน Procedure เหล่านี้ก็จะสามารถกรองและตรวจสอบได้ว่าค่าที่ส่ง/รับ เป็นค่าที่เหมาะสมหรือไม่ ถ้าเราจะเขียน Module ธรรมดาเพื่อเลียนแบบให้โค้ดอื่น "เข้าตามตรอก ออกตามประตู" เหมือนกันก็ทำได้ แต่การอ้างชื่อจาก Module ของเราคงไม่สะดวกเท่าการเขียนด้วย Class Module และจะทำไปทำไมในเมื่อมี Class Module ให้ใช้อยู่แล้ว ผมก็เคยมีงานชิ้นนึงที่ตัดสินใจใช้ Class Module เพราะมีความซับซ้อนและเห็นว่าการเขียนในรูปแบบ Object.Property = ค่า และ Object.Method จะทำให้อ่านโค้ดได้เข้าใจมากกว่าเขียนเป็น Variable = ค่า และ Call Procedure มากๆครับ

ผมไม่ได้มีความเชี่ยวชาญอะไรเกี่ยวกับ OOP คงตอบได้แค่นี้ก่อนครับ

394
กรณีออเดอร์ 1 ใบมีได้หลายรายการ  ก็นึกถึงรูปแบบใบสั่งซื้อทั่วๆไปนี่แหล่ะครับ  รายการแรกในใบออเดอร์ก็มีค่าของเลขบรรทัดเป็น 1  รายการที่สองก็เป็น 2  อย่างนี้ไปเรื่อยๆ  ส่วน Primary Key ก็ประกอบด้วยสองฟิลด์ ฟิลด์เลขใบออเดอร์และฟิลด์เลขบรรทัดครับ

395
ปกติในระบบงาน ทุกเทเบิลจะมีการกำหนดฟิลด์ที่เป็น Primary Key เอาไว้ ระบบฐานข้อมูลจะไม่ยอมให้ค่าในฟิลด์เหล่านี้ซ้ำกัน  Primary Key อาจประกอบไปด้วยหลายฟิลด์ก็ได้  แต่ถ้ามี Primary Key อยู่แล้ว ก็กำหนด Key อื่นๆเพิมเติมก็ได้ และกำหนด Unique property ของ Key นั้นเป็น No หรือ Yes เพื่อให้ค่าในฟิลด์ซ้ำกันหรือไม่ซ้ำกันเหมือนคุณสมบัติของ Primary Key ก็ได้  (Key เหล่านี้รวมๆกันจะอยู่ในหมวดที่ Access เรียกว่า Indexes) 

ในกรณีของออเดอร์สินค้า หมายเลขออเดอร์จะไม่ซ้ำกันอยู่แล้ว ถ้าธุรกิจคุณเป็นแบบออเดอร์เดียวมีแค่รายการเดียว ก็กำหนดฟิลด์หมายเลขออเดอร์เป็น Primary Key ไปได้เลย ดังนั้นเมื่อก็อปปี้มาแล้ว ถ้าไม่แก้ไขค่าฟิลด์ที่เป็น Primary Key ระบบก็จะฟ้องออกมาและไม่ยอมบันทึกลงเทเบิลให้ แต่ถ้าที่ป้อนเป็นแค่รายการหนึ่งในหลายๆรายการในใบออเดอร์ ก็จะต้องหาฟิลด์อื่นๆมารวมกับเลขออเดอร์เพื่อเป็น Primary Key ถ้าระบบไม่ยอมให้ออเดอร์สินค้าตัวเดียวกันปรากฏมากกว่าหนึ่งรายการในออเดอร์หนึ่งๆ ก็เอาฟิลด์เลขอเดอร์และฟิลด์รหัสสินค้ามารวมเป็น Primary Key ได้  แต่ถ้าระบบยอม ก็ต้องหาอย่างอื่นมาเพิ่มเติมเข้าไปอีกเพื่อให้แตกต่างจากรายการอื่นๆในใบนั้นให้ได้ หรือกำหนดฟิลด์หมายเลขบรรทัดรวมกับฟิลด์หมายเลขออเดอร์ก็เป็นวิธีหนึ่งที่ทำๆกันครับ

396
RowSource และ ColumnCount property มีค่าเป็นอะไรครับ และอีกอย่าง ทำไมระหว่างชื่อและนามสกุลถึงดูเหมือนมีช่องว่างหลายช่อง

หน้า: 1 ... 19 20 21 [22] 23 24 25 ... 29