I use PostgreSQL but am looking for SQL answer as standard as possible.
I have the following table "docs" --
Column | Type | Modifiers
------------+------------------------+--------------------
id | character varying(32) | not null
version | integer | not null default 1
link_id | character varying(32) |
content | character varying(128) |
Indexes:
"docs_pkey" PRIMARY KEY, btree (id, version)
id and link_id are for documents that have linkage relationship between each other, so link_id self references id.
The problem comes with version. Now id is no longer the primary key (won't be unique either) and can't be referenced by by link_id as foreign key --
my_db=# ALTER TABLE docs ADD FOREIGN KEY(link_id) REFERENCES docs (id) ;
ERROR: there is no unique constraint matching given keys for referenced table "docs"
I tried to search for check constraint on something like "if exists" but didn't find anything.
Any tip will be much appreciated.
I usually do like this:
table document (id, common, columns, current_revision)
table revision (id, doc_id, content, version)
which means that document has a one-to-many relation with it's revisions, AND a one-to-one to the current revision.
That way, you can always select a complete document for the current revision with a simple join, and you will only have one unique row in your documents table which you can link parent/child relations in, but still have versioning.
Sticking as close to your model as possible, you can split your table into two, one which has 1 row per 'doc' and one with 1 row per 'version':
You have the following table "versions" --
Column | Type | Modifiers
------------+------------------------+--------------------
id | character varying(32) | not null
version | integer | not null default 1
content | character varying(128) |
Indexes:
"versions_pkey" PRIMARY KEY, btree (id, version)
And the following table "docs" --
Column | Type | Modifiers
------------+------------------------+--------------------
id | character varying(32) | not null
link_id | character varying(32) |
Indexes:
"docs_pkey" PRIMARY KEY, btree (id)
now
my_db=# ALTER TABLE docs ADD FOREIGN KEY(link_id) REFERENCES docs (id) ;
is allowed, and you also want:
my_db=# ALTER TABLE versions ADD FOREIGN KEY(id) REFERENCES docs;
of course there is nothing stoping you getting a 'combined' view similar to your original table:
CREATE VIEW v_docs AS
SELECT id, version, link_id, content from docs join versions using(id);
Depending on if it's what you want, you can simply create a FOREIGN KEY that includes the version field. That's the only way to point to a unique row...
If that doesn't work, you can write a TRIGGER (for all UPDATEs and INSERTs on the table) that makes the check. Note that you will also need a trigger on the docs table, that restricts modifications on that table that would break the key (such as a DELETE or UPDATE on the key value itself).
You cannot do this with a CHECK constraint, because a CHECK constraint cannot access data in another table.
Related
Until now I had a column account_name as the primary key for my database. I'd now like to use a hash of account_name as the primary key instead.
So as an interim measure, I added an account_hash column and gave it the UNIQUE constraint, so that both account_name and account_hash exist together.
I populated account_hash for all database entries, and am now actually using account_hash as the key for the database, and am no longer actively using account_name for anything.
But of course because account_name is the "official" primary key, and must be NOT NULL, for any new entries I have been populating both account_name and account_hash with the same hash.
It's all working fine like this, but now I'd like to clean up the database, to get rid of account_name entirely, and to make account_hash the primary key instead.
What is the best way of doing this? It is a working database that is in use constantly, so any change needs to be at minimum disruption to the users.
Here is the \d+ information relating to the relevant columns:
Column | Type | Modifiers | Storage | Stats target | Description
-------------------------------+------------------------+-----------------------------+----------+--------------+-------------
account_name | character varying(255) | not null | extended | |
account_hash | character varying(256) | | extended | |
Indexes:
"users_pkey" PRIMARY KEY, btree (account_name)
"users_account_hash_256_key" UNIQUE CONSTRAINT, btree (account_hash)
Has OIDs: no
Thanks for any help!
You can drop the current primary key with
ALTER TABLE tablename DROP CONSTRAINT users_pkey;
Make the account_hash required with
ALTER TABLE tablename ALTER account_hash SET NOT NULL;
After that you can add a new primary key with
ALTER TABLE tablename ADD PRIMARY KEY USING INDEX indexname;
You may have to drop the users_account_hash_256_key constraint first to not have a duplicate and depending on how the unique index was created, you may have to create the index again for this.
If the account_name column is not used anywhere, it can then be dropped with
ALTER TABLE tablename DROP COLUMN account_name;
Note I would advise against this action. Hashes have collisions, so if you use them as primary keys, there may be a time when you cannot insert a value into the database because of that. Also performance is worse with varchar indexes than with integers (or a UUID, if a very large keyspace is needed), so if there is no specific reason for using hashes, I wouldn't do this.
I am new in PostgreSQL. I'm trying to figure out the syntax for creating the following table.
I'm having difficulties in creating the sequence and the auto increment fields.
Column | Type | Modifiers
--------------+-----------------------+-----------------------------------------------------
id_numuser | integer | not null default nextval('id_numuser_seq'::regclass)
username | character varying(70) |
completename | character varying(70) |
id_cat | integer |
email | character varying(70) |
password | character varying(30) |
active | boolean |
Indexes:
"users_pkey" PRIMARY KEY, btree (id_numuser)
"taskuser_uniq" UNIQUE, btree (username)
Foreign-key constraints:
"users_id_cat_fkey" FOREIGN KEY (id_cat) REFERENCES usercategories(id_numcat)
Use a serial column. Details here:
Auto increment SQL function
The complete Script:
CREATE TABLE users (
id_numuser serial PRIMARY KEY
,username character varying(70) UNIQUE
,completename character varying(70)
,id_cat integer REFERENCES usercategories(id_numcat)
,email character varying(70)
,password character varying(30)
,active boolean
);
You can use pgAdmin to get complete reverse-engineered SQL scripts for all objects.
Aside: I'd suggest to use just text instead of varchar(n).
If you're ever in doubt about how to define something, pg_dump will help.
pg_dump -t 'users' --schema-only
will print a dump that shows the command(s) to create your users table.
It won't use shorthand like SERIAL, so it'll create the sequence then assign the sequence ownership and set the column default. So sometimes there's a shorter and simpler way than how pg_dump does it. The way pg_dump does it will always work, though.
In this case it produces (trimmed):
CREATE TABLE users (
id_numuser integer NOT NULL,
username character varying(70),
completename character varying(70),
id_cat integer,
email character varying(70),
password character varying(30),
active boolean
);
CREATE SEQUENCE users_id_numuser_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE users_id_numuser_seq OWNED BY users.id_numuser;
ALTER TABLE ONLY users ALTER COLUMN id_numuser
SET DEFAULT nextval('users_id_numuser_seq'::regclass);
ALTER TABLE ONLY users
ADD CONSTRAINT users_pkey PRIMARY KEY (id_numuser);
ALTER TABLE ONLY users
ADD CONSTRAINT users_username_key UNIQUE (username);
ALTER TABLE ONLY users
ADD CONSTRAINT users_id_cat_fkey FOREIGN KEY (id_cat) REFERENCES usercategories(id_numcat);
So it's defining the sequence and all the constraints after creating the base table, not as part of it, and it's specifying a bunch of stuff that would usually be set by defaults.
The effect is the same and you can do things this way if you want.
Stuff like SERIAL PRIMARY KEY is basically just convenient shorthand. All that is covered well in the documentation for CREATE TABLE, so once you know what you want you can generally figure out how to define it pretty easily. Most of the time, anything you can write in ALTER TABLE ... ADD ... can be written the same in CREATE TABLE (...), eg:
ALTER TABLE ONLY users
ADD CONSTRAINT users_id_cat_fkey FOREIGN KEY (id_cat) REFERENCES usercategories(id_numcat);
can be done at create time with:
CREATE TABLE users (
....,
CONSTRAINT users_id_cat_fkey FOREIGN KEY (id_cat) REFERENCES usercategories(id_numcat)
);
Additionally, for any column-specific CONSTRAINT there's usually a way to tack it on to the end of the column definition. In this case, you omit the CONSTRAINT constraint_name (it's generated) and the FOREIGN KEY (id_cat) (because the column is implied, you don't need to specify it), and write:
CREATE TABLE users (
....
id_cat integer REFERENCES usercategories(id_numcat),
....
);
Once you know what to look for in the CREATE TABLE docs it's usually easy to find how to write what you want.
I created a set of tables in a database.A assigned relations with foreign key.
i am not getting cascading effect of data in mysql.
i created a table named ME and with MY_ID column name
I also created table named MY_Friends with MY_ID column name and foreing key with references to ME(MY_ID).
I am able to notice cascading effect in mysql
Me
My_ID | int(11) | NO | PRI | 0
My_Friends
My_ID | int(11) | YES | MUL | NULL | |
there are two columns description in two tables
FOREIGN KEY and CASCADE will have no effect if the tables are MyISAM. Check if the tables are defined using the InnoDB engine. Give us the output from SHOW CREATE TABLE Me and SHOW CREATE TABLE My_Friends so we can verify if that's the problem.
I have two tables:
Table A: with a composite primary key.
CommonID (PK) | Month (PK) | some data...
-----------------------------------------
1 | May 2011 | ...
1 | June 2011 | ...
2 | May 2011 | ...
2 | June 2011 | ...
Table B: referencing to table A
ID (PK) | A_CommonID (FK)| some data...
-----------------------------------------
... | 1 | ...
... | 2 | ...
As you can see table B isn't referencing the whole primary key but it will definitely always reference a unique entry in table A because there is a global value for the specified used month which will be used for A.Month in SQL-queries.
Now my question is, is that allowed or am I violating several rules of Database design?
I would really appreciate a nice answer because I will use it in the final document which I have to write for my bachelor's degree.
Thanks a lot in advance!
No, this is not allowed.
If you have a composite primary key consisting of more than one column, your foreign keys must also be composite and reference all the columns involved in your primary key.
A foreign key must reference the primary key, the whole key and nothing but the key (so help you Codd) :-)
What you might be able to do is to have a separate unique index on that A_CommonID column in Table A so that your Table B can reference that unique index (instead of the PK).
I am wondering if all these are exactly the same or if there is some difference.
Method 1:
CREATE TABLE testtable
(
id serial,
title character varying,
CONSTRAINT id PRIMARY KEY (id)
);
Method: 2
CREATE TABLE testtable
(
id serial PRIMARY KEY,
title character varying,
);
Method 3:
CREATE TABLE testtable
(
id integer PRIMARY KEY,
title character varying,
);
CREATE SEQUENCE testtable_id_seq
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
ALTER SEQUENCE testtable_id_seq OWNED BY testtable.id;
Update: I found something on the web saying that by using a raw sequence you can pre-allocate memory for primary keys which helps if you plan on doing several thousand inserts in the next minute.
Try it and see; remove the trailing "," after "varying" on the second and third so they run, execute each of them, then do:
\d testtable
after each one and you can see what happens. Then drop the table and move onto the next one. It will look like this:
Column | Type | Modifiers
--------+-------------------+--------------------------------------------------------
id | integer | not null default nextval('testtable_id_seq'::regclass)
title | character varying |
Indexes:
"id" PRIMARY KEY, btree (id)
Column | Type | Modifiers
--------+-------------------+--------------------------------------------------------
id | integer | not null default nextval('testtable_id_seq'::regclass)
title | character varying |
Indexes:
"testtable_pkey" PRIMARY KEY, btree (id)
Column | Type | Modifiers
--------+-------------------+-----------
id | integer | not null
title | character varying |
Indexes:
"testtable_pkey" PRIMARY KEY, btree (id)
First and second are almost identical, except the primary key created is named differently. In the third, the sequence is no longer filled in when you insert into the database. You need to create the sequence first, then create the table like this:
CREATE TABLE testtable
(
id integer PRIMARY KEY DEFAULT nextval('testtable_id_seq'),
title character varying
);
To get something that looks the same as the second one. The only upside to that is that you can use the CACHE directive to pre-allocate some number of sequence numbers. It's possible for that to be a big enough resource drain that you need to lower the contention. But you'd need to be doing several thousand inserts per second, not per minute, before that's likely to happen.
No semantic difference between method 1 and method 2.
Method 3 is quite similar, too - it's what happens implicitly, when using serial. However, when using serial, postgres also records a dependency of sequence on the table. So, if you drop the table created in method 1 or 2, the sequence gets dropped as well.