Anzeige:
Ergebnis 1 bis 13 von 13

Thema: SQL bedingte Verzweigung

  1. #1
    Registrierter Benutzer Avatar von Molaf
    Registriert seit
    15.11.2004
    Beiträge
    127

    SQL bedingte Verzweigung

    ich möchte gerne eine bedingte Verzweigung erzeugen, direkt in einer SQL-Anweisung.

    Es handelt sich um eine Art Counter, die einen Zähler bei jedem Aufruf hochzählt:
    Code:
    UPDATE zeiten SET zaehler = (zeiten.zaehler+1) where zp=$zp;
    $zp ist ein variabler Zeitpunkt, den ich durch ein Script einbringe. Wenn $zp aber einen neuen Wert enthält, für den es noch keinen Eintrag in der DB gibt, dann müsste eigentlich ein INSERT gemacht werden.

    Derzeit rufe ich die UPDATE-Zeile auf, anschliessend die INSERT-Zeile, eine von beiden hat dann Erfolg.
    Dummerweise verbraucht das unnötig Resourcen und die Fehlerlogs häufen sich so oder so bei jedem Durchlauf mit einer Fehlerzeile.

    Ich habe schon mehrere Konstellationen von IF und EXISTS durchprobiert, die richtige Syntax für einen einzigen, bedingten Aufruf will mir nicht gelingen. Auch die diversen Tutorials im Web haben keine ähnliche Beispielzeile hervorgebracht, wie mir scheint.

    Für Klärung wäre ich dankbar.

    mfg
    Molaf

  2. #2
    Registrierter Benutzer Avatar von Waxolunist
    Registriert seit
    19.06.2006
    Ort
    Wien
    Beiträge
    485
    Das geht nicht bei SQL. Diese Verzweigung musst du schon beim Aufruf machen. Das ist nicht vermeidbar.

    Immer zuerst abfragen ob ein Eintrag bereits existiert mit Select, wenn ja, dann update, ansonsten insert. Anders ist das nicht möglich.

    mfg, christian
    Spezialitäten heute: PLSQL, TSQL, Java (alles mit Webanwendungen), Groovy, Grails, ASP.NET, Javascript, Python, Django
    Straight through, ohne Umwege ans Ziel

  3. #3
    Registrierter Benutzer Avatar von Molaf
    Registriert seit
    15.11.2004
    Beiträge
    127
    Schade, ich dachte, man könnte die Abfrage durch einen geschickt formulierten Einzeiler übergehen.

    Tja, da kann man nichts machen.

    Danke trotzdem.

  4. #4
    Registrierter Benutzer
    Registriert seit
    26.12.2002
    Ort
    Matrix
    Beiträge
    194
    Zitat Zitat von Molaf Beitrag anzeigen
    Schade, ich dachte, man könnte die Abfrage durch einen geschickt formulierten Einzeiler übergehen.
    es geht, hängt aber vom DBMS ab. Oracle und andere bieten MERGE an:

    merge into zeiten a
    using (select <your_ZP> zp from dual) b on (a.zp=b.zp)
    when matched then update set a.zaehler=a.zaehler+1
    when not matched then insert (zp, zaehler) values(b.zp,0);

    -j

  5. #5
    Registrierter Benutzer Avatar von Molaf
    Registriert seit
    15.11.2004
    Beiträge
    127
    Ne, ist bloss ein ganz gewöhnliches Wald-und-Wiesen-MySQL.

  6. #6
    Registrierter Benutzer
    Registriert seit
    21.06.1999
    Beiträge
    677
    Zitat Zitat von Waxolunist Beitrag anzeigen
    Das geht nicht bei SQL. Diese Verzweigung musst du schon beim Aufruf machen. Das ist nicht vermeidbar.

    Immer zuerst abfragen ob ein Eintrag bereits existiert mit Select, wenn ja, dann update, ansonsten insert. Anders ist das nicht möglich.
    Hm, sollte das nicht prinzipiell mit einer WHERE EXISTS Subquery (Achtung: die muss zur Hauptabfrage korreliert sein!) möglich sein?

  7. #7
    Registrierter Benutzer Avatar von Waxolunist
    Registriert seit
    19.06.2006
    Ort
    Wien
    Beiträge
    485
    Also ich kenne die EXISTS-Anweisung nur als built-in Collection Methode bei Oracle.

    Ansonsten gibt es noch das Schlüsselwort EXISTS in Konjunktion mit einer WHERE-Clause. Jedoch kann man UPDATE und INSERT nicht beeinflusse mit einer WHERE-Clause.

    Bei MySQL 5 gibt es ebenfalls eine Merge-Engine. Aber eine Merge-Tabelle zu erstellen ist meiner Meinung nach viel zu aufwändig, nur um eine If-Abfrage zu umgehen. Dafür ist der Merge-Befehl nicht gedacht. Da spielt auch zumeist die Performance nicht mit. Ebenso bei Oracle ist hier die Performance schleppender als bei einem gewöhnlichen

    Code:
    select 1 into v from table where a=x and rownum=1;
    if v=1 then
       update ....;
    else
       insert ....;
    end if;

    mfg, christian
    Spezialitäten heute: PLSQL, TSQL, Java (alles mit Webanwendungen), Groovy, Grails, ASP.NET, Javascript, Python, Django
    Straight through, ohne Umwege ans Ziel

  8. #8
    Registrierter Benutzer
    Registriert seit
    26.12.2002
    Ort
    Matrix
    Beiträge
    194
    Zitat Zitat von Waxolunist Beitrag anzeigen
    Da spielt auch zumeist die Performance nicht mit. Ebenso bei Oracle ist hier die Performance schleppender als bei einem gewöhnlichen

    Code:
    select 1 into v from table where a=x and rownum=1;
    if v=1 then
       update ....;
    else
       insert ....;
    end if;
    nein, das ist nicht korrekt:

    SQL> merge into zeiten a using (select 1 zp from dual) b on (a.zp=b.zp) when matched then update set a.zaehler=a.zaehler+1 when not matched then insert (zp, zaehler) values(b.zp,0);

    1 row merged.


    Statistics
    ----------------------------------------------------------
    0 recursive calls
    1 db block gets
    3 consistent gets
    0 physical reads
    292 redo size
    668 bytes sent via SQL*Net to client
    704 bytes received via SQL*Net from client
    3 SQL*Net roundtrips to/from client
    1 sorts (memory)
    0 sorts (disk)
    1 rows processed

    SQL> select 1 from zeiten where zp=1;


    Statistics
    ----------------------------------------------------------
    0 recursive calls
    0 db block gets
    4 consistent gets
    0 physical reads
    0 redo size
    404 bytes sent via SQL*Net to client
    396 bytes received via SQL*Net from client
    2 SQL*Net roundtrips to/from client
    0 sorts (memory)
    0 sorts (disk)
    1 rows processed

    SQL> insert into zeiten values(2,0);

    1 row created.


    Statistics
    ----------------------------------------------------------
    0 recursive calls
    1 db block gets
    1 consistent gets
    0 physical reads
    296 redo size
    669 bytes sent via SQL*Net to client
    559 bytes received via SQL*Net from client
    3 SQL*Net roundtrips to/from client
    1 sorts (memory)
    0 sorts (disk)
    1 rows processed

    SQL> update zeiten set zaehler=zaehler+1 where zp=1;

    1 row updated.

    Statistics
    ----------------------------------------------------------
    0 recursive calls
    1 db block gets
    3 consistent gets
    0 physical reads
    304 redo size
    668 bytes sent via SQL*Net to client
    575 bytes received via SQL*Net from client
    3 SQL*Net roundtrips to/from client
    1 sorts (memory)
    0 sorts (disk)
    1 rows processed


    select+insert und select + update benötigen beide mehr resourcen als merge.

    -j

  9. #9
    Registrierter Benutzer Avatar von Waxolunist
    Registriert seit
    19.06.2006
    Ort
    Wien
    Beiträge
    485
    Und an welchen Parametern machst du bitte fest, dass hier mehr Resourcen verbraucht wurden?

    Das nächste ist, dass du nur 1 from dual abfragst, damit aber nicht das Problem bei no_data_found löst. Das habe ich übrigens auch vergessen.

    Mein Lösungsvorschlag wäre daher folgender:

    Code:
    select 1 from zeiten where zp=x;
    update zeiten set zaehler=zaehler+1 where zp=x;
    
    exception
      when no_data_found then
        insert into zeiten values(zaehler+1,x);
    Mit Merge sehe ich hier im Moment keine Lösung, ausser vielleicht mit count.

    mfg, christian
    Geändert von Waxolunist (07-02-2007 um 10:19 Uhr)
    Spezialitäten heute: PLSQL, TSQL, Java (alles mit Webanwendungen), Groovy, Grails, ASP.NET, Javascript, Python, Django
    Straight through, ohne Umwege ans Ziel

  10. #10
    Registrierter Benutzer Avatar von Waxolunist
    Registriert seit
    19.06.2006
    Ort
    Wien
    Beiträge
    485
    Hallo,

    Ich habe es jetzt einmal in einem größeren Rahmen getestet und eine Tabelle mit einem Zeitpunkt und einer Zahl angelegt.
    Dann habe ich die Tabelle mit einer fortlaufenden Zahl befüllt und einem systimestamp. 1000 Einträge.
    Dann habe ich folgendes Skript einmal durchlaufen lassen:

    Code:
    declare
      v_starttime timestamp;
      v_endtime timestamp;
      
      v_duration INTERVAL DAY TO SECOND;
      v_index number(19,0) := 500;
    begin
    	
      select systimestamp into v_starttime from dual;
      
      
      loop
        exit when v_index=1500;
        merge into test_zeiten a using 
          (select count(zahl) za from test_zeiten where zahl=v_index) b on (b.za>0) when matched then update set 
          a.zeit=to_date('01011901', 'ddmmyyyy') when not matched then insert (a.zahl, a.zeit) values (v_index, systimestamp);
        v_index := v_index + 1;
      end loop;
    
      select systimestamp into v_endtime from dual;
      v_duration := v_endtime-v_starttime;
      dbms_output.put_line(v_duration);
      
    end;
    Dauer: +00 00:00:11.668108


    Dann habe ich folgendes Skript durchlaufen lassen:
    Code:
    declare
      v_starttime timestamp;
      v_endtime timestamp;
      
      v_duration INTERVAL DAY TO SECOND;
      v number(1,0);
      v_index number(19,0) := 1000;
    begin
    	
      select systimestamp into v_starttime from dual;
      
      
      loop
        exit when v_index=2000;
        begin
    	select 1 into v from test_zeiten where zahl=v_index;
    	update test_zeiten set zeit=to_date('01011902', 'ddmmyyyy') where zahl=v_index;
    	
    	exception
    	  when no_data_found then
    	     insert into test_zeiten values(v_index,systimestamp);
        end;
        v_index := v_index + 1;
      end loop;
    
      select systimestamp into v_endtime from dual;
      v_duration := v_endtime-v_starttime;
      dbms_output.put_line(v_duration);
      
    end;
    Dauer: +00 00:00:00.182802

    Das spricht wohl eine eindeutige Sprache, es sei denn, du zeigst mir eine reale Lösung mit Merge.

    mfg, Christian
    Spezialitäten heute: PLSQL, TSQL, Java (alles mit Webanwendungen), Groovy, Grails, ASP.NET, Javascript, Python, Django
    Straight through, ohne Umwege ans Ziel

  11. #11
    Registrierter Benutzer
    Registriert seit
    26.12.2002
    Ort
    Matrix
    Beiträge
    194
    Zitat Zitat von Waxolunist Beitrag anzeigen
    Das spricht wohl eine eindeutige Sprache, es sei denn, du zeigst mir eine reale Lösung mit Merge.
    siehe a.sql.txt.

    ergebnis:

    merge
    Elapsed: 00:00:00.30

    ZAEHLER F COUNT(*)
    ---------- - ----------
    0 N 1000
    1 I 1000

    no_merge
    Elapsed: 00:00:00.43

    ZAEHLER F COUNT(*)
    ---------- - ----------
    0 N 1000
    1 I 1000

    sieht ebenfalls eindeutig für mich aus, allerdings für merge.

    -j

  12. #12
    Registrierter Benutzer
    Registriert seit
    26.12.2002
    Ort
    Matrix
    Beiträge
    194
    Zitat Zitat von Waxolunist Beitrag anzeigen
    Und an welchen Parametern machst du bitte fest, dass hier mehr Resourcen verbraucht wurden?
    an den gets und roundtrips.

    Das nächste ist, dass du nur 1 from dual abfragst, damit aber nicht das Problem bei no_data_found löst. Das habe ich übrigens auch vergessen.
    ich frage nicht 1 ab sondern zp.

    -j

  13. #13
    Registrierter Benutzer Avatar von Waxolunist
    Registriert seit
    19.06.2006
    Ort
    Wien
    Beiträge
    485
    Ja, dann hast du wohl recht. Ich habe Merge einfach falsch verwendet.

    mfg, christian
    Spezialitäten heute: PLSQL, TSQL, Java (alles mit Webanwendungen), Groovy, Grails, ASP.NET, Javascript, Python, Django
    Straight through, ohne Umwege ans Ziel

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •