I have the following db structure with 3 tables as an example:
Employee
id // primary key, autoincrement
employee_no // a varchar
Scenario
id // primary key, autoincrement
key // a varchar
Case
id // primary key, auto-increment
employee_id // foreign key to Employee table
scenario_id // foreign key to Scenario table
Say I already have data in employee and scenario table and I want to insert a new case into the case table so that it fills the foreign keys during the insert. The new case has employee_no in employee table and key in scenario table. I will need to join the two tables using the above values to get employee id and scenario id.
This post (Mysql: How to insert values in a table which has a foreign key) showed how this can be done with one foreign key, how do I do the same thing with two foreign keys?
I currently have something like this that does not work:
INSERT INTO `case` (scenario_id, employee_id, employee_no)
SELECT
(SELECT scenario.id FROM scenario WHERE scenario.`key` = 'UC01') as scenario_id,
(SELECT employee.id, employee.employee_no FROM employee WHERE employee.employee_no = "0001") as employee_id, employee_no
Join the two tables:
INSERT INTO case (scenario_id, employee_id)
SELECT s.id, e.id
FROM scenario AS s
CROSS JOIN emplopyee AS e
WHERE s.`key` = 'UC01'
AND e.employee_no = '0001'
Issue:
I'm using PostgreSQL Database.
I have one table (Albums) to be linked to two other tables (Clients, Domains). So if you are Client or Domain you can have Album. But in Albums table owner can handle only single foreign key. How can I solve this issue?
Dream: Single Album can own only (1) Client or Domain. Need fix issue with foreign keys. Albums: id | owner (multiple foreign -> Clients:id or Domains:id) --> can not do this | name. I just need some smart rework.
Tables (now can have Album only Domain):
Albums
Clients
Domains
Albums (table with foreign key yet):
id | owner (foreign key -> Domains:id) | name
Clients:
id | first_name | last_name
Domains:
id | owner | name
Add 2 FK columns, and a CHECK constraint, to enforce only one of them is NOT NULL...
Something like this:
CREATE TABLE albums (
id serial PRIMARY KEY,
client_id integer,
domain_id integer,
name varchar(255) NOT NULL,
FOREIGN KEY (client_id) REFERENCES clients(id),
FOREIGN KEY (domain_id) REFERENCES domains(id),
CHECK ((client_id IS NULL) <> (domain_id IS NULL))
);
To query you can use something like this:
SELECT a.id, COALESCE(c.id, d.id) AS owner_id, COALESCE(c.name, d.name) AS owner_name,
a.name AS title
FROM albums a
LEFT JOIN clients c ON a.client_id = c.id
LEFT JOIN domains d ON a.domain_id = d.id
#e_i_pi's version
CREATE TABLE entities (
id serial PRIMARY KEY,
type integer, -- could be any other type
-- any other "common" values
);
CREATE TABLE client_entities (
id integer PRIMARY KEY, -- at INSERT this comes from table `entities`
name varchar(255) NOT NULL,
);
CREATE TABLE domain_entities (
id integer PRIMARY KEY, -- at INSERT this comes from table `entities`
name varchar(255) NOT NULL,
);
CREATE TABLE albums (
id serial PRIMARY KEY,
owner_id integer FOREIGN KEY REFERENCES entities(id), -- maybe NOT NULL?
name varchar(255) NOT NULL,
);
Query:
SELECT a.id, owner_id, COALESCE(c.name, d.name) AS owner_name, a.name AS title
FROM albums a
LEFT JOIN entities e ON a.owner_id = e.id
LEFT JOIN client_entities c ON e.id = c.id AND e.type = 1 -- depending on the type of `type`
LEFT JOIN domain_entities d ON e.id = d.id AND e.type = 2
Righto, so as suggested in the comment to the answer by #UsagiMiyamoto, there is a way to do this that allows declaration of entity types, with cascading. Note that this solution doesn't support unlimited entity types, as we need to maintain concrete FK constraints. There is a way to do this with unlimited entity types, but involves triggers and quite a bit of nastiness.
Here's the easy to understand solution:
-- Start with a test schema
DROP SCHEMA IF EXISTS "entityExample" CASCADE;
CREATE SCHEMA IF NOT EXISTS "entityExample";
SET SEARCH_PATH TO "entityExample";
-- We'll need this to enforce constraints
CREATE OR REPLACE FUNCTION is_entity_type(text, text) returns boolean as $$
SELECT TRUE WHERE $1 = $2
;
$$ language sql;
-- Unique entity types
CREATE TABLE "entityTypes" (
name TEXT NOT NULL,
CONSTRAINT "entityTypes_ukey" UNIQUE ("name")
);
-- Our client entities
CREATE TABLE clients (
id integer PRIMARY KEY,
name TEXT NOT NULL
);
-- Our domain entities
CREATE TABLE domains (
id integer PRIMARY KEY,
name TEXT NOT NULL
);
-- Our overaching entities table, which maintains FK constraints against clients and domains
CREATE TABLE entities (
id serial PRIMARY KEY,
"entityType" TEXT NOT NULL,
"clientID" INTEGER CHECK (is_entity_type("entityType", 'client')),
"domainID" INTEGER CHECK (is_entity_type("entityType", 'domain')),
CONSTRAINT "entities_entityType" FOREIGN KEY ("entityType") REFERENCES "entityTypes" (name) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "entities_clientID" FOREIGN KEY ("clientID") REFERENCES "clients" (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "entities_domainID" FOREIGN KEY ("domainID") REFERENCES "domains" (id) ON DELETE CASCADE ON UPDATE CASCADE
);
-- Our albums table, which now can have one owner, but of a dynam ic entity type
CREATE TABLE albums (
id serial PRIMARY KEY,
"ownerEntityID" integer,
name TEXT NOT NULL,
CONSTRAINT "albums_ownerEntityID" FOREIGN KEY ("ownerEntityID") REFERENCES "entities"("id")
);
-- Put the entity type in
INSERT INTO "entityTypes" ("name") VALUES ('client'), ('domain');
-- Enter our clients and domains
INSERT INTO clients VALUES (1, 'clientA'), (2, 'clientB');
INSERT INTO domains VALUES (50, 'domainA');
-- Make sure the clients and domains are registered as entities
INSERT INTO entities ("entityType", "clientID")
SELECT
'client',
"clients".id
FROM "clients"
ON CONFLICT DO NOTHING
;
INSERT INTO entities ("entityType", "domainID")
SELECT
'domain',
"domains".id
FROM "domains"
ON CONFLICT DO NOTHING
;
If you don't like the idea of inserting twice (once in client, once in entites, for example) you can have a trigger on inserts in the clients table, or alternately create an insert function that inserts to both tables at once.
So normally when one wants to declare a foreign key on a populated table, they'll first make sure that the foreign table has all the values contained in the table where the foreign key is being created.
select * from myschema.Orders
where ProductID not in
(select ProductID from myschema.Products);
Should any records be returned from the above query, then the DBA will first have to deal with those before setting a foreign key. But what if the Products table contained a composite foreign key? How would the above check be done if the Products table had both the fields ProductID and (For example) BatchNumber as a primary key?
This is assuming the Orders table also has both fields.
You can use NOT EXISTS in that case.
Something like this:
select *
from myschema.Orders o
where not exists (
select ProductID
from myschema.Products p
where o.ProductID = p.ProductID
and o.BatchNumber = p.BatchNumber
);
I'm currently migrating data from our old database schema into a new one.
I have a table called Product on my old database, on my new database schema I still have a Product table and a new column for b_id, and another table B.
During migration, I will need to insert an entry to table B for every product that I have in my table and update the Product table to set the b_id for the newly created entry on b for this product. How can I accomplish this?
To transfer the data for the product table, I have:
INSERT INTO newSchema.Product
SELECT id, prodName
FROM oldSchema.Product
I'm thinking of looping into the oldSchema.Product and for every product, have a call to INSERT INTO B and UPDATE TABLE Product, but no idea how to put this into code.
Any help will be appreciated. Thanks!
The same goes for B table...
INSERT INTO newschema.B (product_id)
SELECT id
FROM oldschema.product
Edited:
CREATE TABLE newschema.b (
id INT NOT NULL AUTO_INCREMENT,
product_id INT,
PRIMARY KEY (id)
);
CREATE TABLE newschema.product (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(35) NOT NULL,
b_id INT,
PRIMARY KEY (id),
FOREIGN KEY (b_id) REFERENCES b(id)
);
INSERT INTO newschema.b (product_id)
SELECT product_id
FROM oldschema.product ;
INSERT INTO newschema.product (id, name, b_id)
SELECT OP.id,
OP.name,
NB.id
FROM oldschema.product AS OP,
newschema.b AS NB
WHERE NB.product_id = OP.id ;
I'm working on a Django application and created a new table tblA which has a foreign key that is linked to the primary key of tblB.
Now tblB already has several entries (and thus several primary keys in it already).
I want to run an SQL query that will create a new row for every primary key in tblA inside tblB with the corresponding foreign key copied and default values of all other columns in tblB inserted in the rows.
I hope I was clear enough!
you have forgot to paste your table structure .. I am considering as below-
tblA
(
aCol1_PK (primary key),
acol2_desc (description)
)
as of now just take an example of two columns only.
Now for table B
tblB
(
bCol1,
bCol2,
bCol3_FK (foreign key)
)
Now let say you have default values as -
bCol1 - "B_Col1_default_val"
bcol2 - "B_Col2_default_val"
for this situation if you want to insert rows for p_key from table A which is not present in Table B, you can try below query -
insert into tblB(col1,col2,col3)
( select 'b_col_default_val','b_col_default_val', acol1_pk
from tbla a
where a.acol1_pk not in ( select b.bcol3_fk from tblb b))
if you want to insert one row for all the primary key value present in table A then -
insert into tblB(col1,col2,col3)
( select 'b_col_default_val','b_col_default_val', acol1_pk
from tbla a )
I hope it will help you ..
if you require any clarification you can ask/comment.