Deleting a record in parent table and updating "DELETED" column in child table - plsql

I have a parent table(profiles) in which profile_id is the primary key and is a foreign key to 3 different child tables. (s_profile, p_profile, c_profile)
Now, i want to delete a record from the table profile and want to update a "DELETED" column in the child tables with sysdate.
However my script doesn't allow it saying "Foreign key violated- Child record found".
Is there a solution to it?

What is the purpose of that foreign key constraint, if you want to allow deleting master while details exist?
Anyway, here's an example which shows what you might do; is it the right way, I can't tell (I suspect not - once again, it is cancels the purpose of the referential constraint).
Create two tables - master and its detail:
SQL> create table profiles
2 (id_profile number primary key);
Table created.
SQL> create table s_profile
2 (id number primary key,
3 id_profile number constraint fk_s_pro references profiles (id_profile),
4 deleted date);
Table created.
SQL>
Sample data and attempt to delete a master while detail exists:
SQL> insert into profiles values (1);
1 row created.
SQL> insert into s_profile (id, id_profile) values (100, 1);
1 row created.
SQL> delete from profiles where id_profile = 1;
delete from profiles where id_profile = 1
*
ERROR at line 1:
ORA-02292: integrity constraint (SCOTT.FK_S_PRO) violated - child record found
SQL>
Create a trigger on the master table which removes foreign key value and sets the date:
SQL> create or replace trigger trg_bd_prof
2 before delete on profiles
3 for each row
4 begin
5 update s_profile s set
6 s.id_profile = null,
7 s.deleted = sysdate
8 where s.id_profile = :old.id_profile;
9 end;
10 /
Trigger created.
Let's try to delete master again:
SQL> delete from profiles where id_profile = 1;
1 row deleted.
SQL> select * From s_profile;
ID ID_PROFILE DELETED
---------- ---------- ----------
100 2018-10-03
SQL>
If you want to save foreign key value, you could alter detail table and add another column, say deleted_id_profile and populate it with the same trigger. Though, what would you do with it, if parent doesn't exist any more and you can't find any info about it?

Related

CHECK Constraint based on a column value IN OTHER Table

SqlServer
Suppose I have 2 tables:
Table 1 - having column A
Table 2 - having column B [Bit] Not Null
Is it possible to have a Check Constraint, such that value of Column B can be "0", only when Column A is NOT NULL.
OR put it other way, value of Column B can be "1", only when Column A is NULL.
Thanks in advance.
Assuming that these tables are already related by a suitable foreign key, we can implement this check using a computed column and a new foreign key.
Make sure you read to the end
So if we have:
CREATE TABLE Table1 (
Table1ID char(5) not null,
ColumnA int null,
constraint PK_Table1 PRIMARY KEY (Table1ID)
)
CREATE TABLE Table2 (
Table2ID char(7) not null,
Table1ID char(5) not null,
ColumnB bit not null,
constraint PK_Table2 PRIMARY KEY (Table2ID),
constraint FK_Table2_Table1 FOREIGN KEY (Table1ID) references Table1 (Table1ID)
)
We can run this script:
alter table Table1 add
ColumnBPrime as CAST(CASE WHEN ColumnA is NULL THEN 1 ELSE 0 END as bit) PERSISTED
go
alter table Table1 add constraint UQ_Table1_WithColumnBPrime UNIQUE (Table1ID, ColumnBPrime)
go
alter table Table2 add constraint FK_Table2_Table1_CheckColumnB FOREIGN KEY (Table1ID, ColumnB) references Table1 (Table1ID,ColumnBPrime)
Hopefully you can see how this enforces the relationship between the two tables1.
However, there's an issue. In T-SQL, any DML statement may only make changes to one table. So there's no way to issue an update that both changes whether ColumnA is null or not and changes Column B to suit it.
This is another good reason not to have Column B in the database at all - it's derived information, and in our quest to ensure it always matches its definition, we'd have to always delete from Table 2, update Table 1 and re-insert in Table 2.
1It's now a matter of personal taste whether you remove the previous foreign key or leave it in place as the "real" one.

Output of the SQLite's foreign_key_list pragma

Using SQLite3 with the following schema:
CREATE TABLE Customers(ID INTEGER PRIMARY KEY, Company TEXT NOT NULL UNIQUE, Country TEXT NOT NULL, City TEXT NOT NULL);
CREATE TABLE Orders(ID INTEGER PRIMARY KEY, CustomerID INTEGER NOT NULL, FOREIGN KEY(CustomerID) REFERENCES Customers(ID) ON DELETE RESTRICT ON UPDATE RESTRICT);
and issuing this command:
PRAGMA foreign_key_list(Orders);
results in the following output:
0|0|Customers|CustomerID|ID|RESTRICT|RESTRICT|NONE
As the documentation says nothing about the meaning of the output of this pragma, apart from the obvious (Customers - Parent table, CustomerID - Child key, ID - Parent key, RESTRICT - ON DELETE and the second RESTRICT - ON UPDATE) I presume that NONE coresponds to the unsupported MATCH clause.
The thing which I can't figure out by myself is the meaning of the first two zeros. Could someone tell me what it is?
The output of PRAGMA foreign_key_list() consists of following columns in order -
id, seq, table, from, to, on_update, on_delete, match
So, in the output you got the first two 0s are for id and seq.
Take below example executed in sqlite3 cli with header and column option on -
CREATE TABLE Test (first INTEGER, second INTEGER, FOREIGN KEY (first) REFERENCES A(a) ON DELETE CASCADE, FOREIGN KEY (second) REFERENCES B(x) ON DELETE CASCADE);
sqlite>
sqlite> PRAGMA foreign_key_list(Test);
id seq table from to on_update on_delete match
---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
0 0 B second x NO ACTION CASCADE NONE
1 0 A first a NO ACTION CASCADE NONE
I know the table Test look like a nightmare, but just ignore the horrible schema for a moment.
You can see there are two foreign keys in table Test and so there are two entries shown in PRAGMA foreign_key_list() output. You can see the id fields value are 0 and 1 respectively but the seq values are all 0.
As you may know sqlite allows multiple column names in foreign key statement.
So if you take the next example -
sqlite> CREATE TABLE Test2 (first INTEGER, second INTEGER, FOREIGN KEY (first, second) REFERENCES A(a, b) ON DELETE CASCADE);
sqlite>
sqlite> PRAGMA foreign_key_list(Test2);
id seq table from to on_update on_delete match
---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
0 0 A first a NO ACTION CASCADE NONE
0 1 A second b NO ACTION CASCADE NONE
So, the multiple column key results in multiple rows in the output where id is same as this is actually one key. But the seq value differs as there are multiple columns in the key.

Insert/update trigger updating column value of all rows

I am running into a logical problem.My Trigger is:
create trigger Points1
on Posts
after insert, update
As
declare #value int
declare #postedby int
select #value= Count(Message) from Posts
select #postedby = PostedBy from Posts
update AspNetUsers set User_points = #value * 3
where ( AspNetUsers.Id = #postedby)
I dont know whether i am doing it right or not.
Two tables: AspNetUsers table with User_points column and Id Column as primary key
Posts table with PostId as primary key and PostedBy as foreign key referencing the AspNetUsers table.
Now, i want to compare PostedBy with Id column and if they both are same then update the User_Points column with +3 on every single message he posted.
Now, problem is:
1> It is inserting same number of points in every Row.It should check only currently inserted row and the PostedBy column of that row and then compare with Id column of other table and should Update user's Point of only that Id.
But same result nothing happens
Please tell me how to do it.
thanks in advance
change
select #postedby = PostedBy from Posts
to
select #postedby = PostedBy from INSERTED
'INSERTED' is a magic table that keep insert/updated data in this scope.
Same as this 'DELETED' table keep previous data in update a row

Can multitable Insert statement combine columns from view and columns from another view?

I'm planning to do SQL expert examination.
I have doubts that answer D is correct:
Evaluate the following command:
CREATE TABLE employees
( employee_id NUMBER(2) PRIMARY KEY
, last_name VARCHAR2(25) NOT NULL
, department_id NUMBER(2)NOT NULL
, job_id VARCHAR2(8)
, salary NUMBER(10,2));
You issue
the following command to create a view that displays the IDs and last
names of the sales staff in the organization:
CREATE OR REPLACE VIEW sales_staff_vu AS
SELECT employee_id, last_name,job_id
FROM employees
WHERE job_id LIKE 'SA_%'
WITH CHECK OPTION;
Which two statements are true regarding the above view? (Choose two.)
A. It allows you to insert rows into the EMPLOYEES table .
B. It allows you to delete details of the existing sales staff from
the EMPLOYEES table.
C. It allows you to update job IDs of the existing sales staff to any
other job ID in the EMPLOYEES table.
D. It allows you to insert IDs, last names, and job IDs of the sales
staff from the view if it is used in multitable INSERT statements.
Source
A is FALSE as the view doesn't allow inserting into the department_id column which is mandatory.
B is TRUE, although it would be more accurate to say that the view only allows deletions of employees where the job_id matches the predicate LIKE 'SA_%'.
C is FALSE, as the WITH CHECK OPTION means that you can't change the job_id if the new job_id doesn't match the view's predicate.
D is FALSE: a multitable insert statement can't just insert some columns into the view and the remaining columns into the employees table. Even if you join the view to the table, the insert must still insert into the base table, not into the view:
insert into
(select e.employee_id, e.last_name, e.department_id, e.job_id
from sales_staff_vu v
join employees e
on v.employee_id = e.employee_id)
values
(1, 'KEMP', 2, 'SA_X');
I suspect this is a test of your ability to verify and ignore wrong information on the internet - i.e. 99% of sites say D is true!
Now, my answer can be easily disproved by crafting a multitable insert statement that successfully inserts via the view.
According to my knowledge answer D is wrong.Reasons are
1.If A is wrong, definitely D is wrong.because department_id column is mandatory field on the table, but it is not mentioned in view.so we can't insert a row using this view.
2.In Answer D,multitable INSERT statements are INSERT ALL, INSERT FIRST,etc.
to check this Answer,i have tried these steps
CREATE TABLE employees123
( employee_id NUMBER(2) PRIMARY KEY
, last_name VARCHAR2(25) NOT NULL
, department_id NUMBER(2)NOT NULL
, job_id VARCHAR2(8)
, salary NUMBER(10,2));
CREATE OR REPLACE VIEW sales_staff_vu123 AS
SELECT employee_id, last_name,job_id,department_id
FROM employees123
WHERE job_id LIKE 'SA_%'
WITH CHECK OPTION;
--department_id is added to view
--here i am trying to insert my employees table rows to employees123 table
INSERT ALL
INTO sales_staff_vu123 --using View
SELECT employee_id, last_name,job_id,department_id
FROM employees;
Error at Command Line:153 Column:15
Error report:
SQL Error: ORA-01702: a view is not appropriate here
01702. 00000 - "a view is not appropriate here"
*Cause:
*Action:
So my decision is we cant use views with multitable insert statements.

SQLITE fill value with unique random table

I want to create a table with a field that is unique and limited to a certain value. Lets say that the limit is 100, the table is full, I remove a random row, and when I create a new row it has the value that was freed before.
It doesn't need to be the fastest thing in the world (the limit is quite small), I just want to implement it in a DB.
Any ideas?
Create one more column in main table, say deleted (integer, 0 or 1). When you need to delete with certain id, do not really delete it, but simply update deleted to 1:
UPDATE mytable SET deleted=1 WHERE id = <id_to_delete>
When you need to insert, find id to be reused:
SELECT id FROM mytable WHERE deleted LIMIT 1
If this query returns empty result, then use INSERT to create new id. Otherwise, simply update your row:
UPDATE mytable SET deleted=0, name='blah', ... WHERE id=<id_to_reuse>
All queries reading from your main table should have WHERE constraint with NOT deleted condition:
SELECT * FROM mytable WHERE NOT deleted
If you add index on deleted, this method should work fast even for large number of rows.
This solution does everything in a trigger, so you can just use a normal INSERT.
For the table itself, we use an autoincrementing ID column:
CREATE TABLE MyTable(ID INTEGER PRIMARY KEY, Name);
We need another table to store an ID temporarily:
CREATE TABLE moriturus(ID INTEGER PRIMARY KEY);
And the trigger:
CREATE TRIGGER MyTable_DeleteAndReorder
AFTER INSERT ON MyTable
FOR EACH ROW
WHEN (SELECT COUNT(*) FROM MyTable) > 100
BEGIN
-- first, select a random record to be deleted, and save its ID
DELETE FROM moriturus;
INSERT INTO moriturus
SELECT ID FROM MyTable
WHERE ID <> NEW.ID
ORDER BY random()
LIMIT 1;
-- then actually delete it
DELETE FROM MyTable
WHERE ID = (SELECT ID
FROM moriturus);
-- then change the just inserted record to have that ID
UPDATE MyTable
SET ID = (SELECT ID
FROM moriturus)
WHERE ID = NEW.ID;
END;

Resources