I have a table created by this SQL statement:
CREATE TABLE employees (
id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR(20),
PRIMARY KEY (id)
);
I would like to insert into the table using something like
INSERT IGNORE INTO employees (name) values ('foo');
but for that statement to do nothing if there is already a person with a name 'foo' in the table. Is there a statement out there that ignores duplicates on a field other than a primary key or a field that is defined as unique?
INSERT INTO employees (name)
SELECT "foo" name FROM (select count(*) c
from employees
where name = "foo"
having c = 0) x;
You should have an index on name for efficiency. I'm not sure why you don't want to make it a unique index.
FIDDLE
Related
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.
I have the following problem:
Every author has a name and n-aliases for this name. Every alias comes from a source. Every alias has m sources. For example
AUTHOR| ALIAS | SOURCE
-------------------------
Will| Willy |George
Will| Bill | Jenny
William| Will| Francis
William| Bill| Maya
I have one table for the author and his name, one for all of his aliases:
CREATE TABLE alias (
authors_id INT NOT NULL,
alias VARCHAR(150) NOT NULL,
id INT NOT NULL AUTO_INCREMENT
PRIMARY KEY (author_id,alias);
id serves as foreign key.
Here's the second table for the sources
CREATE TABLE alias_source (
id INT NOT NULL AUTO_INCREMENT
source VARCHAR(150) NOT NULL,
alias_id INT NOT NULL,
PRIMARY KEY (id)
FOREIGN KEY (alias_id) REFERENCES alias(id);
Now I need an MySQL insert statement for when I insert
author,alias,source into alias that the source is inserted into alias_source.
And on duplicate alias no only a new source is added.
An INSERT statement in SQL can only insert into one table, and can only list columns of that one table. You can't include your source column in the INSERT statement, because it's not a column of the alias table.
Triggers can allow you to insert into a secondary table, but the trigger needs to know the value to insert. In this case the trigger doesn't have any way of getting the value for source.
This is a task that is much easier to do with two INSERT statements.
INSERT IGNORE INTO alias SET author_id = ?, alias = ?;
INSERT INTO alias_source ...
But you have a problem because your alias table has an auto-increment column id, but this column is not part of a key. In InnoDB, you must make the auto-increment column the first column of a key.
Your alias_source table has a foreign key referencing alias(id) but that's not allowed. A foreign key must reference a key of the parent table. It should reference the unique or primary key, and should reference all columns of the key (otherwise you get a row in a child table that might reference multiple rows in the parent table, and that makes no sense).
If you want to use an auto-increment column for your alias table, make it the primary key, and put a secondary UNIQUE constraint on the other columns.
CREATE TABLE alias (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
authors_id INT NOT NULL,
alias VARCHAR(150) NOT NULL,
UNIQUE KEY (author_id,alias));
Then query the id after you insert. Whether you inserted a new row, or the row already existed, you'll get the id for the row with the chosen author and alias.
INSERT IGNORE INTO alias SET author_id = ?, alias = ?;
SELECT id FROM alias WHERE author_id = ? AND alias = ? INTO #id;
INSERT INTO alias_source SET alias_id = #id, source = ?;
Re your followup question in comment:
Good idea, but it doesn't work the way you may think it does. You can do a dummy set of id=id and set a session variable as a side effect.
mysql> insert into alias set author_id=1, alias='alias'
on duplicate key update id = #id:=id;
mysql> select #id;
+------+
| #id |
+------+
| NULL |
+------+
But why didn't it set the session variable to the id? Because this insert was not a duplicate, it was a new row. So the id value had not yet been generated by auto-increment at the time the IODKU executed.
If you subsequently do the same IODKU for a row that is a duplicate, the id value has previously been added to the row, so you get the value in the side-effect:
mysql> insert into alias set author_id=1, alias='alias'
on duplicate key update id = #id:=id;
mysql> select #id;
+------+
| #id |
+------+
| 1 |
+------+
So you'd have to write application code anyway to check if #id is NULL or not, and do a SELECT id query if #id is NULL.
That seems more complicated than just doing the SELECT id as a standard action following the INSERT IGNORE like I showed above.
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 have created 'students' table where 'sid' is the primary key and I have inserted many values into sid. I have created a second table called 'courses' and it has a primary key 'cid' I have entered values for cid as well. Now, I want to create a relation table called 'enroll' which I have done like-
create table enroll(
grade char(2),
sid int not null,
cid int not null,
primary key(sid,cid),
foreign key (cid) references courses(cid) on delete cascade,
foreign key (sid) references students(sid) on delete cascade
);
Now, when I try to view the table using select * from enroll;
I don't get any output. It says "0 rows returned". Why is this? Isn't it supposed to have all the values of sid and cid from the other tables?
In Order to create a new table from values in your other table you can do something like this:
CREATE TABLE new_table AS (SELECT * FROM old_table);
The select statement will be the fields that will be pulled.
You can rename the columns like: Select [field name] as [what you want field name to be]
For More information read this article
Anyway for your particular case:
Create table enroll AS (Select s.sid AS 'Sid', c.cid AS 'Cid' from courses c inner join students s on c.something = s.something)
replace '.something' with the id of the student
you just created the table structure, the schema, your table is empty, so you dont get any results.