[Databankontwerp] Uitgebreide treeview opslaan én uitlezen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Carharttguy
  • Registratie: Juli 2010
  • Laatst online: 04-07 23:09
Beste

Voor een hobby/leerproject ben ik bezig met een soort van pseudo-compiler die een 'zelf uitgevonden' taal vertaalt naar bekende/grote/veel gebruikte programmeertalen. (Tot nu toe alleen C#, later waarschijnlijk Java en Obj-C erbij)

Omdat dit programma dus een vertaalslag moet maken, heb ik een databank met daarin regels code. Nu wou ik die op een soort van treeview manier opslaan, zodat ik later zelf een kleine editor kan schrijven, waarmee ik gemakkelijk adhv die treeview kan zien waar ik die code opsla, een voorbeeldje van die 'treeview':

-Layout
--Control
---settext
---setheight
---setleft
---....
--Form
---Initialisation
---addcontrol
-Logic
--Variable
---declaration

De treeview kan (en zal) ook wel dieper gaan dan 3. (ongelimiteerd eigenlijk)
Nu heb ik op dat op een wel erg lame manier gedaan. Nu ziet mijn databankstructuur (voor zover je het structuur kan noemen) er zo uit:

idnamecodeplatform
1layout_control_settext<de overeenkomstige code>0


'Platform' verwijst naar een andere tabel, genaamd platforms, waar dan bijvoorbeeld 'Desktop' inzit (dat is dan C# code, want ik compileer met mono), maar in de toekomst kan daar ook 'Android' inzitten, dan zal '<de overeenkomstige code>' Java zijn, en er in de kolom platform iets anders zitten. Wat nu nog niet in de tabel zit, maar ik er wel in wil hebben, is een soort van reference code, dus bijvoorbeeld een row, en als die die eruit haalt, dan heb je eigenlijk 5 andere rows gecontamineerd.

Goed, dit is natuurlijk niet goed. Maar ik weet niet zo goed hoe ik het echt 'goed' moet doen, ik dacht aan een structuur zoals:

codeTabel:
idnamecodeplatform


treeview tabel
idparentidchildid


reference tabel
idcodeidreferenceid


Op die manier zou het (denk ik) ook wel gaan, maar dan heb ik nog totaal geen idee hoe ik de juiste code uit die database ga trekken met een query. Ik wil dat dan oplossen met een SQL-query, maar heb totaal geen idee hoe, ik dacht aan een function ofzo, maar ik weet dan ook niet hoe ik juiste parameters kan meegeven dat hij diep genoeg in de treeview kan.

Nu heb ik deze code in mijn programma om de juiste code uit de database te halen:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  Dim ps as MySQLPreparedStatement = con.Prepare("SELECT code FROM code WHERE name = ? AND platform = ?")
  
  ps.BindType(0, MySQLPreparedStatement.MYSQL_TYPE_STRING)
  ps.BindType(1, MySQLPreparedStatement.MYSQL_TYPE_LONGLONG)
  
  ps.Bind(0, Name.Lowercase)
  ps.Bind(1, cInt(platfrm))
  
  Dim rs As RecordSet = ps.SQLSelect
  
  If rs <> nil Then
    Dim ac As new aCode
    ac.Code = rs.Field("code").StringValue
    Return ac
  End if


Maar dat zal natuurlijk hevig veranderen, want als ik bijvoorbeeld wil zoeken naar 'initialisation', nja, dat kan van een form komen, maar natuurlijk ook van een object bijvoorbeeld. (En form zit dan weer onder layout, terwijl een object bij logic hoort).

Dus ik zou een SQL functie moeten hebben die iets doet als getCode('layout', 'object', 'initalisation', 'Desktop'), maarja, het kunnen zo 5 parameters zijn, maar ook 20 (bij wijze van spreken). En heb geen idee hoe ik dat in SQL functies moet oplossen. Kan iemand me een richting geven over hoe ik dit zou oplossen?

Of als iemand het helemaal anders zou aanpakken, dan hoor ik dat ook heel graag natuurlijk, andere inzichten kunnen heel nuttig zijn, daar ik niet zo thuis ben in deze materie, en dus nog veel te leren heb.

Alvast bedankt.

Acties:
  • 0 Henk 'm!

  • winkbrace
  • Registratie: Augustus 2008
  • Laatst online: 24-08 15:17
In Oracle heb je hiervoor de CONNECT BY clause.
Ik gebruik dit voor een menu structuur met onbekend aantal verdiepingen:

web_menu:
menu_idparent_menu_idtexturl


SQL:
1
2
3
4
5
6
7
select sys_connect_by_path(wm.menu_id, ', ') path
,      wm.menu_id
,      wm.parent_menu_id
,      wm.text
from   web_menu wm
connect by prior wm.menu_id = wm.parent_menu_id
start with wm.menu_id = 1


pathmenu_idparent_menu_idtext
, 11root
, 1, 221main1
, 1, 2, 332main1_sub1
, 1, 2, 442main1_sub2
, 1, 551main2
, 1, 5, 665main2_sub1
, 1, 5, 775main2_sub2
, 1, 5, 885main2_sub3



Ik heb wel eens een methode gemaakt voor een php mysql class die dit simuleert met wat recursion:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    /**
     * fetch array of complete query result
     * @param string $keyColumn
     * @return array
     */
    public function fetchAll($keyColumn = null)
    {
        $array = array();
        $i = 0;
        while ($row = $this->fetch())
        {
            $key = ! empty($keyColumn) ? $row[$keyColumn] : $i++;
            $array[$key] = $row;
        }
            
        return $array;
    }
    

    /**
     * create multi-dimensional array based on a connect by hierarchy
     * underlying rows are inserted at the end of the row as a sub array
     *
     * @param string $keyColumn
     * @param string $parentColumn
     */
    public function connectBy($keyColumn, $parentColumn)
    {
        $this->keyColumn = $keyColumn;
        $this->parentColumn = $parentColumn;
        
        $result = array();
        foreach ($this->fetchAll($keyColumn) as $row)
        {
            if (empty($row[$parentColumn]))
                $result[] = $row;
            else
                $result = $this->insertChild($result, $row);
        }
        
        return $result;
    }
    
    /**
     * recursive help function for connectBy to insert the given row in the right place in the array
     * @param array $array
     * @param array $row
     */
    protected function insertChild($array, $row)
    {
        $loopable = array_filter($array, function($val) { return is_array($val); });
        if (count($loopable) > 0)
        {
            foreach ($loopable as $i => $nrow)
            {
                if ($nrow[$this->keyColumn] == $row[$this->parentColumn])
                {
                    $array[$i][] = $row; // plaats de row ertussen als een level diepere array
                    break;
                }
                // doorloop eventuele dieper liggende arrays
                $array[$i] = $this->insertChild($nrow, $row);
            }
        }
        
        return $array;
    }

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Ik zou hiervoor je favoriete ORM gebruiken (bijv. Entity Framework) en de object in laten laden door de ORM of desnoods iets met recurive sql queries doen als het niet performed..

Die extra tabel lijkt me niet zo logisch, tenzij kinderen meerdere ouders hebben of je cirkelrelaties wil toestaan. ;)

Voorbeeld van standaardstructuur:
SO: Tree stucture table and entity framework
MSDN: Recursive Queries Using Common Table Expressions
Of een klassieker: Crisp's blog: Formatting a multi-level menu using only one query

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • Matis
  • Registratie: Januari 2007
  • Laatst online: 21-09 10:43

Matis

Rubber Rocket

Als je ook nog volgorde wilt toepassen, zal ik eens kijken naar een Nested Set (indien je wilt met multiple roots).
Voor doctrine gebruik ik zelf de volgende extensie: https://github.com/blt04/doctrine2-nestedset

If money talks then I'm a mime
If time is money then I'm out of time


Acties:
  • 0 Henk 'm!

  • Carharttguy
  • Registratie: Juli 2010
  • Laatst online: 04-07 23:09
Hartelijk dank voor de informatie, ik ga even onderzoeken.
@Pedorus, ja ik had al door dat er iets niet compleet logisch is, hoe zou jij het doen? In m'n codetabel een parentid meegeven, en geen extra tabel gebruiken?