Timeline from 2 related tables - 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!!!

Related

Creation of a "temporary" table for daily operations?

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.

how to get users information stored in several tables and show in his profile, is it posible with single query?

i have developed an online auction system in which users can sale or buy goods, my problem is with retrieving auctions relative information that are in two separate tables one contains information such as (auction_id,owner,title,description,base_price,..) and the other contains information about requests for each auction: (bid_id,auction_id,bidder,price,date), each user may post several auctions or not, i want to show the highest price and the bidder(some one who gives such price) for that price and number of requests additional to information stored in auction table for each auction
but when i join to table, if there is no request for auction so the result will be zero and you will see the message: there is no information to show but the user has just posted a new auction, what should i do?! should i check if there is a request for each auction and if yes then get these information?! dosent in code duplication? in this way i should connect to db twice in a single request for profile page
here is my tables and current query:
create table `auction`(
`auction_id` INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`owner` VARCHAR(32) NOT NULL,
`group_id` TINYINT UNSIGNED NOT NULL ,
`title` VARCHAR(100) NOT NULL,
`sale_type` VARCHAR(1) NOT NULL,
`base_price` INT NOT NULL,
`min_increase` INT NULL,
`photo` VARCHAR(200) NULL,
`description` VARCHAR(500) NOT NULL,
`start_date` DATETIME NOT NULL,
`termination_date` DATETIME NULL,
`sold` VARCHAR(1) NOT NULL DEFAULT 0,
`purchaser` VARCHAR(32) NULL,
`deleted` VARCHAR(1) NOT NULL DEFAULT 0,
FOREIGN KEY(owner) REFERENCES users(user_name) on delete cascade on update cascade,
FOREIGN KEY(purchaser) REFERENCES users(user_name) on delete cascade on update cascade,
FOREIGN KEY(group_id) REFERENCES commodity_groups(group_id) on delete cascade on update cascade)
ENGINE=InnoDB default charset=utf8;
create table `bid`(
`bid_id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`auction_id` INT UNSIGNED NOT NULL,
`bidder` VARCHAR(32) NOT NULL,
`price` INT NOT NULL,
`date` DATETIME NOT NULL,
`deleted` VARCHAR(1) NOT NULL DEFAULT 0,
FOREIGN KEY(auction_id) REFERENCES auction(auction_id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(bidder) REFERENCES users(user_name) ON DELETE CASCADE ON UPDATE CASCADE)
ENGINE=InnoDB default charset=utf8;
and here is my query i use prepared statements:
SELECT `auction`.`auction_id` , `title` , `base_price` , `min_increase` , `photo` , `description` , `start_date` , `termination_date` , `max_bidder` , `bids_count` , `max_bid`
FROM `auction` , (
SELECT `bid`.`auction_id` , `bidder` AS max_bidder, `bids_count` , `max_bid`
FROM `bid` , (
SELECT `auction_id` , count( bid_id ) AS bids_count, max( price ) AS max_bid
FROM `bid`
WHERE `auction_id`
IN (
SELECT `auction_id`
FROM `auction`
WHERE `owner` = ?
)
GROUP BY (
auction_id
)
) AS temp
WHERE `bid`.`auction_id` = `temp`.`auction_id`
AND `price` = `max_bid`
) AS temp2
WHERE `auction`.`auction_id` = `temp2`.`auction_id`
it is clear that if there is no request for auction, the result will be zero and no auction will be shown to user in his profile, however he or she has just post a new auction, i will thank if any body could help me
What you have is more of a database design problem and a future scalability problem than an actual problem. You know you can make two requests if you want to.
If you care about scaling things up, you're going to have to think very carefully about what user information you want to replicate across multiple servers, and how you're going to synchronize that. The basic answer is: Yes, you use joins to include the user information you want. But a more complicated answer is that you might want to create mini tables with just a little bit of user information (duplicated and synchronized) that you can join very quickly, which no user would ever write to -- in other words they are written only by the master table either through a slave setup or with some cron job.
A lot depends on how large you expect your site to be and how many people might be writing to the users table. It's assumed that many people will be writing to the auction table, so ideally you don't want ANY foreign key dependencies on that table or you will get deadlocks. It should be an ISAM or Federated table, probably.

finding latest entry in DB and all other before it in a different table

This is a fairly easy thing in theory but i havent got a clue how to do it. What i need is a method to find the latest timestamp/entry in a table called 'answers' in an MySQL DB. And upon finding this, to find all IP's stored in a table called 'ip' by timestamp that occur before the said mention 'answers' entry.
Any ideas? Im guessing an if() function would be the best way?
Edit: table structure where 'ip' exists.
CREATE TABLE IF NOT EXISTS `votes` (
`id` int(250) NOT NULL AUTO_INCREMENT,
`ip` varchar(30) NOT NULL,
`answer_id` int(250) NOT NULL,
`poll_id` int(250) NOT NULL,
`timestamp` int(250) NOT NULL, PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
And the table where the 'answers' live
CREATE TABLE IF NOT EXISTS `answers` (
`id` int(250) NOT NULL AUTO_INCREMENT,
`poll_id` int(250) NOT NULL,
`answer` varchar(250) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
Surely this can be a pure database solution an doesn't need anything doing in PHP?
Assuming the tables are in the same database, both have a timestamp column and are named answers and votes respectively, then:
SELECT * FROM votes v
WHERE v.timestamp <= (SELECT max(a.timestamp) FROM answers a)
EDIT : Replaced names according to your schema, but I don't see a timestamp column in answers?
select * from ip x where x.timestamp <= (select max(y.timestamp) from answer y)
To get the latest timestamp you can use...
SELECT timestamp_field FROM answers ORDER BY timestamp_field DESC LIMIT 1
Then run a second query matching the result from that query.

which is the correct way for this database design

I have four tables, namely
countries,states,cities,areas
which will be the best feasible solution for my database table
Method A :
CREATE TABLE IF NOT EXISTS `countries` (
`id` int(11) auto_increment NOT NULL,
`name` varchar(50) NOT NULL,
PRIMARY KEY  (`id`),
UNIQUE(`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `states` (
`id` int(11) auto_increment NOT NULL,
`name` varchar(50) NOT NULL,
`country_id` int(11) NOT NULL,
PRIMARY KEY  (`id`),
UNIQUE(`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `cities` (
`id` int(11) auto_increment NOT NULL,
`name` varchar(50) NOT NULL,
`state_id` int(11) NOT NULL,
PRIMARY KEY  (`id`),
UNIQUE(`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `areas` (
`id` int(11) auto_increment NOT NULL,
`name` varchar(50) NOT NULL,
`zipcode` int(11) NOT NULL,
`city_id` int(11) NOT NULL,
PRIMARY KEY  (`id`),
UNIQUE(`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
or Method B :
CREATE TABLE IF NOT EXISTS `countries` (
`id` int(11) auto_increment NOT NULL,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE(`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `states` (
`id` int(11) auto_increment NOT NULL,
`name` varchar(50) NOT NULL,
`country_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE(`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `cities` (
`id` int(11) auto_increment NOT NULL,
`name` varchar(50) NOT NULL,
`state_id` int(11) NOT NULL,
`country_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE(`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `areas` (
`id` int(11) auto_increment NOT NULL,
`name` varchar(50) NOT NULL,
`zipcode` int(11) NOT NULL,
`city_id` int(11) NOT NULL,
`state_id` int(11) NOT NULL,
`country_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE(`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Thank you..
The first is less likely to have problems with synchronization.
The second will offer better performance through denormalization.
Possible related thread: What is a good way to denormalize a mysql database?
The second version will lead to major headaches if mismatched data is entered. Take the following sample data:
countries: Canada, USA
states: Saskatchewan, Michigan
cities: Saskatoon, Detroit
zipcode: 90210 (california)
insert into area (...) ('Canada', 'Michigan', 'Saskatoon', 90210)
all individually valid, but the entire record is utterly wrong. Yet, by your design, it's supposed to be valid.
It probably depends on what queries you are going to run on those tables. In general, A is normalized whereas B is not (A will use less space).
I would start with Method A, but if it turns out performance requires the additional columns further down the chain, I'd add them only as needed.
Just be sure to make your _id columns indexes.
I prefer Method A at first glance, but without knowing specifics about what you want the relationships and constraints to be, it's impossible to say categorically that one is "better" than the other. Follow your application's functional requirements.
Congratulations on looking towards a normalised approach: it's nice to see!
I personally would choose the first one [Method A]. If you know, for example, the city ID of an area, then you automatically know the state ID and the country ID. While the second one may be a bit more convenient, you may run into issues down the line if say, a city moved to a different state.
It's always best to start with the normalized form. I would only suggest Method B if you had your RDBMS automatically managing cached column updates. For example, if you mistakenly placed Los Angeles in Michigan, you would need to update multiple locations (unless you had triggers that would update cascading pieces of information in denormalized tables). But without triggers, Method A is without a doubt the best form.
This is assuming of course that your constraints match the ones implicitly dictated by common interpretation when viewing Method A's definition.

delete main row and all children mysql and php

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.

Categories