ช่วยหน่อยคับ
กระทู้เก่าบอร์ด อ.Yeadram

 3,496   13
URL.หัวข้อ / URL
ช่วยหน่อยคับ

ช่วยหน่อยคับ
คือผมต้องการออกแบบระบบเกี่ยวกับเก็บประวัติสมาชิก
โดยให้สร้างรหัสสมาชิกอัติโนมัติ ต่อท้ายกันไปเรื่อย ๆ
เช่น 520001 - 529999 / 52 คือปีที่เป็นสมาชิก /0001-9999 คือ ลำดับที่
กำหนดให้ 52 เป็นค่าคงที่น่ะคับ
ผมเข้าใจว่าถ้าจะเขียนโค้ดจะต้อง หาเลขลำดับที่ ที่มากที่สุดออกมาก่อน
จากนั้นก็บวกเพิ่มเข้าไปอีก 1 เราก็จะได้รหัสสมาชิกล่าสุด อย่างนี้
ถูกต้องไหมคับ? (จะบวกได้ไหมในเมื่อรหัสสมาชิกเป็น Text)?

แล้วถ้าผมจะให้โปรแกรมที่ออกรหัสสมาชิกอยู่ที่เครื่อง client 2-3 เครื่อง
โดยลิงค์ตารางมาจากเครื่อง Server มันจะมีปัญหาตรงที่ออกรหัสสมาชิกหรือไม่คับ
หมายถึง เวลาเราเรียกค่าที่มากที่สุด หากเครื่อง client เรียกพร้อมกัน (มีสิทธิ์พร้อมกันได้)
เครื่อง client ก็จะได้รหัสสมาชิกค่าที่มากที่สุดเหมือนกัน อย่างนี้รหัสมันก็ต้องชนกัน(ซ้ำกัน)แน่นอน
แล้วก็ต้องฟ้อง err เพราะเราตั้งให้รหัสสมาชิกเป็นคีย์หลัก เรามีวิธีแก้ปัญหานี้ได้อย่างไรบ้างคับ


ช่วยหน่อยคับผม

13 Reply in this Topic. Dispaly 1 pages and you are on page number 1

1 @R01884
ครับ การรันแบบนี้ ควรให้ฟิลด์นี้เป็น text ดีที่สุดครับ

การหาเลขที่สุดท้ายเราก็วางแนวคิดไว้ว่า ต้องการหาค่ามากที่สุดที่ขึ้นต้นด้วย 52
ถ้าได้ไม่ได้ค่าอะไรให้เป้น 1
ถ้าได้ค่าออกมาให้บวกด้วย 1

ก็จะได้โค้ดว่า
dim maxID
dim NextID
maxID= dmax("ฟิลด์รหัส", "ตาราง", "left(ฟิลด์รหัส,2)='52'")l
if maxID = 0 then
NextID = "520001"
else
NextID = "52" & format(cint(right(MaxID,4))+1,"0000")
end if


ส่วนที่มีข้อกังวลว่า แต่ละ client อาจจะได้ ค่า Max เป็นค่าเดียวกัน อันนี้ต้องปรับแนวคิดใหม่ เราไม่ต้องสนใจว่า มันจะได้ค่าเดียวกันครับ ให้สนใจว่า จะบันทึกค่าต่อไปอย่างไร

ถ้าตารางนี้ มีฟิลด์รหัส เป็น Primary ผมแนะนำอย่างนี้ครับสำหรับการวางแนวคิด

1. ให้ client ไปหาค่า Max ออกมา แล้วเตรียมค่าถัดไป (NextID) เอาไว้เลย (ขั้นตอนนี้ใช้วิธีทำตามที่แนะนำไปแล้ว ด้านบน ได้ครับ)
2. เมื่อได้ NextID ให้ทำการบันทึกเข้าตารางไปก่อน ฟิลด์อื่นยังไม่ต้องบันทึกก็ได้ (จังหวะนี้ใช้ Insert Into...)
3. ถ้าการบันทึกไม่มี error ใดๆ ก็ค่อยให้ client บันทึกข้อมูลฟิลด์อื่นๆ ต่อไปได้เลย แล้วทำการเซฟ (จังหวะนี้ใช้ Update...set...where...)

ปัญหาที่ควรกังวลต่อไปคือ งานในลำดับที่ 2 ครับ
- เป็นไปได้ว่า มี client บันทึกพร้อมกันทำให้เกิด error
- เป็นไปได้ว่า มี อีก client บันทึกค่า NextID ของเราเข้าไปก่อนหน้านี้ทำให้เราบันทึก NextID ของเราเข้าไปอีกไม่ได้ (client ฝั่งนู้นบันทึกก่อนเรา)

การแก้ไขครับ (เจาะเข้าไปในงานลำดับที่ 2 เพียงอย่างเดียว)
2.1 ให้บันทึก NextID
2.2 ถ้า error ให้รอ x วินาที
2.3 ทำการบันทึกเข้าอีกครั้ง
2.4 ถ้าเกิด error ให้กลับไปเริ่มทำงานในลำดับที่ 1 ใหม่อีกครั้ง (สันนิษฐานว่า มี client เครื่องอื่นใช้เลขที่นี้ไปแล้ว)

เราจะเจาะเข้าไปในงานลำดับ ที่ 2.2 อีกครับ
ถ้าเราระบุเป็นค่าตายตัวไปว่า ให้รอ 3 วินาที ทุกๆ client ต่างใช้โปรแกรมเดียวกัน มันก็คือรอกันคนละ3 วินาทีเท่ากัน ดังนั้น มันก็อีหรอบเดิม คือส่งคำสั่งเข้าไปพร้อมกัน แม้จะรอกี่รอบๆ ก็ตาม   ฐานข้อมูลบน server มันก็ช็อคเหมือนเดิม
ดังนั้น เราจะแก้ปัญหาตรงนี้ด้วยการ "ไม่ระบุค่าตายตัว" ให้มันว่า ต้องรอกี่วิ
เราจึงใช้การ random เอาระหว่าง 1-5 ว่าจะให้ client ตัวนี้ รอกี่วิ ค่อยส่งคำสั่ง (SQL Statement) เข้าไปใหม่

ลองเอาไปวางโฟลว์ดูครับ
ผมได้แต่คิดละครับ แต่ยังไม่เคยลอง เพราะงานของผมยังไม่เกี่ยวข้องกับ server เลยครับ อิอิ



2 @R01903
ขอบคุณคับ
แต่ขอถามเพิ่มเติมคือ
เมื่อ รหัสมาถึงเลข 039999 พอสร้างรหัสใหม่ ซึ่งมันจะต้องได้ 040000
แต่มันกลับ err โดยฟ้องว่า Over Flow
ไม่ทราบว่ามันเป็นเพราะอะไรคับ
3 @R01904
ผมแก้ไขโค้ดเป็นดังนี้คับ
Dim maxID
Dim NextID
maxID = DMax("ID", "tblMember")
    If maxID = 0 Then
        NextID = "000001"
    Else
        NextID = Format(CInt(Right(maxID, 6)) + 1, "000000")
        Me.txtID = NextID
    End If
4 @R01905
ได้แล้วคับ
จาก
NextID = Format(CInt(Right(maxID, 6)) + 1, "000000")
เป็น
NextID = Format(Val(Right(maxID, 6)) + 1, "000000")
5 @R01906
อีกนิดคับ คือ กรณีที่ยังไม่มีเร็คคอร์ด มันจะฟ้อง
err : invalid use of null
ที่บรรทัด
maxID = DMax("CtlID", "tblCatalog")
6 @R01907
ลองใช้ NZ() ช่วยหรือยังครับ
เช่น

maxID = NZ(DMax("CtlID", "tblCatalog"),0)
7 @R01908
คิดเล่นๆ ว่าเลขอัตโนมัติ เอาไว้หาตอน ก่อนบันทึกได้ไหมครับ
เช่น BEFOREUPDATE สั่งให้มัน อ่านเลขสูงสุดอีกครั้ง เพื่อให้แน่ใจว่า
ได้ค่าสูงสุดก่อนบันทึก ผมคิดว่าโอกาศชนกันน่าจะน้อยมาก
เป็นแค่แนวคิด ไม่เคยลอง หรือถ้าลองแล้ว ได้ผลอย่างไรบอกด้วยแล้วกัน
8 @R02166
พิสูจน์แล้วครับ แนวคิดของผมไม่ถูกต้อง ถึงแม้จะดูว่าง่าย
ต้องทำแบบคุณ YEADRAM บอก
ลอง SAVE พร้อมกัน 2 เครื่อง ERROR จริงๆ
9 @R02180
ถ้า ERROR ให้รออีก 5 วินาที แล้วบันทึกซ้ำ
ผมจะเขียน CODE ว่าอ่ยางไรดีครับ
10 @R02182
ผมไม่แน่ใจนะครับ อาจารย์สันติสุขน่าจะคล่อง
แต่ผมจำได้พอเลาๆ ว่าน่าจะประมาณ
wait
หรือ shell wait อะไรซักอย่าง
หรืออาจจะต้องใช้ Api เข้าช่วย

หรืออาจจะต้องใช้ลูกเล่นของ Timerinterval ของฟอร์ม
ถ้าตั้งค่า timeinteval ของฟอร์มปัจจุบันลำบาก เราก็สร้างฟอร์มแบบ popup พร้อมจับเวลาของฟอร์มนั้นก็ได้ เมื่อครบเวลา ก็ให้ฟอร์มนั้นปิดตัวเอง โดยในเหตุการเมื่อปิดก็ส่งค่ากลับมาฟอร์มปัจจุบันบอกว่า ครบเวลาที่รอ แล้ว
ประมาณนี้ครับ น่าจะพอพลิกแพลงได้นะครับ
แต่ได้ถ้าคำสั่งพวก wait หรือ shell wait ผมว่าสะดวกกว่าเยอะเลยครับ

11 @R02183
ใช้ API นี้ก็ได้ครับ

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

เวลาใช้ก็สั่ง Sleep 5000 สำหรับ 5 วินาทีครับ
12 @R02204
ทดลองใช้แล้วครับ Ok
แต่ไปค้นใน HELP มีวิธีนี้ด้วยก็เลย POST เผื่อไว้ ใครจะใช้

Dim PauseTime, Start, Finish, TotalTime
If (MsgBox("Press Yes to pause for 5 seconds", 4)) = vbYes Then
    PauseTime = 5    ' Set duration.
    Start = Timer    ' Set start time.
    Do While Timer < Start + PauseTime
        DoEvents    ' Yield to other processes.
    Loop
    Finish = Timer    ' Set end time.
    TotalTime = Finish - Start    ' Calculate total time.
    MsgBox "Paused for " & TotalTime & " seconds"
Else
    End
End If


13 @R02205
ระวังนะครับ Timer จะให้ค่าเป็นวินาที เริ่มตั้งแต่เที่ยงคืนไปเรื่อยๆ พอถึงเที่ยงคืนก็รีเซ็ทเป็น 0 ใหม่ ดังนั้นถ้าคุณไปทำฟังก์ชั่นนี้ก่อนเที่ยงคืน แล้วระยะเวลา Start + PauseTime นั้นเกิดเลยเที่ยงคืนไป ทีนี้คุณจะวนลูปไม่รู้จบนะครับ
@ ประกาศใช้งานเว็บบอร์ดใหม่ => บอร์ดเรียนรู้ Access สำหรับคนไทย
แล้วจะใส่ลิ้งอ้างอิงมาที่โพสต์เก่านี้หรือไม่ก็ตามสะดวกครับ
Time: 0.2625s