Reputation: 124
I have two tables with one-to-many relation with such structure:
meeting meeting_person
+--+------------+ +--+----------+-----------+---------+
|id|meeting_type| |id|meeting_id|person_type|person_id|
+--+------------+ +--+----------+-----------+---------+
|1 | 1 | |1 | 1| 1| 100 |
|2 | 2 | |2 | 1| 1| 101 |
+--+------------+ |3 | 1| 2| 102 |
|4 | 2| 3| 103 |
+--+----------+-----------+---------+
What I want to do is check whether the specific record exists before inserting, and if so warn user about it.
The thing is I need to check whether a meeting with such meeting_type
and person_id
with person_type
exists, and that for all persons.
For example, if with given data I wanted to insert meeting with meeting_type = 1 and such meeting_persons:
Upvotes: 0
Views: 2542
Reputation: 94859
You want to know if a meeting with the meeting type and the persons in question already exists. So join and count:
select count(*)
from
(
select m.id
from meeting m
join meeting_persion mp on mp.meeting_id = m.id
where m.meeting_type = 1
and (mp.person_type, mp.person_id) in ((1,100),(1,101),(2,102))
group by m.id
having count(*) = 3
);
This query results in the number of matching meetings (0 or more).
If, however, you are only interested in meetings with exactly those persons, i.e. no additional persons, then you must move the criteria on persons from WHERE
to HAVING
:
select count(*)
from
(
select m.id
from meeting m
join meeting_persion mp on mp.meeting_id = m.id
where m.meeting_type = 1
group by m.id
having count(case when (mp.person_type, mp.person_id) in ((1,100),(1,101),(2,102))
then 1 end) = 3
and count(*) = 3
);
This query results in the number of matching meetings (0 or 1).
Upvotes: 1
Reputation: 3161
How about:
WITH MEETING(ID, MEETING_TYPE) AS
(SELECT 1, 1 FROM DUAL
UNION SELECT 2, 2 FROM DUAL),
MEETING_PERSON(ID, MEETING_ID, PERSON_TYPE, PERSON_ID) AS
(SELECT 1, 1, 1, 100 FROM DUAL
UNION SELECT 2, 1, 1, 101 FROM DUAL
UNION SELECT 3, 1, 2, 102 FROM DUAL
UNION SELECT 4, 2, 3, 103 FROM DUAL)
SELECT CASE WHEN COUNT(*) = 0 THEN 'Yes' ELSE 'No' END AS show_warning FROM meeting, meeting_person
WHERE meeting.id = meeting_person.meeting_id
AND meeting.meeting_type = 1
AND (person_type, person_id) NOT IN ( (1,100),(1,101),(2,102) ); -- comment this and uncomment second line for second example to check
--AND (person_type, person_id) NOT IN ( (1,100) );
Upvotes: 0
Reputation: 1269463
As phrased, you need to use a trigger for this. I might suggest changing the data structure to include meeting_type
in meeting_person
. Yes, I know that violates normal form. But if you had it, you could enforce your logic with a simple constraint:
alter table meeting_person add constraint unq_meetingperson_person_type
unique (person_id, meeting_type);
How can you do this safely? Use foreign key relationships:
alter table meeting add constraint unq_meeting_type_id
unique (type, id);
alter table meeting_person add constraint fk_meetingperson_meetingtype_id
foreign key (meeting_type, id) references meeting(meeting_type, id);
This does incur extra space for the additional (unnecessary) indexes. It does require including one extra column in meeting_type
. But it does allow you to implement this logic without using triggers.
Upvotes: 1