von Xbase nach PostgreSQL

hi,

ich möchte meine Xbase++ “Logik” (Bedingung) nach PostgreSQL übertragen

 x                       y
-------------------------------------------
2949666 1420416 2949666 Erste Ebene A    1
2556452 1420504 2556452 Erste Ebene B    2
2490924 1420592 2949666 Zweite Ebene A   3
2359850 1420680 2949666 Zweite Ebene B   4
2490920 1420768 2490924 Dritte Ebene A   5
6095334 1420856 2490924 Dritte Ebene B   6
2949746 1420944 2490924 Dritte Ebene C   7

es geht um die 1st Spalte “x” und die dritte Spalte “y”
die “Sortierung” soll so wie abgebildet sein wobei die letzte Spalte am Anfang 0 ist

Bedingung : wenn x = y -> Parent der Childs haben könnte

// erstmal "reset"
AEval( aNode, {|x,i| x[A_nIndex] := 0} ,,, .T. )

FOR i := 1 TO iMax
   hTVitem  := aNode[i][A_hTVitem]
// nur bei nicht gefüllten
   IF aNode[i][A_nIndex] = 0  
      aNode[i][A_nIndex] := nIndex++
   ENDIF
// das ein Parent suche nach Childs 
   IF aNode[i][A_hTVitem] = aNode[i][A_nItemRoot]
      IF ::HaveRealChild(hTVitem,@nIndex) > 1
...
METHOD DXE_TreeView:HaveRealChild(hTVItem,nIndex)
...
   DO WHILE .T.
// Parent und alle Childs
      nPosi := ASCAN(aNode,{|x| x[A_nItemRoot] = hTVItem},nStart )
      IF nPosi > 0
// nur bei nicht gefüllten
         IF aNode[nPosi][A_nIndex] = 0
            aNode[nPosi][A_nIndex] := nIndex++
         ENDIF
...

Frage : wie bekomme ich das in eine Query ?

dankbar für jede Hilfe als Newbie.

hi,

ich versuche als Newbie die Frage noch mal anders zu stellen.

ich habe in der Table Parent - Child Beziehungen. Die Parent würde ich mit

SELECT ... WHERE x = y

heraus bekommen. nun “könnte” ich die wie gewohnt, in einer Schleife, die einzelnen Elemente abarbeiten mit

SELECT ... WHERE y = gehört_zu_dem_Parent

hinzu kommt nun noch ein “Zähler”, der inkrementiert wird, und dann per

UPDATE ... SET z = i++

wobei z -> letzte Spalte mit der “Sortierung”
Frage : kann man das “alles” in eine Query bekommen ? wenn “wie” ?

Ja, google mal nach “postgresql recursive query”, das geht seit 8.4.


Andreas

danke … das sieht doch schon mal gut aus.
kurz quer gelesen … aber da bleibt mein Problem mit einem “Zähler” …
“wie” (per Refernz ?) und “wo” könnte ich den “Zähler” übergeben wenn ich mit “einer” Query arbeite ?

Zähler für die Tiefe der Rekursion? Im Start-teil eine Variable als Spalte definieren und auf auf 1 setzen, also with recursive … 1 as level, … und dann im weiteren union all dann select … level +1


Andreas

YUP

DANKE für die Stichwörter “with recursive”, “1 as level”, “union”.
damit werde ich mich mal beschäftigen wie ich das Puzzle zusammen bekomme.

noch eine “theoretische” Situation : wenn alles sauber läuft hätte ich in der “Zähler”-Spalte nur UNIQUE Werte und keine Lücken …
das wäre ja nun typisch für ein Field Type “Serial” welches mit NEXTVAL() “hochzählt”.

Frage :
1.) wie kann ich “alle” im Field “Serial” auf 0 (zurück) setzten ? (ich meine nicht den Zähler selbst mit SetVAL() )
2.) könnte man NEXTVAL() auch in einer solchen rekursiven Query machen ?

Danke für die Hilfe

Update? Oder versteh ich Dich falsch?

2.) könnte man NEXTVAL() auch in einer solchen rekursiven Query machen ?

Danke für die Hilfe

Ja. (ungetestet)

Andreas

ich dachte nur, als Newbie, frag mal lieber weil es Type “serial” ist ob ich “da” dann “so” rumwuseln kann.

und “wie” :wink:

Stichwörter “with recursive”, “1 as level”, “union”.

leider fällt es einem Newbie oft schwer ein Beispiel in ein anderen umzusetzen.
ich habe nun diesen Code im “das offizielle Handbuch” ( ISBM 3-8266-1337-6) gefunden

CREATE FUNCTION ListChildren(text) RETURNS SETOF text AS 'SELECT name FROM nodes WHERE parent = $1' LANGUAGE SQL;

und dachte WOW das ich doch genau dein Problem.

CREATE TABLE nodes(
  name numeric,
  hitem numeric,
  parent numeric,
  caption text,
  sortIndex numeric)

COPY nodes FROM 'D:\ALASKA\PG\DATA.CSV' DELIMITER ','

2949666, 1420416, 2949666, "Erste Ebene A", 0
2556452, 1420504, 2556452, "Erste Ebene B", 0
2490924, 1420592, 2949666, "Zweite Ebene A", 0
2359850, 1420680, 2949666, "Zweite Ebene B", 0
2490920, 1420768, 2490924, "Dritte Ebene A", 0
6095334, 1420856, 2490924, "Dritte Ebene B", 0
2949746, 1420944, 2490924, "Dritte Ebene C" , 0

und dazu dann die Function “umgebaut” auf numerisch statt Text

CREATE FUNCTION ListChildren(numeric) RETURNS SETOF numeric AS 'SELECT name FROM nodes WHERE parent = $1' LANGUAGE SQL;

SELECT listchildreN(parent),name,parent,caption FROM nodes;

leider “verstehe” ich das Resultset nicht … was klappt da nicht ?

Also, um da mal einen Anfang zu setzen:

test=*# select * from recur ;
 id |     name      | vater | mutter
----+---------------+-------+--------
  1 | opa           |       |
  2 | oma           |       |
  3 | kind          |     1 |      2
  4 | frau vom kind |       |
  5 | enkelkind1    |     3 |      4
  6 | enkelkind2    |     3 |      4
  7 | enkelkind3    |     3 |      4
(7 rows)

test=*# with recursive kinder as (select p.*, 1 as level from recur p where name='opa' union all select p.*, c.level+1 from recur p join kinder c on (c.id=p.vater)) select c.name,c.level, coalesce(vater.name,'--unbekannt--') as vater from kinder c left join recur as vater on (c.vater=vater.id);
    name    | level |     vater
------------+-------+---------------
 opa        |     1 | --unbekannt--
 kind       |     2 | opa
 enkelkind1 |     3 | kind
 enkelkind2 |     3 | kind
 enkelkind3 |     3 | kind
(5 rows)

Hilft das?


Andreas

DANKE !!!

uuuuuufff … wie lange benötigt man um auf eine solche Lösung zu kommen ?
jajaja … das Ergebnis sieht so aus wie ich es mir denke … aber der “Level” ist ja schon “sortiert” :wink:

das “Nodes” Beispiel “hätte” schon sehr gut gepasst denn ich möchte tatsächlich eine TreeView / ListView Anzeige damit aufbauen : Kunden → Aufträge → Position → Subposition → noch mehr …

ich benötige also eine “dynamische” Lösung deiner Version “für viele Generationen” :wink:

Bedingung :
1.) ich habe den “opa” == (Root)-“Parent” → “Kunde” → Level 1 … ( und bei den Test Daten x == y )
2.) jetzt muss ich nach den “kind” → Aufträge suchen die zu dem “Kunden” gehören ( y == KundenNummer )
3.) sollte es “Enkelkinder” → Positionen geben → “kind” == “parent” → “+”

ich will in diesem “Level” aber NICHT die “einzelnen Positionen” von “jedem” Auftrag. es reicht mir wenn ich “weiss” wie viele Positionen ( Childs ) es gibt und in “eine” Row z.b. die Auftrags Nummer anzeige mit einem “+” im TreeView.

wenn der User noch mehr Informationen haben will “+” benötige ich den nächste “Level” : Positionen zur der Auftrags Nummer.

deshalb fand ich die Idee im Buch mit der FUNCTION interessant die ich “recursive” verwenden “könnte” … ( wenn ich begreifen würde was bei mir NICHT funktioniert … )

p.s. meine Absicht ist es nur so viele Zeilen zu “lesen” wie auf den Bildschirm “passen”.
die “sortierung” nach “Level” soll mir nur helfen beim richtigen Darstellen der Daten in einer solchen TreeView / ListView Kombination

ich knobel immer noch an dem rekursiven Problem …

ich habe nun versucht so zu erweitern :

CREATE FUNCTION SearchChilds(numeric) RETURNS SETOF nodes AS
$BODY$
DECLARE
r nodes%rowtype;
BEGIN
FOR r IN SELECT * FROM nodes 
WHERE parent = $1 
LOOP

--- wie bekomme ich eine WHERE Bedingung ( auf "was" ?) rein ?
---UPDATE nodes SET __record = nextval('nodes___record_seq');

RETURN NEXT r; -- return current row of SELECT
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql ;

wenn ich nun

SELECT * FROM SearchChilds(2949666);

angebe gibt er mir schön die Childs aus :slight_smile:

in der FOR r ( was ist “r” ? die “ganze” ROW ? ) würde ich nun gerne noch Aktionen starten
… wie das UPDATE aber wie mache ich das “nur” auf die “r ROW” (mir fehlt “was” für ein WHERE) ?

nun müsste ich das Ergebnis nehmen um die Child zu überprüfen ob die nun wieder Eltern sind.
also hab ich mir gedacht warum nicht das ganze gleich in die Function und hab damit erweitert

LOOP
-- NEU
SELECT name,
   -- recursive
   CASE WHEN name = parent THEN ( SELECT * FROM SearchChilds(name) )
        
   END
FROM nodes;
 
RETURN NEXT r; -- return current row of SELECT

und das ergab dann das

– Abfrage ausführen:
SELECT * FROM SearchChilds(2949666) as ggg;
FEHLER: Unteranfrage darf nur eine Spalte zurückgeben
LINE 3: CASE WHEN name = parent THEN ( SELECT * FROM SearchChilds…
^
QUERY: SELECT name,
– recursive
CASE WHEN name = parent THEN ( SELECT * FROM SearchChilds(name) )

END
FROM nodes
CONTEXT: PL/pgSQL function searchchilds(numeric) line 11 at SQL-Anweisung

“so” geht es also (auch) nicht …

bitte, bitte helft einem Newbie … das “Problem” kann doch nicht so schwer zu lösen sein (wenn man weiss wie ) !

könnte es sein das genanntes Buch “alt” ist ?
ich finde das Kapitel[h=1]7.8. WITH Queries (Common Table Expressions)[/h]nicht (in deutsch) :frowning:

sehe gerade :

  1. Auflage 2003

uuuups … 10 Jahre “alt”

WITH RECURSIVE included_parts(name, parent, caption) AS (
SELECT name, parent, caption FROM nodes WHERE parent = 2949666
UNION --ALL
SELECT p.name, p.parent, p.caption
FROM included_parts pr, nodes p
WHERE p.name = pr.parent
)
SELECT name, parent, caption
FROM included_parts;

so gibt er mir auch Parent -> Child zurück aber nicht die “Enkel” …

Frage : wie bekomme ich “Opa” ( = 2949666 ) als Parameter übergeben ?

wenn ich nun das Result Set der Child habe “könnte” ich das ganze ja wieder aufrufen …

Frage : kann man “in” dem Code einen rekursiven Aufruf machen wenn die WHERE Bedingung “name = parent” erfüllt ist ?

und dann noch die Frage warum ich das “ALL” nicht verwenden kann/darf … ?

Vergleich einfach noch mal mit meinem Beispiel. So habe ich in meinem Beispiel im Select nach dem UNION ALL einen JOIN mit der vorher erstellten Tabelle.

Für debugging fehlt noch der Kaffee.

Andreas

hi,

ich tue mein Bestmögliches aber als Newbie ist mir nicht möglich gewesen dein Beispiel auf meine Daten anzuwenden.
ich habe also “deinen” Code genommen und mit “meinen” Field Namen ersetzt und es probiert. ( copy / paste )

opa | 1 | --unbekannt–

bei “Vorgabe” des “opa” funktioniert dein Beispiel ja wunderbar aber es ist in der “Generation” auf “Enkel” begrenzt.aber es “passt” nicht zu meinen Daten wo auch der “opa” einen Vater haben müsste und der “Enkel” noch ur-ur-ur … “Kinder” haben könnten.

bei mir ist “opa” z.b. ein Ordner im Laufwerk “C:” … und ich hab ja viele Ordner im Root.
wie viele "opa"s ich nun brauche hängt von der Anzeige des Bildschirms ab.

bei einem “click” auf “opa” könnte ich eine Funktion nehmen wie diese

CREATE FUNCTION ListChildren(numeric) RETURNS SETOF numeric AS 
'SELECT name FROM nodes WHERE parent = $1' 
   --and sortIndex < 1
   --Aktion Childs z.b. "markieren" Level
   --NextVal('sortIndex_Seq') WHERE ???
LANGUAGE SQL;

ich “müsste” mich doch “recursive” durch die “Kinder” wühlen können ohne Begrenzung wie ich es in Xbase++ mache.

die “einfache” Function reicht mir aber noch nicht sondern ich müsste die “erweitern” (Kommentare) wobei ich mir nicht sicher bin “wo” es statt finden muss. das “erweitern” um einen “Level-Index” benötige ich später zum Bildschirmaufbau des TreeView - ListView Control.


da ich durch dein Beispiel neue Stichwörter hatte bin ich auf die suche nach “mehr” Beispielen gegangen.
z.b. diese Beispiel sah für mich ganz vielversprechend aus.

CREATE TABLE supplyitem(si_id integer PRIMARY KEY, si_parentid integer, si_item varchar(100));

--load up the table (multirow constructor introduced in 8.2)
INSERT INTO supplyitem(si_id,si_parentid, si_item)
VALUES (1, NULL, 'Paper'),
(2,1, 'Recycled'),
(3,2, '20 lb'),
(4,2, '40 lb'),
(5,1, 'Non-Recycled'),
(6,5, '20 lb'),
(7,5, '40 lb'),
(8,5, 'Scraps');

--Recursive query (introduced in 8.4 returns fully qualified name)
WITH RECURSIVE supplytree AS
(SELECT si_id, si_item, si_parentid, CAST(si_item As varchar(1000)) As si_item_fullname
FROM supplyitem
WHERE si_parentid IS NULL
UNION ALL
SELECT si.si_id,si.si_item,
    si.si_parentid,
    CAST(sp.si_item_fullname || '->' || si.si_item As varchar(1000)) As si_item_fullname
FROM supplyitem As si
    INNER JOIN supplytree AS sp
    ON (si.si_parentid = sp.si_id)
)
SELECT si_id, si_item_fullname
FROM supplytree;

aber wenn ich das für meine Daten “umschreibe” habe ich das selbe Problem mit dem “opa” … (1, NULL, ‘Paper’)
daher auch die Frage

Frage : wie bekomme ich “Opa” ( = 2949666 ) als Parameter übergeben ?

vielleicht stelle ich auch die Frage falsch … kann man “WITH RECURSIVE …” auch “in” eine Function schreiben ?
wenn ja wie kann man für ein Result-Set, per “SELECT * FROM listchildreN(”+parent+“)”, die Function “recursive” aufrufen ?

nach meiner Xbase “Logik” würde ich die Elemente eines Result-Set “einzeln”, in einer FOR / NEXT, die Function ListChildren(numeric) abarbeiten lassen um einen kompletten Baum wiederherzustellen.
ich möchte nicht jedes mal, wie beim Explorer, bei “opa” anfangen wenn ich zuletzt in der 7th “Generation” war




das hast du dir auch verdient wenn du es bis hier geschafft hast :wink:

wenn du Zeit hättest dir das Problem noch mal anzusehen … DANKE
hier dazu noch mal meine “lates” Test Daten.

CREATE TABLE nodes(
  name numeric,
  hitem numeric,
  parent numeric,
  caption text,
  __record serial,
CONSTRAINT nodes_pkey PRIMARY KEY (__record) ) ;

COPY nodes FROM 'X:\TEMP\DATA.CSV' DELIMITER ','

2949666, 1420416, 2949666, "Erste Ebene A",-1
2556452, 1420504, 2556452, "Erste Ebene B",-2
2490924, 1420592, 2949666, "Zweite Ebene A",-3
2359850, 1420680, 2949666, "Zweite Ebene B",-4
2490920, 1420768, 2490924, "Dritte Ebene A",-5
6095334, 1420856, 2490924, "Dritte Ebene B",-6
2949746, 1420944, 2490924, "Dritte Ebene C",-7

in “DATA.CSV” habe jetzt “,” (Komma) als Delimiter.
zusätzlich ist das Field “__record” jetzt Type “serial” ( statt numerisch )

für Newbies : ich hab für die “__record” Werte < 0 gewählt weil sonst diese Meldung kommt

FEHLER: doppelter Schlüsselwert verletzt Unique-Constraint »nodes_pkey«
DETAIL: Schlüssel »(__record)=(0)« existiert bereits.
********** Fehler **********
FEHLER: doppelter Schlüsselwert verletzt Unique-Constraint »nodes_pkey«
SQL Status:23505
Detail:Schlüssel »(__record)=(0)« existiert bereits.

damit ist auch ein

SELECT setval('nodes___record_seq', 0 ) FROM nodes;

“so” für ein “reset” nicht möglich.

für jeden Kommentar, Ideen oder Tips dankbar wie ich hinter das Geheimniss von PostgreSQL komme.

Gugg mal hier: select abfrage Kategoriebaum | Datenbank-Forum

ist es das, was Du suchst?


Andreas

diesmal bin ich über “categories_description” gestolpert … ich finde es “nirgends”.

ich “sehe” auch öfter diese Konstruktion

where parent IS NULL union all
bzw.
where parent = 0 union all

die ( 0 ) habe ich nicht in meinem Table …
ich werde wohl mein “vorhandenes lauffähiges” Xbase++ Konzept für PostgreSQL überdenken müssen …

ok erst mal vielen Danke für deine Mühe.