MySQL - how to lock a single row? - mysql

My aim is to create a system to book trips. My system is built in MySQL + PHP. I'm using InnoDB as engine in MySQL.
When an user choose which trip he wants to book, I want the ipothetic page "*.php" to lock one of those trip for some times (ex. 10 minutes) and after this time release it.
Which is the best way to lock the trip in the DB?
Notice that I didn't talk about rows or table because I'm asking for the easiest way to implement this kind of feature and I'm not sure if mines is that one. So I've tried to design a table to contain the trips, and look like this:
CREATE TABLE IF NOT EXISTS Trip (
id INT(1) NOT NULL AUTO_INCREMENT,
descr VARCHAR(50) NOT NULL,
quantity INT(2) NOT NULL DEFAULT 10,
PRIMARY KEY (id)
) Engine=InnoDB

This is your Ticket table
CREATE TABLE IF NOT EXISTS Trip (
id INT(1) NOT NULL AUTO_INCREMENT,
descr VARCHAR(50) NOT NULL,
quantity INT(2) NOT NULL DEFAULT 10,
PRIMARY KEY (id)
) Engine=InnoDB
Let me visualize your user table as this ( just for testing )
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1 ;
Now You need add an extra table where you can track your users booking order ( Example like this )
CREATE TABLE IF NOT EXISTS `trip_track` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`trip_id` int(11) NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1 ;
Now Every time you insert a booking query you need to insert user_id,ticket_id and timestamp ( You can use date or Mysql's default time stamp )
Then whenever user tries to book any ticket use a Query to check the time difference between current time and the timestamp from the database
That's it! .. see? that's simple :)

Related

MySql trigger insert new entry with wrong timestamp

I have an old database with version 5.0.95 (which unfortunately I cannot upgrade).
In this database I have a table clientes_states:
CREATE TABLE `clientes_states` (
`id` int(11) NOT NULL auto_increment,
`client_id` int(11) NOT NULL,
`state_id` int(11) NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1239670 DEFAULT CHARSET=latin1 ROW_FORMAT=FIXED;
For each change of a client state, a new entry is generated. From this reason this table is huge, which makes it complicated to achieve last state of all clients.
In order to access easily the current state of each client, I created the following table, and added a trigger:
CREATE TABLE `actual_clients_states` (
`id` int(11) NOT NULL auto_increment,
`client_id` int(11) NOT NULL,
`state_id` int(11) NOT NULL,
`updated_at` timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `client_id` (`client_id`)
) ENGINE=InnoDB AUTO_INCREMENT=21022 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
CREATE TRIGGER clients_states_insert
AFTER INSERT
ON clientes_states FOR EACH ROW
INSERT INTO actual_clients_states (client_id, state_id)
VALUES (new.client_id, new.state_id)
ON DUPLICATE KEY UPDATE state_id=NEW.state_id;
I have two databases, one for testing and one for staging.
From some reason, the updated_at field is updated with wrong date (almost a month ago) on the staging server, but works fine on the testing server.
Any idea what might be the problem?

What is better in performance for 1-to-1 relationship: a new table or a VARCHAR column with a lot of empty entries

I have to make the decision whether to add a new column (VARCHAR) to an existing table or to create a new table.
If a added a new column there would be empty entries, so I wonder how detrimental this is when the datatype is VARCHAR.
So concretely I have a "messages" table in which I store messages from userA to userB. Every user should have the chance to respond to a given message exactly 1 time so I could add a "response" column with VARCHAR to the existing messages table or I could create a new table "responses" with "index_message" and "response" as columns.
What is better?
EDIT:
CREATE TABLE `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sender` int(10) unsigned NOT NULL,
`receiver` int(10) unsigned NOT NULL,
`message` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL,
`ref` varchar(180) COLLATE utf8_unicode_ci DEFAULT NULL,
`status` tinyint(4) NOT NULL DEFAULT '0',
`type` tinyint(4) NOT NULL,
`multiple` tinyint(4) NOT NULL DEFAULT '0',
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=82 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `responses` (
`msg_id` int(10) unsigned NOT NULL,
`response` varchar(140) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`msg_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
It all depends on your need or process flow.
If you're allowing users to respond only once to a message, then go for 1 table only.
CREATE TABLE `messages` (
columns go here.....
) ENGINE=InnoDB AUTO_INCREMENT=82 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
If you're allowing users to respond more than once to a message, because follow-ups may occur, then go for that multiple table relationship.
CREATE TABLE `messages` (
columns go here...
) ENGINE=InnoDB AUTO_INCREMENT=82 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `responses` (
msg_id, response, sender....
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
But you can also allow your users to respond more than once to a message with just 1 table. You'll just put the msg_id column inside your messages table, so every message and responses are saved in the same table. With that setup, users can reply to messages and responses (just like how emails work, but I'm not saying that they use the same table structures).
CREATE TABLE `messages` (
id, title, to, from, message/response, date, msg_id....
) ENGINE=InnoDB AUTO_INCREMENT=82 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Using two tables and join will litteraly double your query time. When it comes to 1-1 relationship, you should always merge the two tables.
The only downside is the lack of flexibility. Say you decide to allow many replies in the future, you will have to do lot of changes. This is why this kind of optimisation is done at the end of a project.
If you use an ORM (like hibernate), your are able to select if the data should be stored in one or two table without impacting your queries.

Table "Products" with predefined products, user can customize the price. How to avoid data redundancy?

I've been thinking on this problem for fews days and I still can't find a way to do what I want.
Below is how my database is currently designed (it's where I'm stuck) :
This is what I want :
a User can create multiple PriceSheets. A User can give a PriceSheet any name he wants. There are two PriceSheets types : "Lab Fulfillment", or "Self Fulfillment".
if the User chooses "Lab Fulfillment", he can import all or part of the Products of one of the predefined Labs. (I rephrase : there are few Labs that come with a predefined list of Products). The User will only be able to customize the price. He can't add custom products to this PriceSheet.
if the User chooses "Self Fulfillment", he can add his own products, and can personalize each field (name, cost, price, dimension_h, dimension_l).
I don't know how to link the tables between them. If I put the predefined Products in the Products table and set a Many-to-Many relationship between PriceSheets and Product, the default price of a predefined Product will be overwritten when a User customizes it, which is not what I want.
Also, I want the default values of my predefined Products to be only once in my database. If 100 users uses the predefined Products, I don't want the default cost to be in my database 100 times.
Don't hesitate to ask for precisions, I had trouble making this question clear and I think it's still not totaly clear.
Thanks in advance for your help
OK, database normalization 101. Lots of ways to do this, would take me a day to really optimize all this, this should help:
User
Lab
Product
id name cost dimension .....
1 a
2 b
3 c
4 d
So those three tables are fine. All your products will go in the Product table. No foreign keys in any of those tables.
PriceSheet
user_id custom_price product_id type
1 1.99 1 lab-fulfillment
0 NULL 2 self-fulfillment
1 5.99 3 lab-fulfillment
So a user can have as many price sheets as they want, and they can only adjust the price of a product. This can actually be normalized further if you so wish:
PriceSheet (composite key on id, user_id, FK user_id)
id user_id
0 0
1 1
2 1
LabPriceSheet (you could add an id, might be better, or you could use a composite key, stricter)
PriceSheet_id custom_price lab_product_id
0 1.99 0
2 5.99 1
CustomPriceSheet
PriceSheet_id custom_product_id
1 0
With foreign keys as appropriate. This now makes MySQL restrict the custom_price, rather than in PHP (although you would still have to deal with ensuring correct INSERT!).
Now, to deal with who adds the products:
CustomProduct
id user_id product_id timestamp
0 3 2 ...
LabProduct
id lab_id product_id timestamp
0 0 1 ...
1 0 3 ...
So let's double check:
This is what I want :
a User can create multiple PriceSheets. check A User can give a PriceSheet
any name he wants. check There are two PriceSheets types : "Lab
Fulfillment", or "Self Fulfillment". check
if the User chooses "Lab Fulfillment", he can import all or part of the Products of one of the predefined Labs. (I rephrase : there are few Labs that come with a predefined list of Products). The User will only be able to customize the price. He can't add custom products to this PriceSheet.
Yup, because he would create a LabPriceSheet that can only add lab_product_id. Custom price is there too, that overrides the default price in product table.
if the User chooses "Self Fulfillment", he can add his own products, and can personalize each field (name, cost, price, dimension_h, dimension_l).
Yup, he would add a product (you would need to check if a similar one exists, else return the id of the existing product in the product table), and then that would also be an entry in CustomProduct.
I don't know how to link the tables between them. If I put the predefined Products in the Products table and set a Many-to-Many relationship between PriceSheets and Product, the default price of a predefined Product will be overwritten when a User customizes it, which is not what I want.
Yeah that won't happen :) Never (very very rarely) implement many-many rels.
Also, I want the default values of my predefined Products to be only
once in my database. If 100 users uses the predefined Products, I
don't want the default cost to be in my database 100 times.
Of course.
Let me know if you want the MySQL code, I assume you're good! Remember to use InnoDB and properly configure your MySQL configuration!
EDIT
I felt like helping you out with a copy and paste thing. I like copy and paste things. Also, there's a redundant user_id column in the blurb above which I fixed in an earlier edit.
SET GLOBAL innodb_file_per_table = 1;
SET GLOBAL general_log = 'OFF';
SET FOREIGN_KEY_CHECKS=1;
SET GLOBAL character_set_server = utf8mb4;
SET NAMES utf8mb4;
CREATE DATABASE SO; USE SO;
ALTER DATABASE SO CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
CREATE TABLE `User` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`email` VARCHAR(555) NOT NULL,
`password` VARBINARY(200) NOT NULL,
`username` VARCHAR(100) NOT NULL,
`role` INT(2) NOT NULL,
`active` TINYINT(1) NOT NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` DATETIME ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `Lab` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(1000) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `Product` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(1000) NOT NULL,
`password` VARBINARY(200) NOT NULL,
`cost` DECIMAL(10, 2) NOT NULL,
`price` DECIMAL(10, 2) NOT NULL,
`height` DECIMAL(15, 5) NOT NULL,
`length` DECIMAL(15, 5) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `CustomProduct` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`user` BIGINT(20) UNSIGNED NOT NULL,
`product` BIGINT(20) UNSIGNED NOT NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`user`) REFERENCES `User`(`id`),
FOREIGN KEY (`product`) REFERENCES `Product`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `LabProduct` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`lab` BIGINT(20) UNSIGNED NOT NULL,
`product` BIGINT(20) UNSIGNED NOT NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`lab`) REFERENCES `Lab`(`id`),
FOREIGN KEY (`product`) REFERENCES `Product`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `PriceSheet` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(1000) NOT NULL,
`user` BIGINT(20) UNSIGNED NOT NULL,
PRIMARY KEY (`id`,`user`),
FOREIGN KEY (`user`) REFERENCES `User`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `LabPriceSheet` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`price_sheet` BIGINT(20) UNSIGNED NOT NULL,
`lab_product` BIGINT(20) UNSIGNED NOT NULL,
`custom_price` DECIMAL(10, 2) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`price_sheet`) REFERENCES `PriceSheet`(`id`),
FOREIGN KEY (`lab_product`) REFERENCES `LabProduct`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `CustomPriceSheet` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`price_sheet` BIGINT(20) UNSIGNED NOT NULL,
`custom_product` BIGINT(20) UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`price_sheet`) REFERENCES `PriceSheet`(`id`),
FOREIGN KEY (`custom_product`) REFERENCES `CustomProduct`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

MySQL Unique values for 2 or more dates

I'm using a barcode scanner to scan qr codes from tickets for an event, this will be posted to a database with the following structure:
CREATE TABLE IF NOT EXISTS `attendees` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`attendeeguid` varchar(32) NOT NULL,
`scanned` int(1) NOT NULL DEFAULT '0',
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `attendeeguid` (`attendeeguid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=15 ;
Now this attendeeguid has to be unique, but if an event is 2 or more days, it should be possible to scan this attendeeguid again on another date. How can I do this?
I'm using MySQL and I'm using this in an Objective-C iOS App.

MySQL query: pull all the other categories minus the one that has been saved

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