I'm looking at storing customer details upon registering for my service, the service in question is a booking system, each user needs to have his/her own calender system which keeps records of all bookings (arrival data/time, name , price etc) i can envision a way of storing all this unique user information in a single table linked by only userID?
CREATE TABLE IF NOT EXISTS `bookings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`room_number` varchar(50) NOT NULL,
`name` varchar(20) NOT NULL,
`arrival` date NOT NULL,
`depart` date NOT NULL,
`nights` int(11) NOT NULL,
`price` decimal(11,2) NOT NULL,
`date_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)
Each user would need to store this information? surely i would need to create a whole new table for each user? (Which i know is just plain slow and wrong).
You don't want to store a separate table for each user (except under some very specific requirements which are rather unusual). Your table is missing a userId. Something like:
CREATE TABLE IF NOT EXISTS `bookings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`UserId` int(11) NOT NULL,
`room_number` varchar(50) NOT NULL,
`name` varchar(20) NOT NULL,
`arrival` date NOT NULL,
`depart` date NOT NULL,
`nights` int(11) NOT NULL,
`price` decimal(11,2) NOT NULL,
`date_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (UserId) references users(UserId)
);
Don't worry about the number of rows in the table. SQL is designed to handle millions of rows for most applications. In fact, splitting the data among multiple tables would introduce some major problems with performance (notably partially filled pages) that could greatly reduce performance.
Related
We are building a system with concept of Admin and Employee. so basically Admin is an employee with all powers and can view all the data created by other Employee.
CREATE TABLE `Vendor` (
`vendor_Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(40) NOT NULL,
`email_Id` varchar(40) DEFAULT NULL,
`landline_Number` varchar(15) DEFAULT NULL,
`mobile_Number` varchar(15) DEFAULT NULL,
`address_Line1` varchar(65) NOT NULL,
`address_Line2` varchar(65) DEFAULT NULL,
`city` varchar(255) NOT NULL,
`pincode` int(6) NOT NULL,
`country` varchar(255) NOT NULL,
PRIMARY KEY (`vendor_Id`),
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1
CREATE TABLE `Employee` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`vendor_Id` int(10) unsigned DEFAULT NULL,
`name` varchar(40) NOT NULL,
`username` varchar(40) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`role` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `employee_username_unique` (`username`),
KEY `employee_vendor_id_foreign` (`vendor_Id`),
CONSTRAINT `employee_vendor_id_foreign` FOREIGN KEY (`vendor_Id`) REFERENCES `Vendor` (`vendor_Id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1
CREATE TABLE `Action` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`emp_Id` int(10) unsigned DEFAULT NULL,
`name` varchar(60) NOT NULL,
`assigned_To` varchar(40) DEFAULT NULL,
`deadline` datetime(3) NOT NULL,
`notes` varchar(400) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `action_emp_id_foreign` (`emp_Id`),
CONSTRAINT `action_emp_id_foreign` FOREIGN KEY (`emp_Id`) REFERENCES `Employee` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1
There are other tables Roles and EmployeeRoles which I thought were not required here.
Approach 1 : Now, when the Admin logs in to see all the Actions created by everyone
we first need to query the Employee table to find all the employees of that Vendor (we will have the Vendor_Id stored in the session when the admin/employee logs in)
Then query the Action table with where in employee_Id array from Step 1
Is this a good approach ?
Approach 2 : or in Action table, I shall store Vendor_Id for each record (mainly all this effort only so that when Admin logs in I can easily retrieve all the records for that Vendor. When Admin logs in from the session I can easily find the Vendor_Id and query the Action table.
I don't know at this moment which would be a better approach. Any suggestions ?
Like Action, there are other 3 tables where similar concept needs to be applied.
Edit 1: There can be a case where we can have multiple vendors registered under a Single brand (future extension) and the Super-Admin would like to analyze the data across multiple branches.
First approach is the basic normalisation approach. As you are putting vendor_id into session, you can also put employee array (having emp_ids belonging to that vendor) into session or cache. Here you would not have to query again and again as it will be refreshed when session or cache expires.
Second solution is the denormalised one. Here you would run into problems based on consistency. On updation of each vendor_id-emp_id mapping you need to update your action table too.
So you have to compare the volume of write queries to read queries. If read queries are too high then go with second. But I suppose there will be only 1-2 admins in an organisation of small size. I would go with Ist until I run into some serious performance issues.
You can stick to solution 1. With an index on the Vendor id in the employee table you should be good with an inner join with actions table.(Unless you are planning to have millions of rows in the tables and looking towards having of performance at single digit milliseconds level)
I have a little problem with regards to my SQL query. I have a transactions table where all product purchases, orders, and returns are saved. When a user enters a purchase, I save an entry into that table with a positive quantity. When a user enters an order, I save an entry to that table with a negative quantity. When a user enters a product return, I also save a positive quantity in it. When I need to know the current inventory I just sum up all the entries of the product.
What I need to know is if a returned product still in the inventory or not. I need to know this so that I can inform the user that there is a returned good stock.
Here is the structure of my table:
CREATE TABLE `product_history` (
`lineno` int(11) NOT NULL AUTO_INCREMENT,
`profileno` int(7) NOT NULL,
`detailno` int(9) NOT NULL,
`profiledate` date DEFAULT NULL,
`productid` int(5) DEFAULT NULL,
`unitid` int(3) DEFAULT NULL,
`lotnumber` varchar(20) DEFAULT NULL,
`quantity` int(6) DEFAULT NULL,
`free` int(4) DEFAULT NULL,
`expiry` char(20) DEFAULT NULL,
`transactiontype` enum('O','P','PC','RT') DEFAULT NULL,
`status` enum('active','inactive') DEFAULT NULL,
PRIMARY KEY (`lineno`),
KEY `update_index` (`profileno`,`profiledate`,`transactiontype`)
) ENGINE=InnoDB AUTO_INCREMENT=5497 DEFAULT CHARSET=latin1;
Here is what I currently have:
Archive tables (one for each year, 2008-2011) and 4 newly created tables for 2012 broken into quarters. All of these tables, including the new one, have the same structure and keys. The naming convention for these is ARCHIVE_PLAYS. I then have a "live" table (Called PLAYS) for current data. I have a merged table that combines all tables so that I can run reports. The issue I have, which I didn't have before, is that this merged table is showing duplicates. They have the same Primary keys so this shouldn't be the case, right? It must have something to do with the new tables I just created as I didn't have this issue before.
Structure:
**COMPANY**
COMPANY.MERGED_PLAYS
COMPANY.ARCHIVE_PLAYS_2008
COMPANY.ARCHIVE_PLAYS_2009
COMPANY.ARCHIVE_PLAYS_2010
COMPANY.ARCHIVE_PLAYS_2011
COMPANY.ARCHIVE_PLAYS_2012Q1
COMPANY.ARCHIVE_PLAYS_2012Q2
COMPANY.ARCHIVE_PLAYS_2012Q3
COMPANY.ARCHIVE_PLAYS_2012Q4
**COMPANY2**
COMPANY2.PLAYS
Each table, with the exception of the Merged_Plays, has the following Create:
CREATE TABLE `ARCHIVE_PLAYS_2011` (
`ENTRY_ID` BIGINT(20) NOT NULL,
`NODE_ID` VARCHAR(48) NOT NULL,
`HW_ID` VARBINARY(64) NOT NULL,
`LOG_DAY` DATE NOT NULL,
`ROW_NUMBER` INT(11) NOT NULL,
`NODE_NAME` VARCHAR(128) NOT NULL,
`FILE_NAME` VARCHAR(1024) NOT NULL,
`PRESENTATION_NAME` VARCHAR(1024) NULL DEFAULT NULL,
`SMIL_SEQUENCE_ID` VARCHAR(256) NULL DEFAULT NULL,
`SMIL_CONTENT_ID` VARCHAR(256) NULL DEFAULT NULL,
`PLAY_TIME_MS` BIGINT(20) NOT NULL,
`PLAY_TIME` TIME NOT NULL,
`STATUS_CODE` VARCHAR(48) NULL DEFAULT NULL,
`NUM_SCREENS_CONNECTED_AND_ON` INT(11) NULL DEFAULT NULL,
`NUM_SPEAKERS_CONNECTED_AND_ON` INT(11) NULL DEFAULT NULL,
`SCREEN_LAYOUT_MATCHES` CHAR(1) NULL DEFAULT NULL,
`ENTRY_PROCESSED` CHAR(1) NULL DEFAULT NULL,
`FILE_PATH` VARCHAR(1024) NULL DEFAULT NULL,
PRIMARY KEY (`NODE_ID`, `LOG_DAY`, `ROW_NUMBER`),
INDEX `PLAYLOG_ENTRY_ID` (`ENTRY_ID`),
INDEX `PLAYLOG_LOG_DAY` (`LOG_DAY`),
INDEX `PLAYLOG_LOG_DAY_PLAY_TIME` (`LOG_DAY`, `PLAY_TIME`),
INDEX `PLAYLOG_FILE_NAME` (`FILE_NAME`(600)),
INDEX `PLAYLOG_NODE_NAME` (`NODE_NAME`),
INDEX `PLAYLOG_FILE_NAME_NODE_NAME` (`FILE_NAME`(600), `NODE_NAME`),
INDEX `PLAYLOG_ENTRY_ID_PROCESSED` (`ENTRY_ID`, `ENTRY_PROCESSED`)
)
COLLATE='latin1_swedish_ci'
ENGINE=MyISAM;
A primary key only assures unique data within a single table. You must have duplicate records across multiple tables. Make sure you have deleted all of the 2012 data from the live table. Make sure there are no dups between any of the quarter tables.
Also if the records are 100% dups, if you do a UNION between all of your tables (instead of UNION ALL) you will get unique results, however this will decrease query performance.
I'm not very good at MySQL and i'm going to write a query to count messages sent by an user, based on its type and is_auto field.
Messages can be of type "small text message" or "newsletter". I created two entities with a few fields that differs between them. The important one is messages_count that is absent in table newsletter and it's used in the query:
CREATE TABLE IF NOT EXISTS `small_text_message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`messages_count` int(11) NOT NULL,
`username` varchar(255) NOT NULL,
`method` varchar(255) NOT NULL,
`content` longtext,
`sent_at` datetime DEFAULT NULL,
`status` varchar(255) NOT NULL,
`recipients_count` int(11) NOT NULL,
`customers_count` int(11) NOT NULL,
`sheduled_at` datetime DEFAULT NULL,
`sheduled_for` datetime DEFAULT NULL,
`is_auto` tinyint(1) NOT NULL,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
And:
CREATE TABLE `newsletter` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subject` varchar(78) DEFAULT NULL,
`content` longtext,
`sent_at` datetime DEFAULT NULL,
`status` varchar(255) NOT NULL,
`recipients_count` int(11) NOT NULL,
`customers_count` int(11) NOT NULL,
`sheduled_at` datetime DEFAULT NULL,
`sheduled_for` datetime DEFAULT NULL,
`is_auto` tinyint(1) NOT NULL,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
I ended up with a UNION query. Can this query be shortened or optimized since the only difference is messages_count that should be always 1 for newsletter?
SELECT
CONCAT('sms_', IF(is_auto = 0, 'user' , 'auto')) AS subtype,
SUM(messages_count * (customers_count + recipients_count)) AS count
FROM small_text_message WHERE status <> 'pending' AND user_id = 1
GROUP BY is_auto
UNION
SELECT
CONCAT('newsletter_', IF(is_auto = 0, 'user' , 'auto')) AS subtype,
SUM(customers_count + recipients_count) AS count
FROM newsletter WHERE status <> 'pending' AND user_id = 1
GROUP BY is_auto
I don't see any easy way to avoid a UNION (or UNION ALL) operation, that will return the specified result set.
I would recommend you use a UNION ALL operator in place of the UNION operator. Then the execution plan will not include the step that eliminates duplicate rows. (You already have GROUP BY operations on each query, and there is no way that those two queries can produce an identical row.)
Otherwise, your query looks fine just as it is written.
(It's always a good thing to consider the question, might there be a better way? To get the result set you are asking for, from the schema you have, your query looks about as good as it's going to get.)
If you are looking for more general DB advice, I recommend restructuring the tables to factor the common elements into one table, perhaps called outbound_communication or something, with all of your common fields, then perhaps have "sub tables" for the specific types to host the fields which are unique to that type. It does mean a simple JOIN is necessary to select all of the fields you want, but the again, it's normalized and actually makes situations like this one easier (one table holds all of the entities of interest). Additionally, you have the option of writing that JOIN just once as a "view", and then your existing code would not even need to change to see the two tables as if they never changed.
CREATE TABLE IF NOT EXISTS `outbound_communicaton` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`content` longtext,
`sent_at` datetime DEFAULT NULL,
`status` varchar(255) NOT NULL,
`recipients_count` int(11) NOT NULL,
`customers_count` int(11) NOT NULL,
`sheduled_at` datetime DEFAULT NULL,
`sheduled_for` datetime DEFAULT NULL,
`is_auto` tinyint(1) NOT NULL,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `small_text_message` (
`oubound_communication_id` int(11) NOT NULL,
`messages_count` int(11) NOT NULL,
`username` varchar(255) NOT NULL,
`method` varchar(255) NOT NULL,
PRIMARY KEY (`outbound_communication_id`),
FOREIGN KEY (outbound_communication_id)
REFERENCES outbound_communicaton(id)
) ENGINE=InnoDB;
CREATE TABLE `newsletter` (
`oubound_communication_id` int(11) NOT NULL,
`subject` varchar(78) DEFAULT NULL,
PRIMARY KEY (`outbound_communication_id`),
FOREIGN KEY (outbound_communication_id)
REFERENCES outbound_communicaton(id)
) ENGINE=InnoDB;
Then selecting a text msg is like this:
SELECT *
FROM outbound_communication AS parent
JOIN small_text_message
ON parent.id = small_text_message.outbound_communication_id
WHERE parent.id = 1234;
The nature of the query is inherently the union of the data from the small text message and the newsletter tables, so the UNION query is the only realistic formulation. There's no join of relevance between the two tables, for example.
So, I think you're very much on the right lines with your query.
Why are you worried about a UNION?
I really need some help with forming a MySQL query that I just cannot work out. On my website I have a system in place that will hopefully remember some selections that user made when they last visited the site.
On the site the user can select which category they wish to read the content of next time they come to site. That setting will be remembered but the menu should be displayed slightly different. It should show all the other categories minus the one that has been saved.
So if I have these categories,
Blog
Inspiration
Case Studies
and the user saved Blog, the next time they come to the site the categories list should just be
Inspiration
Case Studies.
How can this data be pulled from the database?
Currently I have a table that identifies the user via a unique cookie id:
CREATE TABLE IF NOT EXISTS `cookieTable` (
`cookieEntryId` int(11) NOT NULL AUTO_INCREMENT,
`cookieId` varchar(32) NOT NULL,
`expirationDate` int(10) NOT NULL,
PRIMARY KEY (`cookieEntryId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
I have a category table
CREATE TABLE IF NOT EXISTS `categoryTable` (
`categoryId` int(11) NOT NULL AUTO_INCREMENT,
`categoryTitle` varchar(25) NOT NULL,
`categoryAbstract` varchar(150) NOT NULL,
`categorySlug` varchar(25) NOT NULL,
`categoryIsSpecial` int(1) DEFAULT NULL,
`categoryOnline` int(1) DEFAULT NULL,
`dashboardUserId` int(11) NOT NULL,
`categoryDateCreated` int(10) NOT NULL,
PRIMARY KEY (`categoryId`),
KEY `dashboardUserId` (`dashboardUserId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
And I have the table that saves what categories the user has saved,
CREATE TABLE IF NOT EXISTS `userMenuTable` (
`menuEntryId` int(11) NOT NULL AUTO_INCREMENT,
`categoryId` int(11) NOT NULL,
`cookieId` varchar(32) NOT NULL,
PRIMARY KEY (`menuEntryId`),
KEY `categoryId` (`categoryId`,`cookieId`),
KEY `cookieId` (`cookieId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6;
The following query should get the categories the user hasn't saved assuming the cookieId stays constant for a user. If it doesn't you should put a userId into the userMenuTable instead. Just replace USERSCOOKIEID with their actual cookie ID.
SELECT * FROM categoryTable WHERE categoryId not in
(SELECT categoryId FROM userMenuTable WHERE cookieId = 'USERSCOOKIEID') as x