AutoNr.zip
Autor: Krzysztof Pozorek
Baza w formacie MsAccess 97
Opis problemu:
Dziury w polu Autonumer powstają na skutek kasowania rekordów lub zaniechania wprowadzenia rekordu. Zdarza się, że dziury w numeracji są niepożądane.
Rozwiązanie:
Prezentowany przykład pokazuje sposób zapisywania nowych rekordów bez dziur. Przykład testowano na zestawie rekordów ok. 60 tys. rekordów. Uzyskane wyniki czasowe wynoszą ok. 1,8 sek. na dodanie nowego rekordu.
Rewelacyjne wyniki rzędu kilkudziesięciu milisekund(!) uzyskał Krzysztof Naworyta,
którego rozwiązanie cytuję poniżej (rozwiązanie Krzysztofa polega
na bardzo szybkim zawężaniu zestawu rekordów do przeszukania)
Poniżej zamieszczam rozwiązanie Krzysztofa (wersja
uaktualniona):
Option Compare Database
Option Explicit
Public Declare Function GetTickCount Lib "kernel32" () As Long
Function APITimer() As Long
APITimer = GetTickCount
End Function
Function FindDziura() As Long
Dim db As Database
Dim rs As Recordset
Dim krok As Long
Dim poz As Long
Dim fd As String
Dim mx As Long, mn As Long
Dim t
t = APITimer()
Set db = CurrentDb
Set rs = db.OpenRecordset("T", dbOpenTable)
' procedura otwiera rekordset na tabeli lokalnej
' w stosunku do bazy db.
' właściwe jej posortowanie wymuszane jest ustawieniem indeksu
With rs
' *** brak rekordów
If .BOF And .EOF Then
FindDziura = 1
Exit Function
End If
' od tego momentu zaczyna się właściwe szukanie
' można szukać dziur nie tylko w kluczu głównym
' ale w którymkolwiek polu, dla którego ustawiono indeks unikalny
' ustawienie właściwości Index recordsetu typu tabela jest duuużo szybszym
' sposobem właściwego posortowania niż instrukcja "Select ... Order By"
' tu następuje główne opóźnienie - ustawienie porządku
.Index = "LP"
fd = "Lp"
' *** dziura na początku zestawu
If .Fields(fd) <> 1 Then
FindDziura = 1
Exit Function
End If
' *** są rekordy
.MoveLast
'krok = .RecordCount
mn = 0 '1
mx = .RecordCount
poz = mx
'jeśli nie ma dziur
If .Fields(fd) = poz Then
FindDziura = poz + 1
Exit Function
End If
' *** jesli znaleziono "przesunięcie", zacieśniaj obszar
krok = -(mx - mn) / 2
Do
.Move krok
poz = poz + krok
'Debug.Print .Fields(fd), poz
If .Fields(fd) <> poz Then
mx = poz
'znak = -1, krok w stronę BOF
krok = -Int((mx - mn) / 2)
Else
'znak = +1, krok w stronę EOF
mn = poz
krok = Int((mx - mn) / 2)
End If
Loop Until mx <= mn + 1
' *** korekta
If .Fields(fd) = poz Then
Do Until .Fields(fd) <> poz
'Debug.Print 1
.Move 1
poz = poz + 1
Loop
FindDziura = poz
Else
Do Until .Fields(fd) = poz
'Debug.Print -1
.Move -1
poz = poz - 1
Loop
FindDziura = poz + 1
End If
t = APITimer() - t
Debug.Print "milisek: " & t
End With
rs.Close: Set rs = Nothing
db.Close: Set db = Nothing
End Function
Powyższy przykład wymaga istnienia tabeli o nazwie T i indeksowanego pola Lp (nazwa indeksu, to również Lp).
Krzysztof Pozorek