Alembic SQLite ALTER TABLE with self-referencing foreign key - sqlite

The Alembic migration for a SQLite database:
def upgrade():
with op.batch_alter_table('my_table') as batch_op:
batch_op.add_column(sa.Column('parent_id', sa.String(24)))
batch_op.create_foreign_key('parent_constraint', 'my_table', ['parent_id'], ['id'])
which is supposed to create a foreign key parent_id referencing id of the same table my_table, creates a reference to a table called _alembic_batch_temp:
CREATE TABLE "my_table" (
id VARCHAR(24) NOT NULL,
parent_id VARCHAR(24),
PRIMARY KEY (id),
CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id)
)
How to create self-referencing constraints when altering a table?

After some research I found that the problem here is the way Alembic does the batch migration. In short, at the current version (0.7.6) of Alembic it's not possible to create relation with self by migration.
As described in the Alembic documentation, to do the migration, new table is created with a temporary name and changes from the alter table
code. In this case:
CREATE TABLE _alembic_batch_temp (
id VARCHAR(24) NOT NULL,
parent_id VARCHAR(24),
PRIMARY KEY (id),
CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id)
)
The table is filled with the data from the old table:
INSERT INTO _alembic_batch_temp (id) SELECT id FROM my_table;
Then the old table is removed:
DROP TABLE my_table;
Finally the newly created table is renamed to it's proper name:
ALTER TABLE _alembic_batch_temp RENAME TO my_table;
The problem with this way of doing things is already visible in the first code snippet. The newly created foreign key is referencing the temporary table and once it's created it can't be changed due to restrictions in SQLite. So after the renaming of the table you end up with the table you provided:
CREATE TABLE "my_table" ( # new name
id VARCHAR(24) NOT NULL,
parent_id VARCHAR(24),
PRIMARY KEY (id),
CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id) # old reference
)
To Avoid this situation you can create the batch migration manually:
Rename the old table to some temporary name:
ALTER TABLE my_table RENAME TO migration_temp_table;
Create new table with proper name and proper reference:
CREATE TABLE my_table (
id VARCHAR(24) NOT NULL,
parent_id VARCHAR(24),
PRIMARY KEY (id),
CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES my_table (id)
)
Copy the data:
INSERT INTO my_table (id) SELECT id FROM migration_temp_table;
Remove the old table:
DROP TABLE migration_temp_table;

Related

Set auto_increment for a primary key already created

I created two tables in a database on MariaDB: CasaProduzione and Produzione.
create table CasaProduzione(nome varchar(80) primary key);
alter table CasaProduzione add column id tinyint;
alter table CasaProduzione drop primary key;
alter table CasaProduzione modify nome varchar(80) not null;
alter table CasaProduzione modify id tinyint primary key auto_increment;
create table Produzione(
id_film smallint not null,
id_casaProduzione tinyint not null,
data date not null,
constraint `fk_produzione`
foreign key (id_film) references Film(id),
foreign key (id_casaProduzione) references CasaProduzione(id)
on update cascade
on delete restrict);
alter table Produzione modify data in smallint(4);
After i moved the column id in the table of CasaProduzione at first
alter table CasaProduzione modify column id tinyint(4) first;
Then i tried to set auto_increment in prevoius column
alter table Produzione modify column id tinyint(4) auto_increment;
ERROR 1833 (HY000): Cannot change column 'id': used in a foreign key constraint 'Produzione_ibfk_1' of table 'Film.Produzione'
So i tried to cancel the foreign key from Produzione
alter table Produzione drop foreign key fk_produzione;
but the result is the same.
What am I doing wrong?
After suggestion from the comment, I post here the result of this command:
SHOW CREATE TABLE Film.Produzione \G;
***************************
1. row
***************************
Table: Produzione
Create Table:
CREATE TABLE Produzione (
id_film SMALLINT(6) NOT NULL,
id_casaProduzione TINYINT(4) NOT NULL,
DATA SMALLINT(4) DEFAULT NULL,
KEY fk_produzione (id_film),
KEY id_casaProduzione (id_casaProduzione),
CONSTRAINT Produzione_ibfk_1 FOREIGN KEY (id_casaProduzione) REFERENCES CasaProduzione (id) ON UPDATE CASCADE )
ENGINE=INNODB DEFAULT CHARSET=utf8mb4
1 row in set (0.001 sec)
As told by #FanoFN, with the command
show CREATE TABLE Film.Produzione \G;
the system showed me the constraint on the table Produzione. The system created the constraint Produzione_ibfk_1
I deleted this constraint with the command
alter table Produzione drop foreign key Produzione_ibfk_1;
Query OK, 0 rows affected (0.130 sec)
Records: 0 Duplicates: 0 Warnings: 0
After I can applied the command
alter table Produzione modify column id tinyint(4) auto_increment;
Thanks a lot

Failed Constraint - how to correctly insert something in a sqlite3 table?

We were asked to create a database, then a table, then fill it with the givens.
Here's what I have come up with so far:
sqlite3 me5.db;
create table petID(pet_id int(3) PRIMARY KEY, pet_name varchar(10), pet_type varchar(10), pet_age int(3));
INSERT INTO petID(pet_id, pet_name, pet_type, pet_age) VALUES(246, 'ROVER', 'DOG', 12);
INSERT INTO petID(pet_id, pet_name, pet_type, pet_age) VALUES(298, 'SPOT', 'DOG', 2);
INSERT INTO petID(pet_id, pet_name, pet_type, pet_age) VALUES(341, 'MORRIS', 'CAT', 4);
INSERT INTO petID(pet_id, pet_name, pet_type, pet_age) VALUES(519, 'TWEEDY', 'BIRD', 2);
create table petowner(pet_id int(3), owner varchar(100) PRIMARY KEY, FOREIGN KEY(pet_id) REFERENCES petID(pet_id) ON DELETE CASCADE);
INSERT INTO petowner(pet_id, owner) VALUES(246, 'SAM COOK');
INSERT INTO petowner(pet_id, owner) VALUES(298, 'TERRY KIM');
However, when I insert these two below:
INSERT INTO petowner(pet_id, owner) VALUES(341, 'SAM COOK');
INSERT INTO petowner(pet_id, owner) VALUES(519, 'TERRY KIM');
I get a 'constraint failed' error. Any ideas how to fix this? Thanks
The constraint that you violated is the primary key of petowner; it prevents 'SAM COOK' appearing twice.
If a pet can have multiple owners, then the primary key of the petowner table must contain both columns:
CREATE TABLE petowner(
pet_id int(3),
owner varchar(100),
PRIMARY KEY(pet_id, owner),
FOREIGN KEY(pet_id) REFERENCES petID(pet_id) ON DELETE CASCADE
);

Except the name of table what property can use to differentiate these tables

I have a database which contains many tables and these tables can be added or removedd any time.So I give each of them a different name like Table1,Table2,...
but it's uncomfortable to use these table because sometime I forget what infomation was stored in Table1
So I want something to differentiate these all tables, some property that I can be specified when I create a table and I can use to access a specific table when I need to fetch informations from that table
As one comment says, you could create a table for notes on the other tables:
CREATE TABLE notes (
id INT AUTO_INCREMENT,
table_name VARCHAR(64),
note VARCHAR(255),
PRIMARY KEY (id)
);
By the way, MySQL (but not SQLite) allows comments on the table itself:
CREATE TABLE table1 (
id INT AUTO_INCREMENT,
val INT,
PRIMARY KEY (id)
) COMMENT = 'Table of stuff';
-- Show the comment
SHOW TABLE STATUS WHERE NAME='table1';
-- Just show names and comments
SELECT `TABLE_NAME`, `TABLE_COMMENT`
FROM information_schema.tables
WHERE table_schema = DATABASE();

foreign key mismatch on delete command for unrelated record

I have the following 5 tables defined with a few records inserted into the 1st 4. This is using sqlite 3.7.1.7 with foreign key constaint enabled.
create table if not exists subject (id varchar(50) primary key,desc varchar(100));
insert into subject (id,desc) values ("subject1","test subject");
create table if not exists subjectlevel (id_subject_id varchar(50) references subject(id) on delete cascade, id integer not null, desc varchar(100) not null, questmcmaxselections integer not null, primary key (id_subject_id,id));
insert into subjectlevel (id_subject_id,id,desc,questmcmaxselections) values ("subject1",1,"test subject1 level 1",4);
insert into subjectlevel (id_subject_id,id,desc,questmcmaxselections) values ("subject1",2,"test subject1 level 2",4);
create table if not exists questmc (id integer primary key, text varchar(300) not null, includeallanswers int not null, subject_id varchar(50), subjectlevel_id integer, foreign key (subject_id, subjectlevel_id) references subjectlevel (id_subject_id,id) on delete cascade);
insert into questmc (text,includeallanswers,subject_id,subjectlevel_id) values ("this is a _ question", 1, "subject1",1);
create table if not exists questmcselection (id integer primary key, text varchar(100) not null, subject_id varchar(50), subjectlevel_id integer, foreign key (subject_id, subjectlevel_id) references subjectlevel (id_subject_id,id) on delete cascade);
insert into questmcselection (text,subject_id,subjectlevel_id) values ("this is a solution","subject1",1);
create table if not exists questmc_questmcselection(id integer primary key, answer integer not null, questmc_id integer, questmcselection_id integer, subject_id varchar(50), subjectlevel_id integer, foreign key (questmc_id) references questmc(id) on delete cascade, foreign key (questmcselection_id) references questmcselection (id) on delete cascade, foreign key (subject_id,subjectlevel_id) references questmc (subject_id,subjectlevel_id) on delete cascade, foreign key (subject_id,subjectlevel_id) references questmcselection (subject_id,subjectlevel_id));
if i attempt to delete the second record in the subjectlevel table, i get a foreign key mismatch error as long as table questmc_questmcselection is defined.
sqlite> delete from subjectlevel where id=2;
Error: foreign key mismatch - "questmc_questmcselection" referencing "questmcselection"
questmc, questmcselection, and questmc_questmcselection have no related existing records that should prevent this deletion. Any idea why this error occurs?
This error has nothing to do with this particular subjectlevel record.
Your problem is that your tables lack the required indexes.
This was not reported earlier because that DELETE statement was the first command that required SQLite to check the consistency of the database schema.
Based on CL's answer -
sqlite> create table parent(a);
sqlite> create table child(a, FOREIGN KEY (a) REFERENCES parent(a));
sqlite> pragma foreign_keys = ON;
sqlite> insert into parent values(3);
sqlite> insert into child values (3);
Error: foreign key mismatch - "child" referencing "parent"
sqlite> create unique index p_a on parent(a);
sqlite> insert into child values (3);
sqlite> _
From the documentation:
Usually, the parent key of a foreign key constraint is the primary key
of the parent table. If [not], then the parent key columns must be
collectively subject to a UNIQUE constraint or have a UNIQUE index [which uses]
the collation sequences ... in the CREATE TABLE
statement for the parent table.
i.e. the alternative is:
sqlite> create table parent(a, b, UNIQUE (a, b));
sqlite> create table child (x, y, FOREIGN KEY (x, y) REFERENCES parent(a, b));
(this also highlights multi-column foreign keys; they work with indexes too...)

Error Code 1005 in mysql while applying Foreign key to a table

I am having two tables tbluserlogindetail and tblRoles.
tbluserlogindetail is as follows
CREATE TABLE `tbluserlogindetail` (
`LoginID` varchar(45) NOT NULL,
`Name` varchar(45) DEFAULT NULL,
`Password` varchar(45) DEFAULT NULL,
PRIMARY KEY (`LoginID`),
UNIQUE KEY `LoginID_UNIQUE` (`LoginID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
EDIT doratesting.tbluserlogindetail;
and my second table tblRoles is as follows
CREATE TABLE `tblroles` (
`RoleID` int(11) NOT NULL,
`LoginID` varchar(45) NOT NULL,
PRIMARY KEY (`RoleID`,`LoginID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
I tried the the following to assign a primary key for the column LoginID in tblroles table but i don't know where i went wrong can any one help me.
I have gone through the documentation but unable to rectify the error so please help me
ALTER TABLE tblroles
ADD FOREIGN KEY (loginid)
REFERENCES tbluserlogindetail(loginid)
The referenced columns must be unique in the referenced table. Try one of these options:
The primary key on tbluserlogindetail is (ID, loginid) so you could use that as your foreign key instead of (loginid). This will require you to add a column tbluserlogindetail_ID to tblroles.
Try adding a unique index to the LoginID column of the tbluserlogindetail table. This is only possible if it is actually unique.
Also, why is your primary key on tbluserlogindetail defined as PRIMARY KEY (ID, LoginID)? The ID field is an auto-increment field and is already unique. So why do you also include the LoginID as part of the primary key? I think you need to go back to your table design and rethink which columns to choose as your primary keys.
this is just guess/ assumption ,i like to share here,
PRIMARY KEY (ID,LoginID)
in tbluserlogindetail are considered as surrogate key,
when we execute the child table,That is tblroles,
child table expected primary key in parent table,
but actually we created the surrogate key, due to this reason, i alter query failed,
ALTER TABLE tblroles ADD FOREIGN KEY (LoginID) REFERENCES tbluserlogindetail(LoginID)
This is my assumption, give feedback for my answer,
I did following changes to execute the Alter table command:
1.tblroles create with given Create query command, after created i manually deleted the
LoginID primary key in tblroles table,
Changed varchar to int in LoginID,
in tbluserlogindetail, deleted the ID AUTO_INCREMENT,Pk.
Check i updated ALTER query

Resources