2009-04-18 19 views
18

Ho un job tavoloÈ possibile creare una query ricorsiva in Access?

Id 
ParentID 
jobName 
jobStatus 

La radice ParentID è 0.

E 'possibile in Access per creare una query per trovare una radice per un dato job? Il database è MDB senza tabelle collegate. La versione Access è del 2003. A job possono essere profondi bambini di diversi livelli.

+0

Si prega di essere più precisi quando si dice "il lavoro può essere in diversi livelli di un bambino" o incollare alcuni dati fittizi. –

risposta

8

No, non lo è. Le query ricorsive sono supportate in SQL Server dopo SServer 2005, ma non in Access.

Se si conosce il numero di leve in anticipo, è possibile scrivere un qry, ma non sarebbe ricorsivo.

In SQL Server, CTE (un'estensione SQL) viene utilizzato per questo: vedere http://blog.crowe.co.nz/archive/2007/09/06/Microsoft-SQL-Server-2005---CTE-Example-of-a-simple.aspx

SQL regolare però non ha il supporto ricorsività.

+0

@onedayquando:/"SQL standard ANSI/ANSI" ha un suffisso all'anno, per distinguerlo da "ISO/ANSI Standard SQL-99"? –

4

Non è possibile eseguire query in modo ricorsivo.

È possibile eseguire un numero arbitrario di giunzioni a sinistra, ma si potranno salire tutti i livelli quanti si sono uniti.

Oppure è possibile utilizzare Celko's "Nested Set Model" per recuperare tutti i genitori. Ciò richiederà la modifica della struttura della tabella, in modo da rendere più complicati gli inserti e gli aggiornamenti.

+0

Non è possibile eseguire query in modo ricorsivo. -> Non in accesso 03 in ogni caso, in molte altre query langs è possibile – Peter

+0

Sì, "tu" l'OP, che sta utilizzando Access, non può eseguire query in modo ricorsivo. – tpdi

+0

Sebbene i set nidificati siano stati resi popolari da Celko, il metodo è accreditato a Michael J. Kamfonas (http://en.wikipedia.org/wiki/Joe_Celko). – onedaywhen

24

È possibile in Access creare una query per trovare la radice del lavoro specificato. Non dimenticare la potenza delle funzioni VBA. È possibile creare una funzione ricorsiva in un modulo VBA e utilizzarne il risultato come campo di output nella query.

Esempio:

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 

È possibile chiamare questa funzione ricorsiva dalla query utilizzando il generatore di query o semplicemente digitando il nome della funzione con gli argomenti in un campo query.

Produrrà la radice.

(Riconosco che l'OP ha un anno ormai, ma sono costretto a rispondere quando tutti dicono che l'impossibile è possibile).

+11

Mentre applaudo il tuo intervento, la domanda originale in realtà limita la risposta a una query, che è, in effetti, impossibile senza VBA. Se sia giusto interpretare "in una query" come "senza dipendenza da funzioni VBA personalizzate" è un altro problema. Penso che sia bello avere la tua soluzione qui anche se non è quello che il poster originale cercava, perché altri con lo stesso problema potrebbero non avere dubbi su di esso non essendo una pura risposta SQL. –

+1

Ciò è estremamente inefficiente, poiché significa aprire un nuovo recordset ogni volta che viene chiamato. Suggerirei almeno di mantenere il recordset in una variabile a livello di modulo o di caricare l'intero recordset in uno script in-memory con script a livello di modulo che probabilmente consentirebbe un accesso più veloce, come [this] (http: // stackoverflow. COM/a/32.161.506). @ David-W-Fenton –

1

OK, ecco l'affare REALE. In primo luogo, qual è il pubblico di destinazione per la tua query .. un modulo? rapporto? Funzione/proc?

Modulo: Aggiornamenti necessari? usa il controllo di treeview mentre goffamente funzionerà bene. Report: nell'evento open utilizzare un modulo di parametri per impostare il livello "Boss Job", quindi gestire la ricorsione in vba e riempire un recordset con i dati nell'ordine desiderato. imposta il recordset dei report su questo recordset pieno ed elabora il report. Funzione/Procedura: funziona più o meno come un carico di dati descritto nel rapporto sopra. Tramite il codice, gestire il "tree walking" necessario e memorizzare il set di risultati nell'ordine desiderato in un recordset ed elaborarlo secondo necessità.

+2

Che controllo Treeview? Non esiste una versione di visualizzazione nativa disponibile in nessuna versione di Access di cui sono a conoscenza. –

+0

Questa potrebbe essere una buona aggiunta alle risposte esistenti, ma ha bisogno di lavoro. Ad esempio, Elimina "OK, ecco l'affare REALE."; Rompi le opzioni in proiettili o paras. Espandi e chiarisci ciascuno di essi. –

2

Questo non può essere fatto utilizzando puro SQL in Access, ma un piccolo VBA ha una lunga strada.

Aggiungere un riferimento alla Microsoft Scripting Runtime (Strumenti ->Riferimenti ...).

Ciò presuppone che l'ID sia univoco e che non vi siano cicli: ad es. madre di A è B, ma madre di B è 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 
0

contributo di Zev mi ha dato una grande quantità di ispirazione e di apprendimento. Tuttavia, è necessario apportare alcune modifiche al codice. Tieni presente che la mia tabella si chiama "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 

E c'è un'altra funzione utile nello stesso contesto. "ChildHasParent" restituisce true, se il bambino corrisponde al ParentID fornito in qualsiasi livello di nidificazione.

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