Suppose I have the following schema:
CREATE TABLE `users` (
`id` int(10) unsigned auto_increment,
`historyId` varchar(255),
PRIMARY KEY (`id`)
);
CREATE TABLE `histories` (
`id` int(10) unsigned auto_increment,
`history` TEXT,
PRIMARY KEY (`id`)
);
A User only ever has one History, and the purpose of not having histories point to users is that many other tables (not mentioned in this schema) also have histories.
What's the simplest way to make it so that deleting a User will also delete its History?
You can use trigger like this:
DELIMITER $$
CREATE TRIGGER delete_user_history_on_delete_user
AFTER DELETE ON `users`
FOR EACH ROW
BEGIN
DELETE FROM `histories` WHERE id = old.historyId;
END$$
DELIMITER ;
I don't think you can use a foreign key to cascade deletes here, because the data types don't match. You have VARCHAR(255) in one table, and INT(10) in the other. (What's up with that?)
I think you'll have to use either a trigger or a stored procedure, neither of which is entirely satisfactory. Triggers are theoretically the safest, in that client code can't sneak around them. (Client code could just avoid calling a stored procedure.) But there are APIs that don't activate MySQL triggers.
MySQL triggers are activated by SQL
statements only. They are not
activated by changes in tables made by
APIs that do not transmit SQL
statements to the MySQL Server
If User-histories it's a 1-1 relation you can put the constraint in the users table and not in the 'hisories' one
Related
I seem to have run across a strange situation where there is a specific table name that I cannot use. Let me explain.
CREATE TABLE IF NOT EXISTS hotstick_work_orders (
work_order_id BIGINT UNSIGNED NOT NULL,
step_id TINYINT UNSIGNED NOT NULL,
user_id INT UNSIGNED NOT NULL,
FOREIGN KEY (work_order_id) REFERENCES work_orders (id) ON DELETE CASCADE ON UPDATE NO ACTION,
FOREIGN KEY (step_id) REFERENCES hotstick_steps (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
PRIMARY KEY (work_order_id, step_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
This works fine on my local MySql stack (v5.6.17), but gives me error code #1005 when I try it using PhpMyAdmin (v4.0.10.7, MySQL v5.5.42) on GoDaddy.
Okay, you say, this is obviously just another case where the FK definitions don't match perfectly, or possibly one where a referenced table is missing an index on the column. However, I can't even create the table without FKs - the following fails just the same:
CREATE TABLE IF NOT EXISTS hotstick_work_orders (
work_order_id BIGINT UNSIGNED NOT NULL,
step_id TINYINT UNSIGNED NOT NULL,
user_id INT UNSIGNED NOT NULL,
PRIMARY KEY (work_order_id, step_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Even more interesting, if I run the original create table query but use a different name, even hotstick_work_order (without the final s), it works FINE. I can rename this table to anything I want (including longer names), EXCEPT for hotstick_work_orders. Trying gives me error #1025, but only with that specific name.
E.g.:
RENAME TABLE hotstick_work_order TO hotstick_work_orders;
Resulting Error: #1025 - Error on rename of './db/hotstick_work_order' to './db/hotstick_work_orders' (errno: -1)
# Whereas this works fine:
RENAME TABLE hotstick_work_order TO hotstick_work_orders_something;
I don't have any previous table with that name, nor could I find any existing constraints in the information_schema.table_constraints table.
Of course I can manage using a different table name, no big deal, but I'm very curious - what could possibly cause such behavior?
What you're probably suffering from is a bad case of cached naming. A proper server restart might solve your problem, but as you mentioned, you can't do it due to shared server configuration.
When I asked you to create a MyISAM table with the exact name was so that we could establish that the problem was indeed cached indexes or constraints linked to your table name. Now what I recommend you to do is:
Try and Repair the MyISAM table.
Try to Optimize the table.
Execute a SELECT SQL_NO_CACHE * FROM TABLE to stop MySQL from caching queries.
Drop the Table.
Re-create it as you wish it would be (with constraints and all, InnoDB).
I have exported a database (structure only) from phpMyAdmin which I want to use as the bases for a script that can update a previous version of the same database structure and add columns and tables if they don't exist.
For example, if the CREATE TABLE statement looks like this:
--
-- Table structure for table `users`
--
CREATE TABLE IF NOT EXISTS `users` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(52) NOT NULL,
`password` varchar(40) NOT NULL,
`dev` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=21;
How can I adjust this so if a field in the table does not exist it will be created even if the table already exists in the database.
The aim of this script is to allow updating of already existing databases, adding columns that may be missing, but it should not remove any tables or fields.
According to this, it seems the easiest way to accomplish this is to create a temporary table (by which I don't necessarily mean of type TEMPORARY, just that the use will be short and limited; although in most cases type TEMPORARY may be a reasonable choice here), copy the data from your old table to the temporary one, create the new table with the updated structure, then bring everything back over from the temporary table. Finally, your script may rename the old table as a backup or drop it and the temporary table entirely.
SET GLOBAL event_scheduler = ON;
CREATE TABLE question(
qid INT AUTO_INCREMENT PRIMARY KEY,
name CHAR(30) NOT NULL,
text CHAR(100) NOT NULL,
variation BOOLEAN NOT NULL,
url CHAR(100) NOT NULL UNIQUE,
expired TIMESTAMP NOT NULL
);
CREATE TABLE alternativ(
aid INT AUTO_INCREMENT PRIMARY KEY,
name CHAR(30) NOT NULL,
text CHAR(50) NOT NULL,
number_chosen INT,
qid INT NOT NULL
);
ALTER TABLE alternativ
ADD FOREIGN KEY (qid)
REFERENCES question(qid);
CREATE EVENT delete_expired
ON SCHEDULE
EVERY 1 DAY
DO
DELETE FROM alternativ WHERE alternativ.qid IN (SELECT qid FROM question WHERE question.expired<CURRENT_TIMESTAMP)
DELETE FROM question WHERE question.expired < CURRENT_TIMESTAMP;
My question is: Should this event work with the specified database? I have tried, but it dosen't seem to work. The idea is that the database itself will delete questions that has expired. Help would much be appreciated.
If you want to specify multiple statements within the event body, you will need to wrap them within a compound statement block such as BEGIN ... END (in order to make such a command work, one must configure one's client to use an alternative statement delimiter in order that it does not think the first encountered semicolon terminates the CREATE EVENT statement—in the mysql command-line tool, one can use the DELIMITER command):
DELIMITER ;;
CREATE EVENT ... DO BEGIN
DELETE ... ;
DELETE ... ;
END ;;
DELIMITER ;
That said, one can delete from multiple tables with a single DELETE command using the multiple-table syntax:
DELETE alternativ, question
FROM alternativ JOIN question USING (qid)
WHERE question.expired < CURRENT_TIMESTAMP
However, all that said, you might fare better specifying foreign key constraints that cascade record deletions:
FOREIGN KEY (qid) REFERENCES question(qid) ON DELETE CASCADE
Then, one only need DELETE the referenced record (i.e. in the question table) and MySQL will delete the referencing records (i.e. in the alternativ table) for you.
Consider the following table:
CREATE TABLE `demo` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`from_val` BIGINT(20) DEFAULT NULL,
`to_val` BIGINT(20) DEFAULT NULL,
PRIMARY KEY (`id`)
);
Is it possible to create a constraint that prevents a second record existing in the same table with a from_val or to_val between another record's from_val and to_val? So, from an empty table the second query should fail with a constraint failure:
INSERT INTO `demo` (`from_val`, `to_val`) VALUES (1,10),(11,20);
INSERT INTO `demo` (`from_val`, `to_val`) VALUES (5,15);
Validating the data on the way in/update is trivial, I was just curious to see whether MySQL could enforce this at a RDBMS level.
No, MySQL does not support (or enforce) any declarative constraint which would raise an exception given the conditions you specify.
The values in the tuple (5,15) ("the second query") do not conflict with any values in any of the rows previously inserted into the table... (1,10),(11,20), at least in terms of what is available to declarative constraints in MySQL.
The workaround is to define BEFORE INSERT and BEFORE UPDATE triggers on the table; those can perform whatever checks you need, and raise an exception.
Apparently MySQL have the really annoying restriction of not being able to update a table inside a trigger defined for that same table.
I'm using MySQL version 5.1 and I get the error: "Can't update table in stored function/trigger because it is already used by statement which invoked this function/trigger".
What I have is this:
create table folder(
id int unsigned not null auto_increment PRIMARY KEY ,
name varchar(100) not null ,
parentId int unsigned not null
) ;
It's a hierarchical folder structure. A folder has a name and possibly a parent folder (if not then parentId is zero).
When a folder is deleted I need to change the parentId of all subfolders which were inside it, so that they don't become children of a nonexistent folder.
It's rather simple (almost trivial):
CREATE DEFINER=root#localhost TRIGGER onFolderDelete after delete ON folder
FOR EACH ROW update folder set parentId=0 where parentId=old.id ;
However, such a simple trigger is not allowed by MySQL because, as I said above, you cannot update a table inside its own trigger.
Is there any way of implementing such a trigger by emulating its effects in some way??
P.S.: Please dont suggest sending both statements in sequence (the DELETE and the UPDATE). That's obviously the last solution if nothing else is possible.
Edit:
I'm using MyISAM engine (performance reasons) so I can't use foreign keys.
Can't you add a foreign key with ON DELETE SET NULL (or DEFAULT) ?
UPDATE (DEFAULT is still not implemented;SET NULL is the only option...)
So you will have something like
create table folder(
id int unsigned not null auto_increment PRIMARY KEY ,
name varchar(100) not null ,
parentId int unsigned null ,
FOREIGN KEY(parentId) REFERENCES folder(id) ON UPDATE CASCADE ON DELETE SET NULL
) ;