Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Dane historyczne - trigger
Forum PHP.pl > Forum > Bazy danych > MySQL
kmpl
Witajcie,

Mam 3 tabele:
sprzet: id_sprzetu, nazwa, indeks, JIM, ilosc
pomieszczenia: id_pomieszczenia, nr_pomieszczenia, opis
lok_sprzetu: id_l_s, id_sprzet, id_pom, iloscloksprzetu, uwagi

W tabeli sprzet mam informacje ile sprzętu i w jakiej ilości mam na stanie (obciążeniu).
W tabeli lok_sprzetu mam dane jaki sprzęt w jakiej ilości znajduje sie w danym pomieszczeniu.

Chciałbym teraz na stronie głównej mieć info - jaka ostatnia operacja została przeprowadzona w bazie danych tzn. delete, update, insert - czego dotyczyła i kiedy to miało miejsce? Już wiem, że powinna być tabela historyczna i triggery.
Zacząłem coś kombinować:
  1. CREATE TRIGGER `lok_sprzetu_history_update_trigger` BEFORE UPDATE ON `lok_sprzetu`
  2. FOR EACH ROW BEGIN
  3. INSERT INTO `lok_sprzetu_history` (id,id_sprzet,id_pom,iloscloksprz,id_miary,uwagi,change_type,change_date)
  4. VALUES
  5. (OLD.id,OLD.id_sprzet,OLD.id_pom,OLD.iloscloksprz,OLD.id_miary,OLD.uwagi,0,NOW());
  6. END;


Nie wiem czy to dobry trop?
mmmmmmm
Trigger - trop dobry. Tylko to. Reszta do poprawki.
Mając tabelę taką:
`lok_sprzetu_history` (id,id_sprzet,id_pom,iloscloksprz,id_miary,uwagi,change_type,change_date)
w triggerze możesz skopiować cały rekord do niej. i OK. Ale... załóżmy, że zmieniło ci się tylko pole id_sprzet. Kopiujesz cały rekord stary. Teraz aby wykazać jakie zmiany zaszły to musisz tak:
1. Znaleźć w tej tabeli ostatni rekord (w tej strukturze brakuje jakiegoś autonumeru, bo przecież dany rekord może być wiele razy modyfikowany, więc samo id nic nie daje) dla danego id.
2. porównać WSZYSTKIE pola z tego rekodu ze WSZYSTKIMI polami rekordu bieżącego
3. Wtedy masz informację, które pola OSTATNIO były zmienione.
Trochę to skomplikowane, nieprawdaż...
A może takie rozwiązanie:
1. tabela:
`lok_sprzetu_history` (auto_id, id, field, old_value, new_value, change_user, change_date)
auto_id - autoinc
id - id zmienianego rekordu (z tabeli lok_sprzetu)
field - nazwa zmienianego pola
old_value, new_value - poprzednia wartosc i aktualna wartosc
change_user i change_date - zmieniacz i data zmieniacza
2. przykładowe dane w tabeli:
1, 1, id_sprzet, 1, 10, user1, 2013-01-01 12:34:56
2, 1, id_pom, 1, 2, user1, 2013-01-01 12:34:56
3, 1, id_pom, 2, 1, user2, 2013-01-02 22:33:44
3. troche skomplikowany trigger wychodzi, bo mniej więcej taki:
IF OLD.id_sprzet<>NEW.id_sprzet THEN
INSERT INTO lok_sprzetu_history(id, field, old_value, new_value, change_user, change_date)
VALUES (OLD.id, 'id_sprzet', OLD.id_sprzet, NEW.id_sprzet, NEW.change_user, NEW.change_date);
END IF;
IF OLD.id_pom<>NEW.id_pom THEN
INSERT INTO lok_sprzetu_history(id, field, old_value, new_value, change_user, change_date)
VALUES (OLD.id, 'id_pom', OLD.id_pom, NEW.id_pom, NEW.change_user, NEW.change_date);
END IF;
...
Dla każdego interesującego cię pola musisz to napisać. Robisz to raz i masz spokój. Do zmiany struktury. Wtedy ten fragment dopisujesz w triggerze i po sprawie.
W tym pierwszym przypadku ze zmianą struktury wiąże się zmiana struktury obu tabel (historii również). Oczywiście na czas zmiany struktury w obu przypadkach powinieneś triggery wyłączyć (postgreSQL) lub zdropować.

W takim układzie zapisujesz sobie zmiany tylko interesujących cię rekordów (zmiana np. timestampa cię nie obchdzi). W tej pierwszej też, ale też musisz napisać IFy.
Każde rozwiązanie ma swoje zady i walety...
Wadę "mojego" rozwiązania jest to, że pola old_vlue i new_value muszą być na tyle uniwersalne, by pomieściły każde inne pole z tabeli. Czyli TEXT.
I dodatkowo w tabeli lok_sprzetu powinny być pola change_user i change_date modyfikowalne z kodu przy każdej zmianie. Wtedy masz info kto ostatnio zmieniał bez 'zaglądania do historii'.
Rozwiązanie to daje również szybki dostęp do np. informacji kto jakie pola i ile razy zmieniał... Zresztą plusó jest więcej... tylko trzeba tego triggera napisać... Ja mam na to sposób i kilka lat temu napisałem do najważniejszych tabel. rozwiązanie działa do dziś. i wszyscy są zadowoleni...
kmpl
Dziękuję za informacje i metody, które mi bardzo mam nadzieję pomogą.
W mojej ewidencji nie ma użytkowników, wszystkie dane wprowadzam i modyfikuje sam. Więc pola userów odchodzą.
Natomiast kiedy aktualizuję sprzęt w różnych pomieszczeniach to po pewnym czasie nie pamietam czy dokonałem zmian w pomieszczeniu x czy y i co zmieniłem. Dlatego ten raport będzie mi potrzebny plus mozliwośc odtworzenia stanu sprzed jakiejs daty.

Cytat
Ja mam na to sposób i kilka lat temu napisałem do najważniejszych tabel. rozwiązanie działa do dziś. i wszyscy są zadowoleni...


Rozwiązanie powyższe czy coś innego?



Proszę o pomoc. Tabela lok_sprzetu_history wygląda tak:

http://zapodaj.net/94578d47ed893.png.html

Czy coś trzeba zmienic?

Trigger:

  1. IF OLD.id_sprzet<>NEW.id_sprzet THEN
  2. INSERT INTO lok_sprzetu_history(id, FIELD, old_value, new_value, change_date)
  3. VALUES (OLD.id, 'id_sprzet', OLD.id_sprzet, NEW.id_sprzet, NEW.change_date);
  4. END IF;


  1. IF OLD.id_pom<>NEW.id_pom THEN
  2. INSERT INTO lok_sprzetu_history(id, FIELD, old_value, new_value, change_date)
  3. VALUES (OLD.id, 'id_pom', OLD.id_pom, NEW.id_pom, NEW.change_date);
  4. END IF;
mmmmmmm
Takie rozwiązanie u mnie działa. No, może bardzo podobne - nazwy pól są inne, nazwy pól są w grawisach (zwłaszcza że nazwa FIELD jest zarezerwowana) oraz przy porównniu zastosowałem Coalesce (bo nulla nie można porównac przez =), czyli:
  1. IF Coalesce(OLD.id_pom,0)!=Coalesce(NEW.id_pom,0) THEN
  2. INSERT INTO lok_sprzetu_history(`id`, `field`, `old_value`, `new_value`, `change_date`)
  3. VALUES (OLD.id, 'id_pom', OLD.id_pom, NEW.id_pom, NEW.change_date);
  4. END IF;
kmpl
  1. CREATE TRIGGER `lok_sprzetu_history_update_trigger` BEFORE UPDATE ON `lok_sprzetu`
  2. FOR EACH ROW BEGIN
  3. IF Coalesce(OLD.id_pom,0)!=Coalesce(NEW.id_pom,0) THEN
  4. INSERT INTO lok_sprzetu_history(`id`, `field`, `old_value`, `new_value`, `change_date`)
  5. VALUES (OLD.id, 'id_pom', OLD.id_pom, NEW.id_pom, NEW.change_date);
  6. END IF;


Błąd SQL syntax. Motam się ze składnią...
mmmmmmm
DELIMITER //
CREATE TRIGGER...
kmpl
  1. Wygląda na TO, że w twoim zapytaniu SQL jest błąd. W znalezieniu przyczyny problemu może pomóc także - jeśli się pojawi - poniższy opis błędu serwera MySQL.
  2.  
  3. ERROR: Nieznany znak przestankowy @ 11
  4. STR: //
  5. SQL: DELIMITER //CREATE TRIGGER `lok_sprzetu_history_update_trigger` BEFORE UPDATE ON `lok_sprzetu`
  6. FOR EACH ROW BEGIN
  7. IF Coalesce(OLD.id_pom,0)!=Coalesce(NEW.id_pom,0) THEN
  8. INSERT INTO lok_sprzetu_history(`id`, `field`, `old_value`, `new_value`, `change_date`)
  9. VALUES (OLD.id, 'id_pom', OLD.id_pom, NEW.id_pom, NEW.change_date);DELIMITER //CREATE TRIGGER `lok_sprzetu_history_update_trigger` BEFORE UPDATE ON `lok_sprzetu`
  10. FOR EACH ROW BEGIN
  11. IF Coalesce(OLD.id_pom,0)!=Coalesce(NEW.id_pom,0) THEN
  12. INSERT INTO lok_sprzetu_history(`id`, `field`, `old_value`, `new_value`, `change_date`)
  13. VALUES (OLD.id, 'id_pom', OLD.id_pom, NEW.id_pom, NEW.change_date);DELIMITER //CREATE TRIGGER `lok_sprzetu_history_update_trigger` BEFORE UPDATE ON `lok_sprzetu`
  14. FOR EACH ROW BEGIN
  15. IF Coalesce(OLD.id_pom,0)!=Coalesce(NEW.id_pom,0) THEN
  16. INSERT INTO lok_sprzetu_history(`id`, `field`, `old_value`, `new_value`, `change_date`)
  17. VALUES (OLD.id, 'id_pom', OLD.id_pom, NEW.id_pom, NEW.change_date);
  18.  
  19.  
  20. zapytanie SQL:
  21.  
  22. DELIMITER //CREATE TRIGGER `lok_sprzetu_history_update_trigger` BEFORE UPDATE ON `lok_sprzetu` FOR EACH ROW BEGIN IF Coalesce(OLD.id_pom,0)!=Coalesce(NEW.id_pom,0) THEN INSERT INTO lok_sprzetu_history(`id`, `field`, `old_value`, `new_value`, `change_date`) VALUES (OLD.id, 'id_pom', OLD.id_pom, NEW.id_pom, NEW.change_date);
  23.  
  24. MySQL zwrócił komunikat: Dokumentacja
  25. #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER //CREATE TRIGGER `lok_sprzetu_history_update_trigger` BEFORE UPDATE ON' at line 1
mmmmmmm
Brakuje END IF; i END;
Działająca całość (zaremowana):
  1. /*
  2. drop table lok_sprzetu
  3. CREATE TABLE `test`.`lok_sprzetu` (
  4.   `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  5.   `id_pom` INT NOT NULL,
  6.   `id_sprzet` INT NOT NULL,
  7.   `iloscloksprzetu` INT NOT NULL,
  8.   `uwagi` TEXT NULL,
  9.   `change_date` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
  10. );
  11.  
  12.  
  13. CREATE TABLE `test`.`lok_sprzetu_history`
  14. (
  15.   `autoid` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  16.   `id` INT NOT NULL ,
  17.   `field` VARCHAR(250),
  18.   `old_value` TEXT,
  19.   `new_value` TEXT,
  20.   `change_date` DATETIME
  21. );
  22.  
  23.  
  24. delimiter //
  25. CREATE TRIGGER `lok_sprzetu_history_update_trigger` BEFORE UPDATE ON `lok_sprzetu`
  26. FOR EACH ROW BEGIN
  27.  
  28. IF Coalesce(OLD.id_pom,0)!=Coalesce(NEW.id_pom,0) THEN
  29.   INSERT INTO lok_sprzetu_history(`id`, `field`, `old_value`, `new_value`, `change_date`)
  30.   VALUES (OLD.id, 'id_pom', OLD.id_pom, NEW.id_pom, NEW.change_date);
  31. END IF;
  32. IF Coalesce(OLD.id_sprzet,0)!=Coalesce(NEW.id_sprzet,0) THEN
  33.   INSERT INTO lok_sprzetu_history(`id`, `field`, `old_value`, `new_value`, `change_date`)
  34.   VALUES (OLD.id, 'id_sprzet', OLD.id_sprzet, NEW.id_sprzet, NEW.change_date);
  35. END IF;
  36. IF Coalesce(OLD.iloscloksprzetu,0)!=Coalesce(NEW.iloscloksprzetu,0) THEN
  37.   INSERT INTO lok_sprzetu_history(`id`, `field`, `old_value`, `new_value`, `change_date`)
  38.   VALUES (OLD.id, 'iloscloksprzetu', OLD.iloscloksprzetu, NEW.iloscloksprzetu, NEW.change_date);
  39. END IF;
  40.  
  41. END;
  42. */
  43. INSERT INTO lok_sprzetu(id_sprzet, id_pom, iloscloksprzetu) VALUES(1,1,1), (2,2,2), (3,3,3);
  44.  
  45. UPDATE lok_sprzetu SET id_sprzet=id_sprzet+1, id_pom=id_pom-1;
  46.  
  47. SELECT * FROM lok_sprzetu_history;
kmpl
Nie wiem co jest grane, ale nie "wchodzi". Wywala błędy.


phpMyAdmin - 2.10.3
Wersja klienta MySQL: 5.0.51a
mmmmmmm
Może nie masz praw do tworzenia triggerow? Nie wiem, bo komunikatu błędu nie podałeś... Powyższy kod NA PEWNO działa. Sprawdzałem na moim lokalu w MyWorkbench
kmpl
  1. Błąd
  2.  
  3. zapytanie SQL:
  4.  
  5. DROP TABLE lok_sprzetu CREATE TABLE `test`.`lok_sprzetu` (
  6. `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  7. `id_pom` INT NOT NULL ,
  8. `id_sprzet` INT NOT NULL ,
  9. `iloscloksprzetu` INT NOT NULL ,
  10. `uwagi` TEXT NULL ,
  11. `change_date` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
  12. );
  13.  
  14. MySQL zwrócił komunikat: Dokumentacja
  15. #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CREATE TABLE `test`.`lok_sprzetu` (
  16. `id` INT NOT NULL AUTO_INCREMENT PRIMARY ' at line 2
mmmmmmm
Kurde, małpa... normalnie małpa. Przepisałeś bez sensu. Masz choć bazę `test`?
zamiast:
  1. DROP TABLE lok_sprzetu CREATE TABLE `test`.`lok_sprzetu` (
  2. `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  3. `id_pom` INT NOT NULL ,
  4. `id_sprzet` INT NOT NULL ,
  5. `iloscloksprzetu` INT NOT NULL ,
  6. `uwagi` TEXT NULL ,
  7. `change_date` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
  8. );
daj
  1. CREATE TABLE IF NOT EXISTS `lok_sprzetu` (
  2. `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  3. `id_pom` INT NOT NULL ,
  4. `id_sprzet` INT NOT NULL ,
  5. `iloscloksprzetu` INT NOT NULL ,
  6. `uwagi` TEXT NULL ,
  7. `change_date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
  8. );
kmpl
Małpa jak diabli... Tabele są już OK, ale nieszczęsny trigger ciągle:


ERROR: Nieznany znak przestankowy @ 11

Już patrzyłem w manualu i za diabły,,,

Mysql Server 5.6 śmga aż miło! smile.gif Trigger działa. Wielkie, wielkie dzieki!

Kontynuując wątek działającego triggera chciałbym wyświetlićw tabeli info jaki sprzęt został zmieniony, kiedy i z jakiej wartości na jaką.

Zrobiłem nowy widok historia gdzie w tabeli wyświetlam dane:
id, stara wartość, nowa wartość, kiedy zmiana. Teraz mam $query = "SELECT * FROM jos_lok_sprzetu_history";
Wszystko ładnie, tylko jak w zapytaniu uzyskać nazwę sprzętu zmienianego? Na razie mam: 642, 300, 60, 2013-10-28 14:27:00.
Chciałby powiązać id z tabeli jos_lok_sperzetu_history z id_l_s jos_lok_sprzetu i uzyskać nazwę sprzętu.
mmmmmmm
połącz joinem te pola.
Ale musisz uważać. Bo jeśli we field jest, że pole `id` się zmieniło, to kicha... (ale takich sytuacji nie powinno być)
kmpl
No właśnie kombinuję z joinem, ale jakieś błędy i bzdury wychodzą.
mmmmmmm
Pokż dane z obu tabel dla jedngo rekordu. I pokaż co kombinujesz...
kmpl
Przykład dla rekordu o id=642

jos_lok_sprzetu
http://www.fotosik.pl/pokaz_obrazek/a421ca369562013f.html


jos_lok_sprzetu_history
http://www.fotosik.pl/pokaz_obrazek/8cfaebaef7211abd.html



  1. $query = "SELECT id.a, field.a, old_value.a, new_value.a, kiedy_zmiana.a, id_sprzet.b, nazwa.c
  2. FROM jos_lok_sprzetu_history
  3. LEFT JOIN jos_sprzet as sprzet b.id_sprzet=sprzet.nazwa
  4. LEFT JOIN jos_lok_sprzetu as lok a.id=lok.id_sprzet";


Bez sensu te zapytanie.
Motam się jak ryba w sieci.

Nie wiem czy to w ogóle możliwe. Chciałbym zrobić tabelę gdzie byłyby takie informacje.

Ostatnie zmiany w systemie:

Pomieszczenie - 500 sprzęt - komputer x stara wartość 23 - nowa wartość - 20
mmmmmmm
Zaczynasz od rekordó, które na pewno masz. Czyli z tabel: jos_sprzet i jos_lok_sprzetu. A skoo masz, to laczysz JOINem. Czyli:
  1. SELECT * /* nie pisz * - dla uproszczenia dalem */
  2. FROM jos_sprzet AS sprzet
  3. JOIN jos_lok_sprzetu AS lok ON lok.id_sprzet=sprzet.id_sprzet

Jak to sie zgadza, to dodajesz tabele ze zmianami (ale moze tych zmian nie byc, wiec LEFT JOIN)
  1. SELECT sprzet.nazwa, sprzet.id_sprzet, lok.nazwa AS nazwa_lokalu, hist.FIELD, hist.old_value, hist.new_value, hist.kiedy_zmiana
  2. FROM jos_sprzet AS sprzet
  3. JOIN jos_lok_sprzetu AS lok ON lok.id_sprzet=sprzet.id_sprzet
  4. LEFT JOIN jos_lok_sprzetu_history AS hist ON hist.id=sprzet.id_l_s



PS. Sorry, jeśli coś źle pisze, ale pisze po omacku, a na laptopie mi klawiatura szwankuje (lew strona zjada, 'W', 'A', 'E' i 'R') smile.gif Taka sytuacja...
kmpl
Hmmm...
Takie zapytanie nie działa. Może coś źle wytłumaczyłem.

Cała baza to 5 tabel (link na końcu).
W tabeli jos_pomieszczenia przechowuję numery i opisy wszystkich pomieszczeń.
W tabeli jos_sprzetu mam cały sprzęt jaki jest na moim obciążeniu. Np. lampki - 24 szt. Ten cały sprzęt jest gdzieś porozdzielany n aposzczególne pomieszczenia.
Tabela jos_lok_sprzętu przedstawia lokalizację sprzętu w poszczególnych pomieszczeniach.
Tabela jos_miary to rodzaj miary czyli sztuka, m2.
Tabela jos_lok_sprzetu_history - historia zmian.

Model:

http://www.fotosik.pl/pokaz_obrazek/ee2053f453ef5c54.html

mmmmmmm możesz pomóc? snitch.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.