Opis problemu:
Idea liczby porządkowej Lp jest sprzeczna z zasadami relacyjności baz danych (właśnie ta relacyjność powoduje, ze żaden rekord nie jest trzeci, czy piąty, tylko równoprawny), dlatego trudno o jakieś naprawdę dobre rozwiązanie w tym zakresie. Niemniej jednak i na to znalazły się sposoby. Możliwości numerowania rekordów w zasadniczy sposób zależą od tego, w jakim obiekcie accessowym chcemy umieścić to nasze Lp.
Raporty
W raportach uzyskanie liczby porządkowej jest najprostsze. Wystarczy utworzyć
pole tekstowe z wartością =1
i ustawić tam sumę bieżącą.
Formularze
W formularzach nie ma możliwości ustawienia sumy bieżącej. Tutaj uzyskanie Lp
jest znacznie bardziej skomplikowane, ponieważ w przeciwieństwie do raportu, na
formularzu może się zmieniać porządek sortowania, można kasować i dopisywać
rekordy - a nasze Lp musi się automatycznie przenumerować. Aby uzyskać efekt
Lp, należy napisać swoją funkcje, którą wstawiamy do pola tekstowego jako
źródło formantu. Znam dwie zasady budowania takich funkcji:
a) metoda zliczania rekordów na formularzu,
b) metoda wykorzystujące właściwości recordsetu AbsolutePosition
lub
PercentPosition
.
Która z tych metod jest lepsza - nie wiem. Zależy to od konkretnych zastosowań. Sposób a) wymaga istnienia klucza unikalnego, trudno go też polecać dla zestawów rekordów rzędu tysięcy, bo działa dość wolno, z kolei metoda b) gorzej się zachowuje w momencie dodawania nowego rekordu, bo wtedy jeszcze nie istnieje odpowiedni wiersz w recordsecie, do którego można się odwołać (próbą rozwiązania tego problemu jest program RecNumberA2k Roberta Krawca).
Poniżej podam przykłady obu rozwiązań.
'****************************** 'Przykład metody a) 'Działa świetnie, ale wymaga unikalnego klucza ID. '****************************** Public Function Lpz(F As Form, ID) On Error GoTo Koniec Dim Rs As Recordset, ZliczID As Long Set Rs = F.RecordsetClone Rs.MoveFirst ZliczID = 1 Do Until (id = Rs!id) Or Rs.EOF ZliczID = ZliczID + 1 Rs.MoveNext Loop Koniec: If Err Then If Not IsNull(id) Then Lpz = ZliczID End If Else Lpz = ZliczID End If End Function '******************** 'Przykład metody b) 'Działa szybciej niż metoda a), ale gorzej 'zachowuje się przy dodawaniu rekordów. '******************** Public Function Lp(F As Form) On Error Resume Next Dim Rs As Recordset Set Rs = F.RecordsetClone Rs.Bookmark = F.Bookmark If Err = 0 Then If Rs.AbsolutePosition < Rs.RecordCount Then If (Rs.AbsolutePosition = Rs.RecordCount + F.Dirty) Then Lp = F.CurrentRecord Else Lp = Rs.AbsolutePosition + 1 End If End If End If End Function
Oto przykłady wywołania tych funkcji:
'W polu tekstowym na formularzu jako źródło formantu piszemy =Lpz([Form];[ID]) 'lub =Lp([Form])
Rozpakuj LiczPorz.zip, aby zobaczyć działanie tych funkcji w praktyce. Obie funkcje zostały wykorzystane do uzyskania Lp na tym samym formularzu ciągłym, co ułatwia porównywanie obu metod.
Kwerendy
W kwerendach są bardzo niewielkie możliwości wyświetlenia liczby porządkowej. Jedyny sposób polega na zastosowaniu podkwerendy (lub funkcji) z określonym warunkiem, która zlicza np. rekordy z ID mniejszym od ID w rekordzie bieżącym. A oto przykład takiej kwerendy z polem Lp:
'Załóżmy, że jest tabela Imieniny i chcemy 'utworzyć Lp o numeracji zgodnej z polem Data SELECT *, (SELECT Count(*) FROM Imieniny As T1 WHERE T1.Data<=Imieniny.Data) AS LP FROM Imieniny;
Niestety, rozwiązanie to ma jedną zasadniczą wadę - nie można zmieniać porządku
sortowania, ani zakładać filtrów, bo cała numeracja się psuje. Jednak w wielu
przypadkach uzyskany efekt zupełnie wystarcza.
Oddzielna sprawa, to zagadnienie szybkości działania. Funkcja Count(*)
jest dobrze optymalizowana za pomocą zastosowanej w Accessie technologii
Rushmore. Jednak mimo to nie jest to konstrukcja szybka dla dużych zestawów
rekordów.
Tabele
W tabelach nie ma możliwości wyświetlenia liczby porządkowej. Pewną namiastką Lp
jest oczywiście pole typu Autonumer, ale prawie zawsze zawiera ono dziury w
numeracji. Dziury w Autonumerze powstają zwykle z dwóch powodów:
- skasowanie rekordu,
- rezygnacja z zapisu nowego rekordu po rozpoczęciu edycji.
Jeśli liczy się tylko ciągłość numeracji, a nie powiązania referencyjne - można
zastosować funkcję OdbudujAutoNr()
przedstawioną dalej, ale zwykle
raz nadanego numeru w tabeli nie wolno(!) zmieniać i wtedy nie można go użyć w
charakterze Lp.
Public Function OdbudujAutoNr() On Error Resume Next Dim fld As Field, idx As Index Dim dbs As Database, tbldf As TableDef Set dbs = CurrentDb Set tbldf = dbs.TableDefs!Imien tbldf.Indexes.Delete "Klucz główny" 'usuwa stary indeks na polu ID tbldf.Fields.Delete "ID" 'usuwa stare pole ID tbldf.Fields.Refresh Set fld = tbldf.CreateField("ID", dbLong) 'tworzy nowe pole ID fld.Attributes = fld.Attributes + dbAutoIncrField 'ustawia Autonumer tbldf.Fields.Append fld Set idx = tbldf.CreateIndex("Klucz główny") 'tworzy nowy indeks, idx.Primary = True 'klucz unikalny idx.Fields.Append idx.CreateField("ID") tbldf.Indexes.Append idx OdbudujAutoNr=Err End Function
Przykład BudujAutoNr.zip pokazuje sposób użycia tej funkcji do odbudowania ciągłej numeracji w polu typu Autonumer. (Dodatkowo znajdziesz tam roczny wykaz imion, który przed kilku laty w porywie zaparcia wklepałem do komputera. Mam wrażenie, że wyświetlanie imienin w rogu aplikacji zawsze jakoś sympatycznie wpływa na odbiorcę naszego oprogramowania i czasem warto zrobić coś takiego)