Baza danych i transakcje

0

Cześć.

Pracuję nad projektem aplikacji ankiet uczelnianych. Opracowałem schemat bazy danych. Niestety nie mogę sobie poradzić z transakcjami do procedur i funkcji.

Bardzo proszę o pomoc w utworzeniu poziomów izolacji transakcji i informację dlatego tak a nie inaczej. Wszędzie wydaje mi się sensowne jedynie read commited :/

OPIS TABEL:
ankiety_wypelnione - przechuje informacje o tym, kto wypełnił ankietę ( później nie może już jej wyświetlić )
historia - zapisuje informacje o zmianach ( w nazwach ankiety, treści pytania )
pytania - przechowuje pytania przypisane do ankiet
pytania_typy - przechowuje informacje o typach pytań ( taknie, ocena, tekstowa itp; w każdej chwili można dodać nowe typy )
odpowiedzi - tabela odpowiedzi do pytań ( anonimowe, nie można wiedzieć jaki użytkownik udzielił daną odpowiedź )
odpowiedzi_archiwum - tabelka archiwalna wypełniana co określony czas za pomocą eventu
ankiety - przechowuje informacje o utworzonych ankietach ( nazwa, data otwarcia, data zamknięcia, informacja o tym do jakiego przedmiotu ją przypisujemy )
uzytkownicy - tabelka do zalogowania, konta użytkowników ( wspólna dla studentów, pracowników, administratorów itd )
uzytkownicy_typy - typy użytkowników ( np: student, wykładowca, administrator itd; w kazdej chwili można dodać nowe typy )
prowadzacy - tabela przypisuje użytkowników ( wykładowców ) do przedmiotów na danych wydziałach i kierunkach ( prowadzący może prowadzić wiele przedmiotów na różnych wydziałach, przedmioty mogą mieć wielu prowadzących )
studenci_przedmioty - przypisujemy studentów do przedmiotów na danych kierunkach ( po to aby móc wyświetlić im stosowne ankiety )
kierunki_przedmioty - tabelka złączeniowa przypisująca przedmioty do kierunków
przedmioty - tabela przechowuje przedmioty
kierunki - tabela kierunków ( są przypisane do wydziałów )
wydziały - tabela wydziałów

OPIS PROCEDUR I FUNKCJI:
archiwizuj_odpowiedzi - procedura wywoływana raz w roku co event, przenosi stare odpowiedzi do archiwum
dodaj_studenta - procedura dodawania nowego studenta
ile_uprawnionych - funkcja zwracająca liczbę uprawnionych do wypełnienia ankiety
ile_wypełniło - funkcja zwraca liczbę osób, które wypełniło ankietę
odpowiedz - procedura odpowiedzi na dane pytanie
srednia_pytania - funkcja zwraca średnią liczbę oceń dla pytań typu "ocena"
zaktualizuj_pytanie - funkcja umożliwiająca aktualizację treść pytania, jeśli nikt jeszcze nie udzielił na nie odpowiedzi

user image

CREATE PROCEDURE `archiwizuj_odpowiedzi`()
BEGIN
	set session transaction isolation level read committed;

	insert into odpowiedzi_archiwum select * from odpowiedzi where odpowiedzi_data < DATE_SUB(CURDATE(), INTERVAL 1 YEAR);
	delete from odpowiedzi where odpowiedzi_data < DATE_SUB(CURDATE(), INTERVAL 1 YEAR);
	
	commit;
END


CREATE PROCEDURE `dodaj_studenta`(`login` varchar(50),`haslo` varchar(50),`email` varchar(50),`imie` varchar(50),`nazwisko` varchar(80),`typ` varchar(30))
BEGIN
	DECLARE TYPY_ID INTEGER;
	DECLARE USER_COUNT INTEGER;
	set session transaction isolation level read uncommitted;

	SELECT uzytkownicytypy_id INTO TYPY_ID FROM uzytkownicy_typy WHERE uzytkownicytypy_nazwa = typ;
	
	SELECT COUNT(*) INTO USER_COUNT FROM uzytkownicy WHERE uzytkownicy_login = login OR uzytkownicy_email = email;

	IF TYPY_ID > 0 AND USER_COUNT = 0 THEN
		INSERT INTO uzytkownicy(uzytkownicy_id,uzytkownicy_login,uzytkownicy_haslo,uzytkownicy_email,uzytkownicy_imie,uzytkownicy_nazwisko,uzytkownicytypy_id) VALUES(null,login,haslo,email,imie,nazwisko,TYPY_ID);
	END IF;

	commit;
END


CREATE FUNCTION `ile_uprawnionych`(`ankieta` int) RETURNS int(11)
BEGIN
	declare studenci int;
	declare kierunek int;
	declare kierunkiprzedmioty int;
	
	select kierunkiprzedmioty_id into kierunkiprzedmioty from ankiety where ankiety_id = ankieta;
	select count(studenciprzedmioty_id) into studenci from studenci_przedmioty where kierunkiprzedmioty_id = kierunkiprzedmioty;

	RETURN studenci;
END



CREATE FUNCTION `ile_wypelnilo`(`ankieta` int) RETURNS int(11)
BEGIN
	declare liczba int;
	select count(*) into liczba from ankiety_wypelnione where ankiety_id = ankieta;

	RETURN liczba;
END




CREATE PROCEDURE `odpowiedz`(`pytanie` int, `odpowiedz` varchar(500))
BEGIN
	declare ocena int;
	declare typ int;
	declare odp varchar(500);
	set odp = cast(odpowiedz as unsigned);
	set session transaction isolation level read committed;

	select pytaniatypy_id into ocena from pytania_typy where pytaniatypy_nazwa = 'ocena';
	select pytaniatypy_id into typ from pytania where pytania_id = pytanie;
	if pytanie > 0 then
		if typ = ocena then
			if odp > 0 then
				insert into odpowiedzi (odpowiedzi_id,pytania_id,odpowiedzi_tresc,odpowiedzi_data) VALUES(null,pytanie,odpowiedz,NOW());
			end if;
		else
			insert into odpowiedzi (odpowiedzi_id,pytania_id,odpowiedzi_tresc,odpowiedzi_data) VALUES(null,pytanie,odpowiedz,NOW());
		end if;
	end if;

	commit;
END



CREATE FUNCTION `srednia_pytania`(`pytania` int) RETURNS int(11)
BEGIN
	declare liczba int;
	select avg(odpowiedzi.odpowiedzi_tresc) into liczba from odpowiedzi, pytania where pytania.pytaniatypy_id = 2 and odpowiedzi.pytania_id = pytania;

	return liczba; 
END



CREATE PROCEDURE `zaktualizuj_pytanie`(`pytanie` int,`tresc` varchar(255))
BEGIN
	
	declare lo integer;

	set session transaction isolation level read committed;	

	SELECT count(*) INTO lo FROM odpowiedzi WHERE pytania_id = pytanie;
	if lo = 0  then 
		UPDATE pytania SET pytania_tresc = tresc WHERE pytania_id = pytanie;
	end if;

	commit;
END

0

Specem od MySQL nie jestem ale podpowiem Ci co nie co bazując na oracle. Po pierwsze do czego się doczepię to funkcje jak np ile_uprawnionych w zasadzie jeśli potrzebujesz tylko count to możesz zrobić widok w przypadku Oracle (i MySQL pewnie też) przy bardzo dużych zbiorach danych baza ma problem przy przechodzeniu między SQL i PL/SQL i takie funkcje nie są zbyt wydajne proponuję zatem przenieść to na VIEW.
Druga rzecz to widzę, że archiwizujesz ankiety i odpowiedzi ale pytań już nie ... co Ci da odpowiedź w archiwum jeśli nie wiesz jak brzmiało pytanie?
Teraz co do tych Twoich transakcji. Nie wiem jak będzie wyglądał interfejs użytkownika ale jak znam życie to będzie pewnie jakiś formularz HTML. Jeśli tak to w przypadku archiwizuj_odpowiedzi w Oracle użyłbym kursora

for rec in (select * from odpowiedzi where ... for update nowait) loop
  insert i delete
end loop;
commit;

takie rozwiązanie może jest i nieco mniej wydajne ale przynajmniej przyblokujesz rekordy przeznaczone do usunięcia i zachowasz pewność, że nikt ich w międzyczasie nie zmieni.
Co do dodaj_studenta to tutaj możesz zostawić tak jak teraz jest lub nawet użyć autonomicznej transakcji (jeśli masz taką potrzebę) ale wątpię.
Problem pojawia się w odpowiedz bo jak rozumiem pytań będzie X, dla każdego pytania ma być odpowiedź i to pewnie jeszcze z możliwością cofnij, popraw itp. w takim wypadku jak bym to przerobił w taki sposób aby po stronie interfejsu użytkownika przechowywać całość odpowiedzi i dopiero w momencie zatwierdzenia ankiety przesłać całą paczkę danych w jednej transakcji. Ma to swoje + i - (za długo by tu pisać) ale uważam, że w Twoim przypadku będzie to najbezpieczniejsze rozwiązanie.

0

Dzięki za odpowiedź. Jak dobrze zauważyłeś - brak dwóch tabel archiwizujących oraz zbędne funkcje.
System ten jest pisany póki co na zaliczenie przedmiotu i prowadzący zgodził się aby na potrzeby zaliczenia zrobić tylko jedną tabelę ( żeby pokazać że umie się eventy, triggery ). Podobnie z funkcjami - są tutaj tylko na pokaz.

W przyszłym semestrze będę pisał do tego aplikację i na pewno te funkcje i procedury pozmieniam.

Co do tego co napisałeś - niestety nie rozumiem odniesienia do MySQL.
Czy ktoś jeszcze potrafi pomóc ?

1 użytkowników online, w tym zalogowanych: 0, gości: 1