Jest tak:
Kwerenda wybiera rekordy.
(zadziwiające, no nie?)
Jedym z warunków jest, żeby ID rekordu znajdowało się na liście typu
multiselect (a konkretnie z właściwością Multiselect ustawioną na
"Extended")
Czyli mam listę wielokrotnego wyboru i chcę odfiltrować te rekordy, których
ID znajdują się wśród "wartości" listy.
("Wartości" piszę w cudzysłowie, bo wartością takiej listy jest w
rzeczywistości zawsze Null)
Moje rozwiązanie jest takie, że napisałem sobie funkcję, która dostaje na
wejście ID rekordu, przegląda sobie kolekcję ItemsSelected listy i jak
znajdzie id rekordu wśród pozycji wybranych, zwraca -1, a jak nie znajdzie,
zwraca 0. Działa, ale wymaga napisania funkcji i użycia jej w kwerendzie.
Można jakoś eleganciej?
--
PL
Hej.
Ja rozwiazalem to nieco inaczej - jak buduje selecta to przegladam kolekcję
ItemsSelected, ale buduje sobie z nich ciag x,y,z - ktory nastepnie
wykorzystuje w warunku in ( i tutaj moja lista) zapytania.
pzdr.
Tomek
Też zawsze mnie niepokoiła niemożność efektywnego sparametryzowania
warunku "In (...)"
Owszem, przy pomocy funkcji Eval można napisać warunek:
(...)
Where
Eval([ID] & " In " & fLst()) = True
gdzie fLst:
Function fLst()
Dim strItems As String
Dim varItem As Variant
Dim lst As Access.ListBox
Set lst = Forms!frmList!Lista0
With lst
For Each varItem In lst.ItemsSelected
strItems = strItems & "," & lst.ItemData(varItem)
Next
If Len(strItems) > 0 Then
strItems = Mid(strItems, 2)
Else
strItems = "0"
End If
End With
fLst = "(" & strItems & ")"
End Function
ale nie czarujmy się ! Jet musi każdorazowo pobrać wartość pola, następnie
przekazać ją do funkcji Eval (dość chybotliwej) i porównać w wyrażeniu "In
(....)"
Indeksy i tak szlag trafił! Czyli efekt taki sam albo gorszy jak przy
wykorzystaniu Twojej funkcji.
Ale jest jeszcze inne "In", bardziej naturalne dla Jeta i z indeksami !
1. kwerenda:
SELECT * FROM mojaTabela As T
WHERE
Exists (Select ID From tmp Where tmp.ID=T.ID) =
List2Table("tmp", "frmList", "Lista0")
2. funkcja List2Table:
Function List2Table(tmpTbN As String, frmN As String, lstN As String) As
Boolean
Dim strItems As String
Dim varItem As Variant
Dim ws As Workspace
Dim db As Database
Dim lst As Access.ListBox
Dim wsp As Currency
Dim i As Long
On Error GoTo err_exit
Set lst = Forms(frmN)(lstN)
Set ws = DBEngine(0)
ws.BeginTrans
Set db = CurrentDb
db.Execute "Delete From " & tmpTbN
With lst
wsp = .ItemsSelected.Count / .ListCount
If wsp < 0.5 Then
For Each varItem In lst.ItemsSelected
db.Execute "Insert Into " & tmpTbN & " (ID) Values(" &
lst.ItemData(varItem) & ")"
Next
Else
For i = 0 To .ListCount - 1
If .Selected(i) = False Then
db.Execute "Insert Into " & tmpTbN & " (ID) Values(" &
.ItemData(i) & ")"
End if
Next
End If
End With
ws.CommitTrans
List2Table = (wsp < 0.5)
Exit Function
err_exit:
If Not ws Is Nothing Then
ws.Rollback
End If
MsgBox Err.Description
End Function
3. uwagi:
tabela "tmp" (pierwszy parametr funkcji) musi już wcześniej istnieć w
bazie! Nie da się jej utworzyć "on fly" (jest to pewnego rodzaju minus)
Jako parametr została dołożona w zasadzie jedynie dla przejrzystości
SQL'a, ale też dzięki temu możemy użyć kilku takich pomocniczych tabel,
nawet w jednej kwerendzie.
Z moich testów wynika, że opłaca się w tej tabeli ustawić indeks na polu
ID (unikalny? - to zależy od listy)
Dla nawet maksymalnego wyboru na liście (połowa zaznaczeń) opóźnienie
poprzez n-insertów jest rekompensowane szybkością wyliczenia warunku
"Exists"
Z tych samych powodów co powyżej z tabelą, nie da się indeksu uprzednio
dropnąć a na
końcu funkcji ponownie założyć, bo tabela tmp jest już blokowana drugim
warunkiem:
Exists (Select ID From tmp Where tmp.ID=T.ID)
Trochę to zagmatwane w porównaniu z tworzeniem wyrażenia SQL w kodzie, ale
wydaje się że nie całkiem pozbawione sensu ...
Tym bardziej jeśli w liście zaznaczyć dużo ;-)
4. W zależności ile tych itemów jest zaznaczonych funkcja zwraca true
(szukaj pasujących do tabeli) lub false (szukaj niepasujących).
Dlatego maksymalne opóźnienie w wyliczeniu kwerendy nastąpi przy
zaznaczeniu połowy listy!
5. Na formularzy warto w przypadku długich list dołożyć pole wyliczane:
=Lista0.ListCount
aby pełnego wypełnienia nie musiała dokonywać funkcja List2Table()
PS.
Jeśli ktoś nie rozumie dlaczego moja funkcja miała by być lepsza od
funkcji Piotra, to śpieszę z wyjaśnieniem:
Moja funkcja pobiera _stałe_ parametry (niezależne od pól wykorzystywanych
w kwerendzie), więc wykonana zostanie raz !
Resztę (warunek "Exists") Jet sobie zoptymalizuje.
Testowane na a'97 i a'2k.
PS.2
Wszelkie uwagi i spostrzeżenia mile widziane (także krytyczne)
--
KN
archiwum grupy:
http://groups.google.pl/advanced_group_search?&as_ugroup=pl*msaccess
kw:
- itemsSelected, lista, kwerenda, filtrowanie, multiwybór, multiselect