Postgresql: One table dependent on another - database

I have three tables in my Database - Students, Books and Books2student.
Students table has StudentID, First Name, Last Name.
Books table has ISBN no, name, no. of copies available.
I want to create a third table Books2Students which has StudentID, books issued, issue date, due date.
How can I create the third table such that if I insert a record in it the no. of copies in Books table should decrease.
If I delete a Student from student table and that student has taken a book then no. of copies in Books table should increased.

You're looking at this the wrong way. The Books table should only have the total amount of copies, not the amount that you need right now. When you query the actual amount of books available, you subtract the amount which has been lent out. You might find that for performance reasons, you want to persist a computed value in the book table itself, but performance optimizations like this come with their own costs :)
Now, whether you do this or update the column manually, you'll need to make sure this works in a concurrency-safe way (so that you don't lend more books than you have, for example). Which gets you into the "distributed systems are hard" territory :)

You can use triggers. It's too simple.
Refer to tutorials: http://www.postgresqltutorial.com/creating-first-trigger-postgresql/
Ignoring the performance part (which is important), here is an example that you should adapt to your table's columns:
---------Creating function-----------
CREATE OR REPLACE FUNCTION student_to_book(p_ID)
RETURNS trigger AS
$BODY$
BEGIN
IF (TG_OP = 'INSERT') THEN
UPDATE Books2Students BS
SET BS.noCopies= noCopies+1
WHERE BS.BOOKS_ISSUED=p_ID;
RETURN NEW;
ELSIF (TG_OP = 'DELETE') THEN
UPDATE Books2Students BS
SET BS.noCopies= noCopies-1
WHERE BS.BOOKS_ISSUED=p_ID;
RETURN OLD;
END IF;
RETURN NULL;
END;
$BODY$
---------Creating TRIGGER-----------
CREATE TRIGGER student_manage
BEFORE INSERT OR DELETE
ON STUDENT
FOR EACH ROW
EXECUTE PROCEDURE student_to_book(ROW.StudentID);

Related

Insert if no PK violation

I need to create a routine that regularly checks a database and make changes if necessary.
Let me explain my problem with the following example.
A Database stores participants and winners of tournaments.
We got two tables storing the base data (tournaments, people)
Furthermore we got two more tables for the references.
Winner table that store tournamentId and peopleId
Participant table that stores tournamentId and peopleId
Now the script should take every entry from the winner table and insert it into the participant table because every winner is a participant as well.
Insert Into ParticipantTable
Select * From WinnerTable
However some users of the database insert winners in the participant table manually, some don't.
The code above won't work since PK Violations can appear.
How can I tell the statement to make insertion only if the entry isn't in participant table yet?
Thanks in advance, sorry for my messi english and keep in mind that the example above is fictional I am aware that the described data model doesn't fit perfectly for the described usecase.
Merge?
merge ParticipantTable as trg
using WinnerTable as src on src.pk_col1 = trg.pk_col1
and src.pk_col2 = trg.pk_col2
... /* here is PK columns binding */
when not matched then
insert (col1, col2, col3, ...)
values (src.col1, src.col2, src.col3, ...);

Oracle-Should I use temp table or ref cursor

I have the following scenario for which i will have to write a stored procedure:
Header table containing invoice_ID and invoice_line_ID
Address Line table containing invoice_line_id and 'Ship_From' and 'Ship_To' corresponding to each invoice_line_ID in header table.
3.Address header table containing invoice_ID and 'Ship_From' and 'Ship_To' corresponding to each invoice_id in header table.
The cases are such that not always all 'Ship_From' and 'Ship_To' information will be present in the Address Line table. In that case the information needs to be selected from Address Header table.
So i will write a case structure and two joins : 1. That will join Header table and Address Line table
2. That will join Header table and Address Header table.
with the condition to do the second join in case entire information for a particular invoice_line_id is not available in line table.
My question here is where should i store the information? I will use a cursor to perform the above case structure. But should i use a ref cursor or a temp table in this case?
Please note that my customer is not liking the idea of extra database objects in the database so i might have to delete the temp table after i am done displaying. I need help on that as well as to is there any alternative to temp table or whether ref cursor take up extra space on the database or not.
In your case you shouldn't use temporary tables. This sort of tables not differs much from ordinary tables. It is an object, that persists in DB always. If you want to create and drop it every time, you need to solve a number of problems. If two users work with a database in a same time, you need to check, is it already created by another user or not. Or you need to be sure, that every user will create a table with unique name. You need a mechanism to delete a table, that was not deleted properly, for example, when user session was aborted due to network problem. And many other problems. It is not a way to use oracle temporary tables.
UPD
About refcursors.
declare
my_cursor sys_refcursor;
num number;
begin
open my_cursor for select rownum from dual;
loop
fetch my_cursor into num;
exit when my_cursor%notfound;
-- do something else
end loop;
end;
/
This is a simple example of using cursors. As for me, it matches more in your situation than temporary table.

table relationships, SQL 2005

Ok I have a question and it is probably very easy but I can not find the solution.
I have 3 tables plus one main tbl.
tbl_1 - tbl_1Name_id
tbl_2- tbl_2Name_id
tbl_3 - tbl_3Name_id
I want to connect the Name_id fields to the main tbl fields below.
main_tbl
___________
tbl_1Name_id
tbl_2Name_id
tbl_3Name_id
Main tbl has a Unique Key for these fields and in the other table, fields they are normal fields NOT NULL.
What I would like to do is that any time when the record is entered in tbl_1, tbl_2 or tbl_3, the value from the main table shows in that field, or other way.
Also I have the relationship Many to one, one being the main tbl of course.
I have a feeling this should be very simple but can not get it to work.
Take a look at SQL Server triggers. This will allow you to perform an action when a record is inserted into any one of those tables.
If you provide some more information like:
An example of an insert
The resulting change you would like
to see as a result of that insert
I can try and give you some more details.
UPDATE
Based on your new comments I suspect that you are working with a denormalized database schema. Below is how I would suggest you structure your tables in the Employee-Medical visit scenario you discussed:
Employee
--------
EmployeeId
fName
lName
EmployeeMedicalVisit
--------------------
VisitId
EmployeeId
Date
Cost
Some important things:
Note that I am not entering the
employees name into the
EmployeeMedicalVisit table, just the EmployeeId. This
helps to maintain data integrity and
complies with First Normal Form
You should read up on 1st, 2nd and
3rd normal forms. Database
normalization is a very imporant
subject and it will make your life
easier if you can grasp them.
With the above structure, when an employee visited a medical office you would insert a record into EmployeeMedicalVisit. To select all medical visits for an employee you would use the query below:
SELECT e.fName, e.lName
FROM Employee e
INNER JOIN EmployeeMedicalVisit as emv
ON e.EployeeId = emv.EmployeeId
Hope this helps!
Here is a sample trigger that may show you waht you need to have:
Create trigger mytabletrigger ON mytable
For INSERT
AS
INSERT MYOTHERTABLE (MytableId, insertdate)
select mytableid, getdate() from inserted
In a trigger you have two psuedotables available, inserted and deleted. The inserted table constains the data that is being inserted into the table you have the trigger on including any autogenerated id. That is how you get the data to the other table assuming you don't need other data at the same time. YOu can get other data from system stored procuders or joins to other tables but not from a form in the application.
If you do need other data that isn't available in a trigger (such as other values from a form, then you need to write a sttored procedure to insert to one table and return the id value through an output clause or using scope_identity() and then use that data to build the insert for the next table.

T-SQL Insert or update

I have a question regarding performance of SQL Server.
Suppose I have a table persons with the following columns: id, name, surname.
Now, I want to insert a new row in this table. The rule is the following:
If id is not present in the table, then insert the row.
If id is present, then update.
I have two solutions here:
First:
update persons
set id=#p_id, name=#p_name, surname=#p_surname
where id=#p_id
if ##ROWCOUNT = 0
insert into persons(id, name, surname)
values (#p_id, #p_name, #p_surname)
Second:
if exists (select id from persons where id = #p_id)
update persons
set id=#p_id, name=#p_name, surname=#p_surname
where id=#p_id
else
insert into persons(id, name, surname)
values (#p_id, #p_name, #p_surname)
What is a better approach? It seems like in the second choice, to update a row, it has to be searched two times, whereas in the first option - just once. Are there any other solutions to the problem? I am using MS SQL 2000.
Both work fine, but I usually use option 2 (pre-mssql 2008) since it reads a bit more clearly. I wouldn't stress about the performance here either...If it becomes an issue, you can use NOLOCK in the exists clause. Though before you start using NOLOCK everywhere, make sure you've covered all your bases (indexes and big picture architecture stuff). If you know you will be updating every item more than once, then it might pay to consider option 1.
Option 3 is to not use destructive updates. It takes more work, but basically you insert a new row every time the data changes (never update or delete from the table) and have a view that selects all the most recent rows. It's useful if you want the table to contain a history of all its previous states, but it can also be overkill.
Option 1 seems good. However, if you're on SQL Server 2008, you could also use MERGE, which may perform good for such UPSERT tasks.
Note that you may want to use an explicit transaction and the XACT_ABORT option for such tasks, so that the transaction consistency remains in the case of a problem or concurrent change.
I tend to use option 1. If there is record in a table, you save one search. If there isn't, you don't loose anything. Moreover, in the second option you may run into funny locking and deadlocking issues related to locks incompatibility.
There's some more info on my blog:
http://sqlblogcasts.com/blogs/piotr_rodak/archive/2010/01/04/updlock-holdlock-and-deadlocks.aspx
You could just use ##RowCount to see if the update did anything. Something like:
UPDATE MyTable
SET SomeData = 'Some Data' WHERE ID = 1
IF ##ROWCOUNT = 0
BEGIN
INSERT MyTable
SELECT 1, 'Some Data'
END
Aiming to be a little more DRY, I avoid writing out the values list twice.
begin tran
insert into persons (id)
select #p_id from persons
where not exists (select * from persons where id = #p_id)
update persons
set name=#p_name, surname=#p_surname
where id = #p_id
commit
Columns name and surname have to be nullable.
The transaction means no other user will ever see the "blank" record.
Edit: cleanup

How to make tasks double-checked (the way how to store it in the DB)?

I have a DB that stores different types of tasks and more items in different tables.
In many of these tables (that their structure is different) I need a way to do it that the item has to be double-checked, meaning that the item can't be 'saved' (I mean of course it will be saved) before someone else goes in the program and confirms it.
What should be the right way to say which item is confirmed:
Each of these tables should have a column "IsConfirmed", then when that guy wants to confirm all the stuff, the program walks thru all the tables and creates a list of the items that are not checked.
There should be a third table that holds the table name and Id of that row that has to be confirmed.
I hope you have a better idea than the two uglies above.
Is the double-confirmed status something that happens exactly once for an entity? Or can it be rejected and need to go through confirmation again? In the latter case, do you need to keep all of this history? Do you need to keep track of who confirmed each time (e.g. so you don't have the same person performing both confirmations)?
The simple case:
ALTER TABLE dbo.Table ADD ConfirmCount TINYINT NOT NULL DEFAULT 0;
ALTER TABLE dbo.Table ADD Processed BIT NOT NULL DEFAULT 0;
When the first confirmation:
UPDATE dbo.Table SET ConfirmCount = 1 WHERE PK = <PK> AND ConfirmCount = 0;
On second confirmation:
UPDATE dbo.Table SET ConfirmCount = 2 WHERE PK = <PK> AND ConfirmCount = 1;
When rejected:
UPDATE dbo.Table SET ConfirmCount = 0 WHERE PK = <PK>;
Now obviously your background job can only treat rows where Processed = 0 and ConfirmCount = 2. Then when it has processed that row:
UPDATE dbo.Table SET Processed = 1 WHERE PK = <PK>;
If you have a more complex scenario than this, please provide more details, including the goals of the double-confirm process.
Consider adding a new table to hold the records to be confirmed (e.g. TasksToBeConfirmed). Once the records are confirmed, move those records to the permanent table (Tasks).
The disadvantage of adding an "IsConfirmed" column is that virtually every SQL statement that uses the table will have to filter on "IsConfirmed" to prevent getting unconfirmed records. Every time this is missed, a defect is introduced.
In cases where you need confirmed and unconfirmed records, use UNION.
This pattern is a little more work to code and implement, but in my experience, significantly improves performance and reduces defects.

Resources