Questa sera nel rispondere ad una domanda apparsa sul newsgroup
microsoft.public.it.sql che chiedeva come sia possibile generare un result set ordinato in modo casuale in Microsoft Access, mi è capitata in mano una vecchia risposta che diedi un paio di anni fa.
Nel rileggerla ho trovato interessante la soluzione proposta, così ho pensato di inserirla nel mio blog.
Vediamo di che si tratta.
In SQL Server è piuttosto semplice ottenere un result set ordinato in modo casuale, ad esempio specificando nella clausola ORDER BY la funzione NEWID() che per ogni riga del result set genera un nuovo valore.
In Access spesso si ricorre alla funzione Jet-SQL RND() (come riportato nell'articolo
How to find N records in random order in Access 2002 della Knowledge Base) ma purtroppo questa funzione genera dei valori pseudo-casuali, ovvero casuali solo all'apparenza.
La verifica è presto fatta.
Immaginiamo di scrivere la query
qryTop10Products e di memorizzarla nel database Northwind(Nwind.mdb):
SELECT TOP 10 ProductName
FROM Products
ORDER BY Rnd(ProductID)
1) Eseguendo la query, otterremo un certo ordinamento;
2) Eseguendola una seconda volta otterremo un ordinamento differente;
3) Chiudiamo Access e riapriamolo;
4) Eseguiamo nuovamente la tua query e... otterremo lo stesso ordinamento del punto 1;
5) Eseguendola una seconda volta e otterremo l'ordinamento del punto 2.
E così via...
Un workaround potrebbe essere quello di utilizzare una funzione Jet-SQL differente, passando alla query un parametro di input "casuale".
Vediamo un esempio.
1) Come prima cosa ho modifichiamo la query
qryTop10Products nel seguente modo:
PARAMETERS [Seed] IEEEDouble;
SELECT TOP 10 ProductName
FROM Products
ORDER BY SIN(ProductID * Seed); 2) Infine ho preparato la pagina
Products.asp che richiama la query valorizzando il parametro Seed con l'ora corrente del webserver convertita in double e moltiplicata per 10.000:
<%@ Language="VBScript" %>
<%
Dim cmd
Dim rs Const adCmdStoredProc = 4
Const adDouble = 5
Const adParamInput = 1
Const adUseClient = 3
Const adOpenStatic = 3
Const adLockReadOnly = 1 On Error Resume Next ' Creo un oggetto ADODB.Command che richiama la Query qryTop10Products
Set cmd = Server.CreateObject("ADODB.Command")
With cmd
.CommandType = adCmdStoredProc
.CommandText = "qryTop10Products"
.Parameters.Append .CreateParameter("Seed", adDouble, adParamInput, , cdbl(Time) * 10000)
.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & Server.MapPath("Nwind.mdb")
End With ' Creo un oggetto ADODB.Recordset client side
Set rs = CreateObject("ADODB.Recordset")
With rs
.CursorLocation = adUseClient
.CursorType = adOpenStatic
.LockType = adLockReadOnly ' Apro il recordset
.Open cmd ' Disconnetto il recordset
Set .ActiveConnection = Nothing
End With ' Distruggo il Command
Set cmd = Nothing
%>
<HTML>
<META HTTP-EQUIV="Refresh" CONTENT="5; URL=http://localhost/products.asp">
<META HTTP-EQUIV="Expires" CONTENT="0">
<TITLE>
Top 10 Product list:
</TITLE>
<BODY>
<H1> Top 10 Product list: </H1>
<%
With rs
Do Until .EOF
Response.Write .Fields("ProductName").Value & "<BR>"
.MoveNext
Loop ' Chiudo il Recordset
.Close
End With ' Lo distruggo
Set rs = Nothing
%> </BODY>
</HTML> Richiamando la pagina via browser noteremo che ogni 5 secondi verrà generata una vista di prodotti effettivamente casuale