[create table] tab bez duplikatów

0

witam serdecznie,
od kilku dni borykam się z problemem dotyczącym zbudowania tab. która nie przechowuje duplikatów. Chciałabym stworzyć taką tab. która dla konkretnego pracownika

employess_id

pracującego w dziale departments_id

 nie zapisze gdy już pracował w jakimś dniu, który znajduję się w przedziale czasowym który jest już zapisany w bazie danych. Dla wyjaśnienia, np:
N1(pracownik)
           v_begin           v_end
           12.01.2010      18.01.2010
N2(ten sam pracownik , ale chcemy go wprowadzić do bazy)
           v_begin           v_end
           13.01.2010      15.01.2010
           
buduje sprawdzanie (znajduje się w kodzie)
          12.01.2010 < 15.01.2010
          13.01.2010 < 18.01.2010 --tu się 2 porównania zgadzają wiec , przedział  [13.01.2010 ,15.01.2010 ]nie powinien zostać zapisany do bazy danych 

Kod przedstawia się następująco:
```sql
create table time_work_test (
  id NUMBER(4) NOT NULL PRIMARY KEY,
  employess_id NUMBER(4),
  departments_id NUMBER(4),
  v_begin TIMESTAMP,
  v_end  TIMESTAMP,
  t_start TIMESTAMP,
  t_stop TIMESTAMP)
CHECK (NOT EXISTS (SELECT N1.id 
                     FROM time_work_test N1 WHERE 1 <=  (SELECT COUNT(N2.employess_id)
						FROM time_work_test N2
						WHERE N1.employess_id = N2.employess_id
						AND N1.departments_id = N2.departments_id
						AND N1.v_begin <= N2.v_end
						AND N2.v_begin <= N1.v_end)));

zwraca mi następujący komunikat błędu:

ORA-00922: missing or invalid option

Uprzejmie proszę o jakąś wskazówkę....?

0
SET @result = (SELECT COUNT(*) FROM time_work_test WHERE employees_id = /* tutaj ID pracownika */ AND /* tutaj data rozpoczecia pracy */ BETWEEN v_begin AND v_end);
IF @result = 0 THEN /* robimy INSERT wstawiajac do bazy.. */
END IF;

Pisane z palca. Nie wiem z jakiej bazy korzystasz, tutaj podsuwam tylko idee ;) Tzn. sprawdzenie warunkiem IF czy podobny rekord juz istnieje, a jezeli nie - wstawienie go.

0

1-czy w check można w ogóle dawać zapytania? sprawdz robiąc najprostszy warunek np:
CHECK (NOT EXISTS (SELECT N1.id
FROM time_work_test N1 WHERE employess_id = N1.employess_id));

2- po co Ci n1 i n2? z opisu na http://techonthenet.com/oracle/check.php wynika, że do nowej wartości odwołujesz się przez nazwę kolumny u ciebie np przez samo employess_id bez aliasu.
coś takiego:
CHECK (NOT EXISTS (SELECT N1.id
FROM time_work_test N1 WHERE v_begin <= N1.v_end //tu reszta warunków))
3. jak się nie da, to użyj triggera on insert

ps piszę posta, klikam wyślij, a on się nie publikuje, i musze pisać drugi raz-masakra

0

Witam serdecznie,

chciałabym przeprosić ze dopiero teraz odpisuje, ale straciłam dostęp do Internetu :( Jestem teraz w kawiarence.

Najbardziej mi zależy na stworzeniu tabeli od razu blokującej dodawanie, ale warto się tez nauczyć tworząc procedurę, która blokuje dodawanie rekordu.
Próbowałam w następujący sposób:

set serveroutput on
CREATE OR REPLACE PROCEDURE d_time_work (x_id time_work_test.id%TYPE,
							x_employess_id time_work_test.employess_id%TYPE,	
							x_departments_name time_work_test.departments_name%TYPE,
							x_begin time_work_test.v_begin%TYPE,
							x_end time_work_test.v_end%TYPE, 
							x_start time_work_test.t_start%TYPE,
							x_stop time_work_test.t_stop%TYPE ) IS 
err EXCEPTION;
wynik NUMBER(3);
p_employess_id time_work_test.employess_id%TYPE;
p_departments_name time_work_test.departments_name%TYPE;
p_begin time_work_test.v_begin%TYPE;
p_end time_work_test.v_end%TYPE; 
   BEGIN 
	SELECT COUNT(*) INTO wynik FROM time_work_test 
	WHERE x_employess_id = p_employess_id
	AND x_departments_name = p_departments_name
	AND p_begin between p_begin AND p_end;	
	
	IF (x_employess_id=p_employess_id AND x_departments_name=p_departments_name AND wynik = 0 ) THEN    
INSERT INTO time_work_test VALUES(x_id, x_employess_id, x_departments_name, x_begin,x_end,x_start,x_stop);
dbms_output.put_line('dodano ponieważ pracownik nie był w tym czasie w pracy'|| wynik);
	ELSE
		RAISE err;  
  	END IF;
	 EXCEPTION 
     WHEN err THEN 
         dbms_output.put_line('pracownik był w tym terminie'|| wynik);
	 WHEN no_data_found THEN 
	   dbms_output.put_line('nie poprawne dane w rekordzie'|| wynik);
   END;

Jednak źle zbudowałam żel zapytanie select .. W kodzie procedury cały czas zawraca mi 0, nawet jeśli pracownik w tym czasie był w pracy.
Próbowałam korzystać również z INNER JOIN ... ale bez rezultatów.

Jeśli ktoś mam chcę spojrzeć i dać mi jakość wskazówkę jak to poprawić byłabym bardzo wdzięczna ...?

0

literówka o tu:
AND p_begin BETWEEN p_begin AND p_end;
zamien na x_begin

0

Witam,

przepraszam ponownie, ale nadal mam bardzo utrudniony dostęp do internetu <płacze>

Próbowałam na wiele sposobów np:

SELECT COUNT(*) INTO wynik FROM time_work_test 
	WHERE x_employess_id = p_employess_id
	AND x_departments_name = p_departments_name
	AND x_begin between x_begin AND x_end;

SELECT COUNT(*) INTO wynik FROM time_work_test t
	INNER JOIN employess e on x_employess_id = p_employess_id
	INNER JOIN departments d on x_departments_name = p_departments_name
	WHERE x_begin between x_begin AND x_end;

  
	SELECT COUNT(p_id) INTO wynik FROM time_work_test t
	INNER JOIN employess e on x_employess_id = p_employess_id
	INNER JOIN departments d on x_departments_name = p_departments_name
		WHERE x_begin <= p_end
		AND p_begin <= x_end;

	SELECT COUNT(t.employess_id) INTO wynik FROM time_work_test t
	INNER JOIN employess e on x_employess_id = e.id
	INNER JOIN departments d on x_departments_name = d.name
		WHERE x_begin <= p_end
		AND p_begin <= x_end;

SELECT COUNT(*) INTO wynik FROM time_work_test t
	INNER JOIN employess e on x_employess_id = e.id
	INNER JOIN departments d on x_departments_name = d.name
		WHERE x_begin <= p_end
		AND p_begin <= x_end;

Niestety te zmiany nie pomogły, nadal mi dodaje pracownika. Nawet jeśli był w tym czasie w pracy .

Proszę miło, o jakąś wskazówkę i wyrozumienie dla mnie z powodu mojego braku internetu :(

0

Na początek odniosę się do Twoich prób check w create table. Nie da się tego tak wykonać ponieważ:

  1. Nie możesz stosować podzapytań w klauzuli check
  2. Możesz odwoływać się wyłącznie do kolumn z tabeli na której zakładasz check

Wydaje mi się, że ograniczenie należy wykonać na poziomie procedury dodającej dane.
Czy dalej masz z nią problemy?

0

Tworząc create table .. odwołuje się do kolumn z tabeli na której chce utworzyć ograniczenie. Te pola

 employess_id,
 departments_id ,
  v_begin,
  v_end  ,
  t_start ,
  t_stop  

sa przecież w tabeli

time_work_test

, na którą chce założyć ograniczenie:

CHECK (NOT EXISTS (SELECT N1.id
                     FROM time_work_test N1 WHERE 1 <=  (SELECT COUNT(N2.employess_id)
                                                FROM time_work_test N2
                                                WHERE N1.employess_id = N2.employess_id
                                                AND N1.departments_id = N2.departments_id
                                                AND N1.v_begin <= N2.v_end
                                                AND N2.v_begin <= N1.v_end)));

nie chciałabym, aby dany pracownik ,mógł znaleźć się w tej tabeli kilka razy w tym czasie gdy już był w pracy.

Tak masz racje chyba najlepszym sposobem, będzie utworzenie procedury :)

ale nie wiem jak zbudować zapytanie select ... aby nie dodawało pracownika, który ma takie dane np

employess_id    = 1
departments_id  = 2
v_begin         = 12-12-2010
v_end           = 15-12-2010

i teraz np jak bym chciała wstawić takie dane :

employess_id    = 1
departments_id  = 2
v_begin         = 13-12-2010
v_end           = 14-12-2010

to powinien się wyświetlić komunikat o błędzie, że pracownik był w tym czasie w pracy......?

0
  1. ale ograniczenie chcesz założyć tworząc podzapytanie - select - tego nie wolno w Oracle

  2. nie jestem do końca pewien jakie masz wymagania dotyczące warunków nachodzenia na siebie zakresów dat - dalatego przykład na szybko:

create table temp(
	emp_id number
	dep_id number
	start date
	end date
);

jeśli sprawdzamy czy nowy przedział ma część wspólną z istniejącym:

select count(*)
from temp t 
where
	    t.emp_id = v_emp_id
	and t.dep_id = v_dep_id
	and (v_start between t.start and t.end or v_end between t.start and t.end)

jeśli spawdzamy czy nowy przedział zawiera się w już istniejącytym to:

...
	and (v_start between t.start and t.end and v_end between t.start and t.end)
0

DZIĘKUJE SERDECZNIE DZIAŁA :>

zamieszczam kod może komuś się przyda:

CREATE OR REPLACE PROCEDURE d_time_work (x_id time_work_test.id%TYPE,
							x_employess_id time_work_test.employess_id%TYPE,	
							x_departments_name time_work_test.departments_name%TYPE,
							x_begin time_work_test.v_begin%TYPE,
							x_end time_work_test.v_end%TYPE, 
							x_start time_work_test.t_start%TYPE,
							x_stop time_work_test.t_stop%TYPE ) IS 
err EXCEPTION;
wynik NUMBER(3);

   BEGIN 
	SELECT COUNT(*) INTO wynik FROM time_work_test 
WHERE  employess_id = x_employess_id 
AND departments_name = x_departments_name 
		AND (x_begin BETWEEN v_begin AND v_end OR x_end BETWEEN v_begin AND v_end);
			

	IF ( wynik = 0 ) THEN    
INSERT INTO time_work_test VALUES(x_id, x_employess_id, x_departments_name, x_begin,x_end,x_start,x_stop);
dbms_output.put_line('dodano ponieważ pracownik nie był w tym czasie w pracy ' );
	ELSE 
		RAISE err;  
  	END IF;
	 EXCEPTION 
     WHEN err THEN 
         dbms_output.put_line('pracownik był w tym terminie'|| wynik);
	 WHEN no_data_found THEN 
	   dbms_output.put_line('nie poprawne dane w rekordzie'|| wynik);
   END;
/
show err

tabela przedstawia się w następujący sposób:

create table time_work_test (
  id NUMBER(4) NOT NULL PRIMARY KEY,
  employess_id NUMBER(4),
  departments_name VARCHAR2(30),
  v_begin TIMESTAMP,
  v_end  TIMESTAMP,
  t_start TIMESTAMP,
  t_stop TIMESTAMP
  );

ALTER TABLE time_work_test
ADD FOREIGN KEY (employess_id)
REFERENCES employess(id);

ALTER TABLE time_work_test
ADD FOREIGN KEY (departments_name)
REFERENCES departments(name);

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