I have inherited a PHP project and the client is wanting to add some functionality to their CMS, basically the CMS allows them to create some news, all the news starts with the same content, and that is saved in one table, the actually news headline and articles are saved in another table, and the images for the news are saved in another, basically if the base row for the news is deleted I need all the related rows to be deleted, the database is not setup to work with foreign keys so I cannot use cascade deletion, so how can I delete the all the content I need to, when I only what the ID of the base news row is?
Any help would be very helpful I am sorry I cannot give you much more help, here is this the original SQL of tables scheme if that helps?
--
-- Table structure for table `mailers`
--
CREATE TABLE IF NOT EXISTS `mailers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`mailer_title` varchar(150) NOT NULL,
`mailer_header` varchar(60) NOT NULL,
`mailer_type` enum('single','multi') NOT NULL,
`introduction` varchar(80) NOT NULL,
`status` enum('live','dead','draft') NOT NULL,
`flag` enum('sent','unsent') NOT NULL,
`date_mailer_created` int(11) NOT NULL,
`date_mailer_updated` int(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=13 ;
-- --------------------------------------------------------
--
-- Table structure for table `mailer_content`
--
CREATE TABLE IF NOT EXISTS `mailer_content` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`headline` varchar(60) NOT NULL,
`content` text NOT NULL,
`mailer_id` int(11) NOT NULL,
`position` enum('left','right','centre') DEFAULT NULL,
`created_at` int(10) NOT NULL,
`updated_at` int(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=18 ;
-- --------------------------------------------------------
--
-- Table structure for table `mailer_images`
--
CREATE TABLE IF NOT EXISTS `mailer_images` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(150) NOT NULL,
`filename` varchar(150) NOT NULL,
`mailer_id` int(11) NOT NULL,
`content_id` int(11) DEFAULT NULL,
`date_created` int(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=15 ;
It is worth noting that the schema cannot be changed nor can I change to the DB to MYISAM so that I can use foreign keys.
Add foreign key to table mailer_content
FOREIGN KEY (mailer_id)
REFERENCES mailers(id)
ON DELETE CASCADE
Add foreign key to table mailer_images
FOREIGN KEY (content_id)
REFERENCES mailer_content(id)
ON DELETE CASCADE
http://dev.mysql.com/doc/refman/5.1/en/innodb-foreign-key-constraints.html
It is worth noting that the schema cannot be changed nor can I change to the DB to MYISAM so that I can use foreign keys.
Why can't the schema be changed? You designed the app, didn't you? Even if you didn't, adding the proper keys is just a matter of adding the right indexes and then altering the right columns. #Michael Pakhantosv's answer has what looks to be the right bits of SQL.
Further, it's InnoDB that does foreign keys, not MyISAM. You're fine there already.
If you could change the schema, making the appropriate IDs actual, real Foreign Keys and using ON DELETE CASCADE would work. Or maybe triggers. But that's just asking for it.
Now, for some reason, ON DELETE CASCADE isn't liked very much around here. I disagree with other people's reasons for not liking it, but I don't disagree with their sentiment. Unless your application was designed to grok ON DELETE CASCADE, you're in for a world of trouble.
But, given your requirement...
basically if the base row for the news is deleted I need all the related rows to be deleted
... that's asking for ON DELETE CASCADE.
So, this might come as a shock, but if you can't modify the database, you'll just have to do your work in the code. I'd imagine that deleting a news article happens in only one place in your code, right? If not, it'd better. Fix that first. Then just make sure you delete all the proper rows in an appropriate order. And then document it!
If you can not change the schema then triggers are not an option.
InnoDB supports transactions, so deleting from two tables should not be an issue, what exactly is your problem?
P.S. It would be worth noting which version of the server are you using.
Related
I have a database containing over 1,000 item information and I am now developing a system that will have this check the API source via a regular Cron Job adding new entries as they come. Usually, but not always the case, when a new item is released, it will have limited information, eg; Image and name only, more information like description can sometimes be initially withheld.
With this system, I am creating a bulletin to let everyone know new items have been released, so like most announcements, they get submitted to a database, however instead of submitting static content to the database for the bulletin, is it possible to submit something which will be executed upon the person loading that page and that bulletin data is firstly obtained then the secondary code within run?
, For example, within the database could read something like the following
<p>Today new items were released!</p>
<?php $item_ids = "545, 546, 547, 548"; ?>
And then on the page, it will fetch the latest known information from the other database table for items "545, 546, 547, 548"
Therefore, there would be no need to go back and edit any past entries, this page would stay somewhat up-to-date dynamically.
Typically you would do something like have a date field on your items, so you can show which items were released on a given date. Or if you need to have the items associated with some sort of announcement record, create a lookup table that joins your items and announcements. Do not insert executable code in the DB and then pull it out and execute it.
CREATE TABLE `announcements` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`publish_date` DATETIME NOT NULL,
`content` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `items` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` VARCHAR(128) NOT NULL,
`description` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `announcement_item_lkp` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`announcement_id` int(11) unsigned NOT NULL,
`item_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `announcement_item_lkp_uk1` (`announcement_id`,`item_id`),
KEY `announcement_item_lkp_fk_1` (`announcement_id`),
KEY `announcement_item_lkp_fk_2` (`item_id`),
CONSTRAINT `announcement_item_lkp_fk_1` FOREIGN KEY (`announcement_id`) REFERENCES `announcements` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `announcement_item_lkp_fk_2` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
With the announcement_item_lkp table, you can associate as many items to your announcement as you like. And since you have cascading deletes, if an item gets deletes, its lookup records are deleted as well, so you don't have to worry about orphaned references in your announcements, like you would it you just stuff a string of IDs somewhere.
You're already using a relational database, let it do its job.
Here is my database mapping table definition,
you can try this, when I create this table and add some records in to it, it is not let me edit or delete the records by phpmyadmin although by query it should be possible,
CREATE TABLE IF NOT EXISTS `map2` (
`map_table_a` varchar(25) DEFAULT NULL,
`map_id_a` int(10) DEFAULT NULL,
`map_table_b` varchar(25) DEFAULT NULL,
`map_id_b` int(10) DEFAULT NULL,
KEY `map_table_b` (`map_table_b`,`map_id_b`),
KEY `map_table_a` (`map_table_a`,`map_id_a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I don't know reason behind this behavior
Depending on your phpMyAdmin version, you should see this error message:
While you do have KEY columns, you have no PRIMARY or UNIQUE columns defined. This is why phpMyAdmin cannot edit your data - it has no way to be sure it is editing the correct row.
Suggested solution: Add the following into your table definition, preferably as the first column:
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
I am a newbie to PHP. I have been given the code snippet below as homework:
CREATE TABLE `admin_log` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`statusdate` DATETIME DEFAULT NULL,
`type` INT(11) DEFAULT NULL
PRIMARY KEY (`id`)
) ENGINE=MYISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin
Why will it not be possible to set up any foreign key constraints using this table?
I have done some research on Google and I cant find a reason why foreign key constraints are not possible. Please help
You can do that like this :
CREATE TABLE `admin_log` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`statusdate` DATETIME DEFAULT NULL,
`type` INT(11) DEFAULT NULL,
INDEX (type),
FOREIGN KEY (type)
REFERENCES type(id)
ON UPDATE CASCADE ON DELETE RESTRICT,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=latin
Notice the engine INNODB insetad of MYISAM which doesn't permit foreign key.
Or using MySQLAdmin in the "structure" tab, click on the "relationals view" link below the table description.
Even it has been mentioned on comment before -- just to make it more prominent: MyISAM is not supporting foreign keys. So you will need to change the engine of your table to e.g. INNODB if possible.
I have a mysql table MAINLIST.
CREATE TABLE `MAINLIST` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`NAME` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`email` tinyint(1) unsigned DEFAULT NULL,
`contact` tinyint(1) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Every day I select a subset of these and perform some operations. Right now I do this within the MAINLIST table, but I think it would be helpful for organization, readability and debugging to create a second table daily import the selected records, do the operations and then send the records back to the Mainlist table and destroy the daily table.
What is the best way to do this with mysql, or are there other ways to approach this problem? Perhaps I should not be doing this at all. I am wondering what best practices are since I'm not experienced with Db design. I am using the redbean ORM and php.
Let's say I have a screenshots table and a replies table. So: every screenshot can have multiple replies (one-to-many). Now, I want to create a combined timeline of the two, but in reality, they're pretty unrelated (in structure).
How can I select data from both tables, ordering by their publish time, descending; when for example, I can have a post, a few comments, then another post; because that would be how the timeline happened?
Normally selecting from both combines the tables; but I don't want that to happen. On that subject, I also need the tables to be distinguishable. Here's the structure for the tables...
--
-- Table structure for table `screenshots`
--
CREATE TABLE IF NOT EXISTS `screenshots` (
`id` int(11) NOT NULL auto_increment,
`user` int(11) NOT NULL,
`description` text NOT NULL,
`time` timestamp NOT NULL default CURRENT_TIMESTAMP,
`ext` varchar(4) NOT NULL default 'png',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=14 ;
-- --------------------------------------------------------
--
-- Table structure for table `screenshot_replies`
--
CREATE TABLE IF NOT EXISTS `screenshot_replies` (
`id` int(11) NOT NULL auto_increment,
`user` int(11) NOT NULL,
`parent` int(11) NOT NULL,
`time` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
`text` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=27 ;
Note: I realize this may be a duplicate of this question but I didn't find the solution there working for me.
Thanks in advance! :)
You should use UNION in this case:
(SELECT id, time, 'screenshots' as tableName FROM screenshots)
UNION
(SELECT id, time, 'replies' as tableName FROM screenshot_replies)
ORDER BY time ASC
You can get the tablename of a field by using the mysql_tablename function in php
You can indicate the tablename as a column in the result set
Whenever I come across a problem like this, where you're finding it impossible to do something because you can't built a query which will extract the data, I have to start wondering do you have the right data model?
Start with what data you want to extract, and then build a model which allows that, not the reverse, and you'll find it much easiler in the long term, and also probably faster performance and quite often better flexibility.
Looking at those tables, I'd have to ask why have two of them? What's wrong with:
CREATE TABLE IF NOT EXISTS `screenshots` (
`id` int(11) NOT NULL auto_increment,
`user` int(11) NOT NULL,
`description` text NOT NULL,
`time` timestamp NOT NULL default CURRENT_TIMESTAMP,
`ext` varchar(4) default 'png',
`parent` int(11),
`text` text,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
That would allow you to do what you want, easily telling if it's a screen shot (if ext is set), a reply (if parent is set) or ... since the model now allows it ... a screenshot which is a reply!!!