Est-il possible de créer une requête récursive dans Access?
Question
J'ai une table de job
Id
ParentID
jobName
jobStatus
Le ParentID racine est 0.
Est-il possible dans Access pour créer une requête pour trouver une racine pour un job
donné?
La base de données est mdb sans tables liées. La version d'accès est 2003. Un job
peut avoir plusieurs niveaux de profondeur petits enfants.
La solution
Non, ce n'est pas. Les requêtes récursives sont pris en charge dans SQL Server après sServer 2005, mais pas dans Access.
Si vous savez d'avance le nombre de niveaux, vous pouvez écrire une requête, mais ce ne serait pas une récursive.
Dans SQL Server, CTE (extension SQL) est utilisé pour cela: voir http://blog.crowe.co.nz/archive/2007/09/06/Microsoft-SQL-Server-2005 --- CTE-exemple-de-a-simple.aspx
SQL régulier cependant ne prend pas en charge la récursivité.
Autres conseils
Il est possible dans Access pour créer une requête pour trouver la racine de votre travail donné. Ne pas oublier la puissance des fonctions VBA. Vous pouvez créer une fonction récursive dans un module VBA et utiliser le résultat comme un champ de sortie dans votre requête.
Exemple:
Public Function JobRoot(Id As Long, ParentId As Long) As Long
If ParentId = 0 Then
JobRoot = Id
Exit Function
End If
Dim Rst As New ADODB.Recordset
Dim sql As String
sql = "SELECT Id, ParentID FROM JobTable WHERE Id = " & ParentId & ";"
Rst.Open sql, CurrentProject.Connection, adOpenKeyset, adLockReadOnly
If Rst.Fields("ParentID") = 0 Then
JobRoot = Rst.Fields("Id")
Else
JobRoot = JobRoot(Id, Rst.Fields("ParentID")) ' Recursive.
End If
Rst.Close
Set Rst = Nothing
End Function
Vous pouvez appeler cette fonction récursive de votre requête en utilisant le générateur de requêtes ou en tapant simplement le nom de la fonction avec des arguments dans un champ de requête.
Il donnera la racine.
(je reconnais l'OP est d'un an maintenant, mais je suis obligé de répondre quand tout le monde dit ce qui est impossible est possible).
Vous ne pouvez pas interroger récursive.
Vous pouvez faire un certain nombre arbitraire de jointures gauche, mais vous ne pourrez monter autant de niveaux que vous avez rejoint.
Vous pouvez aussi utiliser "Nested Set modèle" de Celko pour récupérer tous les parents. Cela nécessitera la modification de la structure de votre table, d'une façon qui rend plus compliqué insertions et mises à jour.
Cela ne peut pas être fait en utilisant SQL pur dans Access, mais un peu VBA va un long chemin.
Ajoutez une référence à Microsoft Scripting Runtime . ( Outils -> Références ... )
Cela suppose que l'identifiant est unique, et qu'il n'y a pas de cycles: par exemple, Le parent A est B, mais le parent B est A.
Dim dict As Scripting.Dictionary
Function JobRoot(ID As Long) As Long
If dict Is Nothing Then
Set dict = New Scripting.Dictionary
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT ID, ParentID FROM Job", dbOpenForwardOnly, dbReadOnly)
Do Until rs.EOF
dict(rs!ID) = rs!ParentID
rs.MoveNext
Loop
Set rs = Nothing
Dim key As Variant
For Each key In dict.Keys
Dim possibleRoot As Integer
possibleRoot = dict(key)
Do While dict(possibleRoot) <> 0
possibleRoot = dict(possibleRoot)
Loop
dict(key) = possibleRoot
Next
End If
JobRoot = dict(ID)
End Function
Sub Reset() 'This needs to be called to refresh the data
Set dict = Nothing
End Sub
OK voici donc la vraie affaire. Tout d'abord, quel est le public cible pour votre requête .. un formulaire? rapport? Fonction / proc?
Formulaire: mises à jour de besoin? utiliser le contrôle TreeView alors que maladroit cela fonctionnera bien. Rapport: en cas ouvert, utilisez une forme de paramètre pour définir le niveau « Boss Job », puis gérer la récursivité et remplir vba un jeu d'enregistrements avec les données dans l'ordre souhaité . établir des rapports à ce recordset et rempli d'enregistrements traiter le rapport. Fonction / procédure: fonctionne à peu près le même que la charge de données décrites dans le rapport ci-dessus. Grâce à un code, gérer le nécessaire « arbre qui marche » et stocker le jeu de résultats dans l'ordre souhaité dans un processus et recordset au besoin.
La contribution de Zev m'a donné beaucoup d'inspiration et d'apprentissage. Cependant, nécessaire pour faire des modifications au code. S'il vous plaît noter que ma table est appelée "tblTree".
Dim dict As Scripting.Dictionary
Function TreeRoot(ID As Long) As Long
If dict Is Nothing Then
Set dict = New Scripting.Dictionary ' Requires Microsoft Scripting Runtime
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("tblTree", dbOpenForwardOnly, dbReadOnly)
Do Until rs.EOF
dict.Add (rs!ID), (rs!ParentID)
rs.MoveNext
Loop
Set rs = Nothing
End If
TreeRoot = ID
Do While dict(TreeRoot) <> 0 ' Note: short version for dict.item(TreeRoot)
TreeRoot = dict(TreeRoot)
Loop
End Function
Et il y a une autre fonction utile dans le même contexte. « ChildHasParent » retourne vrai, si correspond à l'enfant le ParentID fourni à tous les niveaux d'imbrication.
Function ChildHasParent(ID As Long, ParentID As Long) As Boolean
If dict Is Nothing Then
Set dict = New Scripting.Dictionary ' Requires Microsoft Scripting Runtime
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("tblTree", dbOpenForwardOnly, dbReadOnly)
Do Until rs.EOF
dict.Add (rs!ID), (rs!ParentID)
rs.MoveNext
Loop
Set rs = Nothing
End If
ChildHasParent = False
Do While dict(ID) <> 0 ' Note: short version for dict.item(TreeRoot)
ID = dict(ID)
If ID = ParentID Then
ChildHasParent = True
Exit Do
End If
Loop
End Function