trigger

worst_case

Benutzer
Beiträge
14
Hallo,

ich habe einen Trigger erstellt.

Code:
CREATE TRIGGER `trigg_create_spsvarname` AFTER
UPDATE ON `datenpunktliste` FOR EACH ROW

UPDATE datenpunktliste SET
spsvarname = spsname + "_" + varname;


Beim ändern der Felder in der Tabelle "datenpunktliste" soll ein Feld in der Tabelle "datenpunktliste" verändert werden.

Problem:
========
Ich erzeuge einen Trigger wenn in der Tabelle "datenpunktliste" etwas verändert wird, schreibe aber in ein Feld in der Tabelle "datenpunktliste"
... trigger wird erzeugt und durch den trigger wir ein neuer trigger erzeugt ... geht nicht.

Wie könnte ich dies anders lösen ?

Wie kann ich das schreiben verhindern, (Meldefenster) falls ein gleicher wert in "spsvarname" schon existiert ?

Gruß
worst_case
 
Werbung:
Versuchs mal so:

Code:
CREATE TRIGGER 'trigg_create_spsvarname'
AFTER  UPDATE ON 'datenpunktliste'
FOR EACH ROW
BEGIN
        SET NEW.spsvarname = CONCAT(NEW.spsname , "_" , NEW.varname);
END


Ist nicht getestet, sollte aber so sein.
 
Es ist waage zu sagen, dass MySQL sowas kann, ich verwende für meine Datenbanken PostgreSQL und bin bisher nicht davon abgewichen.

Ein Trigger braucht prinzipiell eine Funktion, welche bei einem bestimmten Tabellen-bedingten "Event" ausgelöst wird, sprich in der Reihenfolge einen Trigger zu bauen um einen Teil der Datenbank zu automatisieren muss man:

1.: Die Tabelle erstellen, wo der Trigger laufen soll
2.: Den Event wissen, was den Trigger auslösen soll (insert, update, delete)
3.: Eine Trigger-Funktion schreiben, welche ausgelöst wird, immer wenn auf der Tabelle der Event vollzogen wird
4.: Den Trigger zur Tabelle schreiben mit dem Event, wann der Trigger ausgelöst werden soll

Beispiel:
Tabellen erstellen:
Code:
create table a(b int);
create table b(c int);

Der Event wird von mir aus Erfahrungen mal einfach als "before" definiert, damit ich mit dem new Atribute arbeiten kann

Trigger-Funktion erstellen:
Code:
create or replace function trigger() returns trigger as $$ begin insert into b(c) values (new.b); return new; end; $$ language plpgsql;

Trigger erstellen:
Code:
create trigger trigger before insert on a for each row execute procedure trigger();

Test-Durchgang:
Code:
insert into a(b) values (1);
select * from b;

Resultat:
Code:
1

Wurde sogar von mir getestet, das einzige Problem ist, das eine andere, ebenfalls kostenfreie SQL-Variante, eingesetzt wird,
nämlich:
 
CREATE TRIGGER `trigg_create_spsvarname` ..

Wenn das Update im Trigger bedingungslos ausgeführt wird, so wird bei jedem Update des Datensatzes das eine Feld aktualisiert, egal ob die beiden Quellfelder aktualisiert wurden.
Kein guter Stil.

Im übrigen kann man diesen Wert ganz einfach bei einer Abfrage on the fly erstellen, dann ist er
- 100% aktuell
- benötigt 0 Speicherplatz
- verbrennt nicht unnötig DB Ressourcen

Mag sein, dass es erstmal kein Problem ist, weil nur spezifischer Code Zugriff hat, aber so ist es am Anfang oft. Die nächste Änderung und der nächste Clientzugriff bringt dann das Durcheinander.
 
Der Trigger
Code:
CREATE TRIGGER `trigg_create_spsvarname` AFTER
UPDATE ON `datenpunktliste` FOR EACH ROW

UPDATE datenpunktliste SET
spsvarname = spsname + "_" + varname;

Problem:
========
Ich erzeuge einen Trigger wenn in der Tabelle "datenpunktliste" etwas verändert wird, schreibe aber in ein Feld in der Tabelle "datenpunktliste"
... trigger wird erzeugt und durch den trigger wir ein neuer trigger erzeugt ... geht nicht.
Dein Trigger hat mehrere Probleme und sollte generell enger gefasst sein.

1) FOR EACH ROW ist ein ganz netter "Hack" um Code so zu schreiben das man sich nicht mit lästigen Fällen befassen muss wo mehrere Spalten geändert werden. Das ist aber eine eher plumpe Lösung, besser finde ich, wenn der Trigger einmal feuert und alle Datensätze gleichzeitig verarbeitet.
2) Du beschränkst dich nicht auf Datensätze, wo sich spsname oder varname geändert hat sondern auf alle.
3) Du beschränkst dich nicht auf Datensätze, die verändert wurden, sondern machst die ganze Tabelle.
4) Rekursion ist eigentlich nur die Folge aus 1 bis 3.

Ich nutze auch kein MySQL und bin mit der Syntax nicht ganz vertraut aber das sollte besser gehen. NEW ist eine Systemtabelle, die alle Datensätze beinhaltet, die verändert wurden. Ein Join damit und eine WHERE Bedingung sollten alle deine Probleme lösen, probier mal:
Code:
CREATE TRIGGER `trigg_create_spsvarname` AFTER
UPDATE ON `datenpunktliste`

UPDATE datenpunktliste
SET datenpunktliste.spsvarname = CONCAT(NEW.spsname , "_" , NEW.varname)
FROM datenpunktliste
INNER JOIN NEW
ON datenpunktliste.pk = NEW.pk
WHERE datenpunktliste.spsvarname <> CONCAT(NEW.spsname , "_" , NEW.varname);
pk ist dabei deine Primary Key-Spalte.

Ich bin nicht ganz sicher was die Verwendung von NEW angeht. @BerndB schreibt in NEW, ich weiß nicht ob das bei einem AFTER-Trigger geht und dann in der Tabelle ankommt. Ich habe jetzt in die bereits aktuallisierte Tabelle geschrieben.
 
Wenn das Update im Trigger bedingungslos ausgeführt wird, so wird bei jedem Update des Datensatzes das eine Feld aktualisiert, egal ob die beiden Quellfelder aktualisiert wurden.
Kein guter Stil.

Hallo,

wie kann ich dem trigger mitgeben, das er nur ausgelöst wird, wenn Feld1 oder Feld 2 in der gleichen tabelle geändert/verändert werden ??

Gruß
worst_case
 
Hallo,

wie kann ich dem trigger mitgeben, das er nur ausgelöst wird, wenn Feld1 oder Feld 2 in der gleichen tabelle geändert/verändert werden ??

Gruß
worst_case
Code:
postgres=# \h create trigger
Command:     CREATE TRIGGER
Description: define a new trigger
Syntax:
CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
    ON table_name
    [ FROM referenced_table_name ]
    [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
    [ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]
    [ FOR [ EACH ] { ROW | STATEMENT } ]
    [ WHEN ( condition ) ]
    EXECUTE { FUNCTION | PROCEDURE } function_name ( arguments )

where event can be one of:

    INSERT
    UPDATE [ OF column_name [, ... ] ]
    DELETE
    TRUNCATE

URL: https://www.postgresql.org/docs/16/sql-createtrigger.html

postgres=#

Siehe Syntax, mittels WHEN ( condition ) kannst Du faktisch eine Where-Condition mitgeben, die festlegt, für welche Rows der Trigger feuern soll.

Zumindest in PostgreSQL.
 
1) FOR EACH ROW ist ein ganz netter "Hack" um Code so zu schreiben das man sich nicht mit lästigen Fällen befassen muss wo mehrere Spalten geändert werden. Das ist aber eine eher plumpe Lösung, besser finde ich, wenn der Trigger einmal feuert und alle Datensätze gleichzeitig verarbeitet.
Wieso Hack ??
und hat auch absolute gar nicht mit "mehreren Spalten" zu tun. Also wie der Name schon sagt "FOR EACH ROW" bezieht sich dies auf jede Zeile die betroffen ist.

z.B.

Update Tab set Field=VALS WHERE id BETWEEN 100 and 150;

Durch dieses WHERE-Bedingung können ja mehrere Zeilen betroffen sein und dort wir aber nur ein TRIGGER a
ausgelöst.
Dieses Vorgehen ist auch notwendig wenn du noch spezielle Conditionen im Trigger abbilden musst.
 
1) FOR EACH ROW ist ein ganz netter "Hack" um Code so zu schreiben das man sich nicht mit lästigen Fällen befassen muss wo mehrere Spalten geändert werden. Das ist aber eine eher plumpe Lösung, besser finde ich, wenn der Trigger einmal feuert und alle Datensätze gleichzeitig verarbeitet.
Das ist kein Hack, sondern die einzige Möglichkeit. Bei MySQL gibt es ausschließlich row level trigger.
 
Falls du nur die beiden Felder zusammenführen willst und dies auch nur lesend brauchst (also ohne index) kannst
du eine virtuelle spalte definieren.

Code:
```
CREATE TABLE vfields (
  feld1 varchar(32),
  feld2 varchar(32),
  spsvarname varchar(64) AS (CONCAT(feld1,' ',feld2))
);


insert into vfields(feld1,feld2) VALUES ('Peter','pan');




SELECT * FROM vfields;
```




| feld1 | feld2 | spsvarname |
|:------|:------|:-----------|
| Peter | pan | Peter pan |

[fiddle](https://dbfiddle.uk/NCFkcffK)
 
Aber der Trigger feuert dann nicht einmal sondern einmal pro Zeile, richtig? Ich habe nur Trigger in MSSQL, da geht das gar nicht. Der Trigger feuert einmal pro UPDATE / INSERT / DELETE.
Genau, der Trigger feuert dann einmal pro Zeile/Datensatz
SQL Server kann nur "statement level trigger", MySQL kann nur row level trigger.
 
Werbung:
Zurück
Oben