Removing row when only a single specific relationship exists - sql

I have a table person with a uid and an email. I also have a table parent_child where I store parental relationships. It has parent_uid and child_uid, which are both required references to the uid of the person table.
When I delete a person, I want to delete all children as long as:
The child does not have an email set. I'm just checking for an # sign for this due to how I use this field in other places.
The child does not have any other parents.
Right now I'm trying to do this, from a BEFORE DELETE ON person trigger, but I'm feeling like it's not the most efficient way to handle this.
FOR child IN SELECT DISTINCT child_uid FROM parent_child WHERE parent_uid = OLD.uid
LOOP
IF NOT EXISTS (
-- Any parent that is not me.
SELECT 1
FROM parent_child
WHERE child_uid = child AND parent_uid <> OLD.uid
) THEN
DELETE FROM person WHERE uid = child AND email NOT LIKE '%#%'
END IF;
END LOOP;

There is no better way to do this than a trigger.
You would index person(uid), parent(parent_uid) and parent_child(child_uid) for efficiency (one of the latter indexes should be unnecessary, because you'll have a primary key constraint on parent_uid and child_uid).

Related

Oracle SQL - need to flip values, but don't know how

select kasutaja_nimi, eesnimi, perenimi, r_nimetus, seeria_nr, max(paigalduse_aeg) as paigaldus
from kasutaja ka
right join riistvara ri on ka.id = ri.id
right join r_paigaldus r on ka.id = r.kasutaja_id
group by kasutaja_nimi, eesnimi, perenimi, seeria_nr, r_nimetus;
This is the output, but I need those values changed. I have ID primary keys for both - kasutaja and riistvara, but I don't know how to match kasutaja ID 1 with riistvara ID 2 and vice versa.
And the output should be like this:
The R_NIMETUS and SEERIA_NR fields are different on my output what I get with my code.
you can't cross the data because you haven't a logic link between two tables.
You have only one solution, you must change your table structure:
Scenario 1
If you have a parent - child relation, please add a foreign key on child table
Scanerio 2
If you have a n:m relationship, please create a middle table with fks to parent and child table.
So in your query you can use JOIN operations to show correctly your results

How can I SELECT DISTINCT on one (excluded) column, but include other columns in the query? (ORACLE)

So I have data arranged loosely like this:
Table 1 - PEOPLE: person_id (primary key), parent_id, child_id, other_parent_fields, other_child_fields
Table 2 - PARENTS: parent_id (auto incrementing primary key), other_fields
Table 3 - CHILDREN: child_id (auto incrementing primary key), parent_id(foreign key referencing PARENTS) other_fields
I want to be able to query for all of the distinct parents from the PEOPLE table, and insert all of the other_parent_fields into the PARENTS table, throwing out the old parent_id from Table 1, in favor of my auto incrementing parent_id in table 2.
I also want to do the same for children, but maintain the parent-child relationships, only using my own ids from table 2 and table 3.
Essentially, I am trying to change the way that the database is designed. Rather than a whole table for all people, I am creating a PARENTS table and a CHILDREN table, the latter of which refers to PARENTS with a foreign key. The reason I am throwing out the ids from table 1 is because I have no reason to care about them in my new table (i.e. the numbering can start back from one, and additional entries can just auto increment the primary key). However, before discarding these IDs from table 1, I need to capture the parent-child relations that they relay.
Is this even possible? How would one go about doing it?
we can assume, for simplicity that no children have children i.e. someone cant be a parent and a child
I did not fully understand your question but it seems that you first query would be this (SQL Server syntax):
insert into Parents
select other_parent_fields, person_id as legacy_parent_id
from (select distinct person_id, other_parent_fields from PEOPLE where parent_id is null) x
The trick would be to first group on parent_id, other_parent_fields and then discard the parent_id. (A distinct is equal to a group by *). The above query only works if other_parent_fields is a pure function of parent_id. I interpret your question as an attempt to normalize denormalized data, so I guess this is true.
In order to extract the children you can do this:
insert into Children
select other_child_fields, parent_id as legacy_parent_id
from (select distinct person_id, other_child_fields from PEOPLE where parent_id is not null) x
Now your tables contain the distinct parents and children as well as their old IDs. You have to write an update query now that assigns the new parent ids into the children table. Then you drop the legacy fields.

Nested query using while condition - sql

I need to do a nested query on a single table. Each row is potentially the parent or child of another row.
Is it possible to do this with a single select statement? Ive started using this statement but it only goes down one level.
select * from myTable where parent_id in
(select id from myTable where name = 'manager' )
This select however will only go down one level. If a row has multiple children they will be ignored. (In the table each row has an Id field , if a row has a parent then the parents Id value will be in the child's parent_Id field. )
If i could include a while loop in the SQL which would always check to see if the returned Id was a parent or not and if it was check and see if any other row was its child by checking the other rows parent_Id. However i m concerned this would take alot of cycles to eventually find all parent child relationships. Any suggestions? Thanks
using Oracle db
I think you are looking for a hierarchical query like this:
select * from mytable
connect by prior id = parent_id
start with name = 'Manager';
(A "nested table" is something else entirely.)

unique pair in a "friendship" database

I'm posting this question which is somewhat a summary of my other question.
I have two databases:
1) db_users.
2) db_friends.
I stress that they're stored in separate databases on different servers and therefore no foreign keys can be used.
In 'db_friends' I have the table 'tbl_friends' which has the following columns:
- id_user
- id_friend
Now how do I make sure that each pair is unique at this table ('tbl_friends')?
I'd like to enfore that at the table level, and not through a query.
For example these are invalid rows:
1 - 2
2 - 1
I'd like this to be impossible to add.
Additionally - how would I seach for all of the friends of user 713 while he could be mentioned, on some friendship rows, at the second column ('id_friend')?
You're probably not going to be able to do this at the database level -- your application code is going to have to do this. If you make sure that your tbl_friends records always go in with (lowId, highId), then a typical PK/Unique Index will solve the duplicate problem. In fact, I'd go so far to rename the columns in your tbl_friends to (id_low, id_high) just to reinforce this.
Your query to find anything with user 713 would then be something like
SELECT id_low AS friend FROM tbl_friends WHERE (id_high = ?)
UNION ALL
SELECT id_high AS friend FROM tbl_friends WHERE (id_low = ?)
For efficiency, you'd probably want to index it forward and backward -- that is by (id_user, id_friend) and (id_friend, id_user).
If you must do this at a DB level, then a stored procedure to swap arguments to (low,high) before inserting would work.
You'd have to use a trigger to enforce that business rule.
Making the two columns in tbl_friends the primary key (unique constraint failing that) would only ensure there can't be duplicates of the same set: 1, 2 can only appear once but 2, 1 would be valid.
how would I seach for all of the friends of user 713 while he could be mentioned, on some friendship rows, at the second column ('id_friend')?
You could use an IN:
WHERE 713 IN (id_user, id_friend)
..or a UNION:
JOIN (SELECT id_user AS user
FROM TBL_FRIENDS
UNION ALL
SELECT id_friend
FROM TBL_FRIENDS) x ON x.user = u.user
Well, a unique constraint on the pair of columns will get you half way there. I think the easiest way to ensure you don't get the reversed version would be to add a constraint ensuring that id_user < id_friend. You will need to compensate for this ordering at insertion time, but it will get you the database Level constraint you desire without duplicating data or relying on foreign keys.
As for the second question, to find all friends for id=1 you could select id_user, id_friend from tbl_friend where id_user = 1 or id_friend = 1 and then in your client code throw out all the 1's regardless of column.
One way you could do it is to store the two friends on two rows:
CREATE TABLE FriendPairs (
pair_id INT NOT NULL,
friend_id INT NOT NULL,
PRIMARY KEY (pair_id, friend_id)
);
INSERT INTO FriendPairs (pair_id, friend_id)
VALUES (1234, 317), (1234, 713);
See? It doesn't matter which order you insert them, because both friends go in the friend_id column. So you can enforce uniqueness easily.
You can also query easily for friends of 713:
SELECT f2.friend_id
FROM FriendPairs AS f1
JOIN FriendPairs AS f2 ON (f1.pair_id = f2.pair_id)
WHERE f1.friend_id = 713

Oracle Delete Rows Matching On Multiple Values

I want to do something like:
DELETE FROM student WHERE
student.course, student.major IN
(SELECT schedule.course, schedule.major FROM schedule)
However, it seems that you can only use one column with the IN operator. Is that true? Seems like a query like this should be possible.
No, you just need parentheses:
DELETE FROM student WHERE
(student.course, student.major) IN
(SELECT schedule.course, schedule.major FROM schedule)
You could also use the EXISTS clause:
DELETE FROM student WHERE
EXISTS
(
SELECT 1 FROM schedule
WHERE schedule.course=student.course
AND schedule.major=student.major
)
DELETE FROM student WHERE
(student.course, student.major) IN
(SELECT schedule.course, schedule.major FROM schedule)
Put parens around your terms in the where clause. Cheers!
In Oracle, you can do a delete from an in-line view, but it generally needs a foreign key that ensures that a row from the table from which the row is deleted cannot be represented by more than one row in the view.
create table parent (id number primary key);
create table child (id number primary key, parent_id number references parent);
insert into parent values(1);
insert into child values(2,1);
delete from (select * from parent p, child c where c.parent_id = p.id);
Note that if any attributes are null, the row's considered not IN. That is, if courses are equal and both student and schedule major are null, row will not be deleted.
If an attribute, such as major, may be null, and you want null = null to be true, try:
DELETE
FROM student
WHERE (student.course, NVL(student.major,'sOmeStRinG') )
IN (SELECT schedule.course, NVL(schedule.major,'sOmeStRinG') FROM schedule)
The syntax below works in SQLServer but I believe it is a standard sql
but as pointed out in comments this is non standard implementation and is not currently supported in Oracle.
I will leave it for reference
delete s
from
student s
inner join schedule sch
on s.course=sch.course
and s.major = sch.major