Define One to Many Relationships with SQL - sql

I'm looking for a way to set up a one to many relationship between 2 tables. The table structures is explained below but I've tried to leave everything off that has nothing to do with the problem.
Table objects has 1 column called uuid.
Table contents has 3 columns called content, object_uuid and timestamp.
The basic idea is to insert a row into objects and get a new uuid from the database. This uuid is then used stored for every row in contents to associate contents with objects.
Now I'm trying to use the database to enforce that:
Each row in contents references a row in objects (a foreign key should do)
No row in objects exists without at least a row in contents
These constraints should be enforced on commit of transactions.
Ordinary triggers can't help probably because when a row in the objects table is written, there can't be a row in contents yet. Postgres does have so called constraint triggers that can be deferred until the end of the transaction. It would be possible to use those but they seem to be some sort of internal construct not intended for everyday use.
Ideas or solutions should be standard SQL (preferred) or work with Postgres (version does not matter). Thanks for any input.

Your main problem is that other than foreign key constraints; no constraint can reference another table.
Your best bet is to denormalize this a little and have a column on object containing the count of contents that reference it. You can create a trigger to keep this up to date.
contents_count INTEGER NOT NULL DEFAULT 0
This won't be as unbreakable unless you put some user security over who can update this column. But if you keep it up to date with a trigger and all you're looking to avoid is accidental corruption, this should be sufficient.
EDIT: As per the comment, CHECK constraints are not deferrable. This solution would raise an error if all the contents are removed even if the intention is to add more in the same transaction.

Maybe what you want to do is normalize a little bit more. You need a third table, that references elements of the other tables. Table objects should have its own uuid and table contents sholud have also its own uuid and no reference to the table objects. The third table should have only the references to the other two tables, but the primary key is the combination of both references.
so for example you have an uuid of the table objects and you want all the contents of that uuid, assuming that the third table has as columns object_uuid and content_uuid, and the table contents has its own serial column named uuid, your query should be like this:
SELECT * FROM thirdtable,contents
WHERE thirdtable.content_uuid = contents.uuid AND thirdtable.object_uuid=34;
Then you can use an on insert trigger on every table
CREATE TRIGGER my_insert_trigger AFTER INSERT OR UPDATE ON contents
FOR EACH ROW EXECUTE PROCEDURE my_check_function();
and then in function my_check_function() delete every row in objects that is not present in the third table. Somebody else answered first while I was answering, if you guys like my solution I could help you to make the my_check_function() function.

Related

Postgres "archive" a row of a table

I have a few tables in postgres that refer to each other. I want to set up a mechanism to "archive" rows in one of my tables. That is, I want to still hold onto the data and be able to read from it, but I don't want to be able to edit that row anymore or edit the foreign keys in other tables to reference this now "archived" row.
Is this something that can be achieved? Essentially, I want the rest of the database to act like this row's primary key is no longer there, the same way that if you try to set an invalid foreign key, postgres will throw an error that that key was not found in the referenced table.
Thanks
EDIT:
I don't want to actually archive any of the data. I say "archive" because I can't think of a better way to describe it. Essentially, I just want to be able to change a bool value in a row of the table and then that signals to postgres to no longer allow any changes to that row or use that rows id as a foreign key in any other tables. The only thing that someone should be able to do would be to change that bool back to true and then interact normally.
Add a flag column, either a bool or an enum jf there are multiple states. Postgres won't check this for you, you have to add a where clause to every applicable query.
This is error prone. You can make it safer by defining a view which already has the where clause. Do all queries on that view. Rename the table to something like "table_all" and let the view use the table's name, then all existing queries will Just Work.

Update records in database automatically

Let me describe my scenario here.
I am having a table with multiple records, where the name is the same, as it's gonna be records for the same person updated on daily basis.
Right now, I am trying to find out the easiest way to update all the names accordingly.
Name is going to be updated (TestName -> RealName)
I want this change to be applied to all the records with the same name, in this case, "TestName"
I can do a single query, but I am trying to find if there's an automatic way to do this correctly.
Been trying using a triggers, but in most cases, I am ending with an infinite loop, as I am trying to update the table, where a trigger is actually bound to, so it's invoking another update and so on.
I don't need an exact solution, just give me some ropes about how it can be achieved, please.
The problem may be simply resolved by using the function pg_trigger_depth() in the trigger, e.g.:
create trigger before_update_on_my_table
before update on my_table
for each row
when (pg_trigger_depth() = 0) -- this prevents recursion
execute procedure before_update_on_my_table();
However, it seems that the table is poorly designed. It should not contain names. Create a table with names (say user_name) and in the old table store a reference to the new one, e.g.:
create table user_name(id serial primary key, name text);
create table my_table(id serial primary key, user_id int references user_name(id));
You can use event triggers in postgresql https://www.postgresql.org/docs/9.3/sql-createeventtrigger.html

Inserting test data which references another table without hard coding the foreign key values

I'm trying to write a SQL query that will insert test data into two tables, one of which references the other.
Tables are created from something like the following:
CREATE TABLE address (
address_id INTEGER IDENTITY PRIMARY KEY,
...[irrelevant columns]
);
CREATE TABLE member (
...[irrelevant columns],
address INTEGER,
FOREIGN KEY(address) REFERENCES address(address_id)
);
I want ids in both tables to auto increment, so that I can easily insert new rows later without having to look into the table for ids.
I need to insert some test data into both tables, about 25 rows in each. Hardcoding ids for the insert causes issues with inserting new rows later, as the automatic values for the id columns try and start with 1 (which is already in the database). So I need to let the ids be automatically generated, but I also need to know which ids are in the database for inserting test data into the member database - I don't believe the autogenerated ones are guaranteed to be consecutive, so can't assume I can safely hardcode those.
This is test data - I don't care which record I link each member row I am inserting to, only that there is an address record in the address table with that id.
My thoughts for how to do this so far include:
Insert addresses individually, returning the id, then use that to insert an individual member (cons: potentially messy, not sure of the syntax, harder to see expected sets of addresses/members in the test data)
Do the member insert with a SELECT address_id FROM address WHERE [some condition that will only give one row] for the address column (cons: also a bit messy, involves a quite long statement for something I don't care about)
Is there a neater way around this problem?
I particularly wonder if there is a way to either:
Let the auto increment controlling functions be aware of manually inserted id values, or
Get the list of inserted ids from the address table into a variable which I can use values from in turn to insert members.
Ideally, I'd like this to work with as many (irritatingly slightly different) database engines as possible, but I need to support at least postgresql and sqlite - ideally in a single query, although I could have two separate ones. (I have separate ones for creating the tables, the sole difference being INTEGER GENEREATED BY DEFAULT AS IDENTITY instead of just IDENTITY.)
https://www.postgresql.org/docs/8.1/static/functions-sequence.html
Sounds like LASTVAL() is what you're looking for. It was also work in the real world to maintain transactional consistency between multiple selects, as it's scoped to your sessions last insert.

SQL Newbie: Use a foreign key for a lookup table being used like an enum() field?

Say I have field Ice_Cream.flavor, with the current choices in lookup table Flavor.flavor.
I use Flavor.flavor like an enum() list, storing the value, not the record ID, in Ice_Cream.flavor. If Flavor.flavor changes, I don't want to update Ice_Cream:flavor. I want it to stay as created.
Should I set up Ice_Cream.Flavor as a foreign key, so I can see the source of the values in my ER diagram, or not?
If you want Ice_Cream.flavor to stay as created even if there is no matching record in Flavor (which is what your question sounds like) then you cannot create a FOREIGN KEY relationship, it will not allow that condition to occur in your database.
Furthermore, if you're storing the actual text Flavor.Flavor string in Ice_Cream.Flavor, there's no particular reason to have a separate RecordID column in Flavor.
IMHO, you do not need a FK here except if you have additional informations about a flavor in the Flavor table beside the name in the column flavor. It is the case because you do not keep an ID, you keep the name AND you want to keep the old value.
I also supposed that you do not want to keep old flavors in the Flavor table or elsewhere except in the Ice_Cream table.
Last but not least, a FK would require that any flavor stored in Ice_Cream.flavor exists in the Flavor table. It is not the case if I understand correctly your question.

Inheritance in database?

Is there any way to use inheritance in database (Specifically in SQL Server 2005)?
Suppose I have few field like CreatedOn, CreatedBy which I want to add on all of my entities. I looking for an alternative way instead of adding these fields to every table.
There is no such thing as inheritance between tables in SQL Server 2005, and as noted by the others, you can get as far as getting help adding the necessary columns to the tables when you create them, but it won't be inheritance as you know it.
Think of it more like a template for your source code files.
As GateKiller mentions, you can create a table containing the shared data and reference it with a foreign key, but you'll either have to have audit hooks, triggers, or do the update manually.
Bottom line: Manual work.
PostgreSQL has this feature. Just add this to the end of your table definition:
INHERITS FROM (tablename[, othertable...])
The child table will have all the columns of its parent, and changes to the parent table will change the child. Also, everything in the child table will come up in queries to the parent table (by default). Unfortunately indices don't cross the parent/child border, which also means you can't make sure that certain columns are unique across both the parent and child.
As far as I know, it's not a feature used very often.
You could create a template in the template pane in Management Studio. And then use that template every time you want to create a new table.
Failing that, you could store the CreatedOn and CreatedBy fields in an Audit trail table referencing the original table and id.
Failing that, do it manually.
You could use a data modeling tool such as ER/Studio or ERWin. Both tools have domain columns where you can define a column template that you can apply to any table. When the domain changes so do the associated columns. ER/Studio also has trigger templates that you can build and apply to any table. This is how we update our LastUpdatedBy and LastUpdatedDate columns without having to build and maintain hundreds of trigger scripts.
If you do create an audit table you would have one row for every row in every table that uses the audit table. That could get messy. In my opinion, you're better off putting the audit columns in every table. You also may want to put a timestamp column in all of your tables. You never know when concurrency becomes a problem. Our DB audit columns that we put in every table are: CreatedDt, LastUpdatedBy, LastUpdatedDt and Timestamp.
Hope this helps.
We have a SProc that adds audit columns to a given table, and (optionally) creates a history table and associated triggers to track changes to a value. Unfortunately, company policy means I can't share, but it really isn't difficult to achieve.
If you are using GUIDs you could create a CreateHistory table with columns GUID, CreatedOn, CreatedBy. For populating the table you would still have to create a trigger for every table or handle it in the application logic.
You do NOT want to use inheritance to do this! When table B, C and D inherits from table A, that means that querying table A will give you records from B, C and D. Now consider...
DELETE FROM a;
Instead of inheritance, use LIKE instead...
CREATE TABLE blah (
blah_id serial PRIMARY KEY
, something text NOT NULL
, LIKE template_table INCLUDING DEFALUTS
);
Ramesh - I would implement this using supertype and subtype relationships in my E-R model. There are a few different physical options you have of implementing the relationships as well.
in O-R mapping, inheritance maps to a parent table where the parent and child tables use the same identifier
for example
create table Object (
Id int NOT NULL --primary key, auto-increment
Name varchar(32)
)
create table SubObject (
Id int NOT NULL --primary key and also foreign key to Object
Description varchar(32)
)
SubObject has a foreign-key relationship to Object. when you create a SubObject row, you must first create an Object row and use the Id in both rows