How do i store repetition of item in table? - mysql

I have three tables
user table
CREATE TABLE IF NOT EXISTS `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(25) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8_unicode_ci;
item table
CREATE TABLE IF NOT EXISTS `item` (
`item_id` int(11) NOT NULL AUTO_INCREMENT,
`item` varchar(20) NOT NULL,
`price` int(3) DEFAULT NULL,
`likes` int(4) DEFAULT NULL,
PRIMARY KEY (`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8_unicode_ci;
order table
CREATE TABLE IF NOT EXISTS `order` (
`user_id` int(11) DEFAULT NULL,
`order_no` varchar(50) DEFAULT NULL,
`item1` varchar(5) DEFAULT NULL,
`item2` varchar(5) DEFAULT NULL,
`item3` varchar(5) DEFAULT NULL,
`item4` varchar(5) DEFAULT NULL,
`item5` varchar(5) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8_unicode_ci;
I store item_id in item1 or item2 so on column in order table. i give only 5 items can be select by user but they can increase quantity of any item like any user order two quantity of item1 how do i store this on database .
and any suggestion for improvement in tables .

The best way to allow an unlimited number of items per order is to use another table:
CREATE TABLE IF NOT EXISTS `orderitem` (
`order_no` varchar(50),
`itemid` INT,
`qty` INT NOT NULL DEFAULT 1,
`price` NUMERIC(9,2) NOT NULL,
PRIMARY KEY (order_no, itemid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8_unicode_ci;
Remove the five item columns from your order table.
As the user adds items, INSERT to the table above. You therefore have no limit to the number of items for each order.
This design is necessary whenever you have a many-to-many relationship. It's not a good idea to use five columns, that creates a limitation that is inconvenient (as you know).
If you want to allow the user to increase the quantity of a given line item, add a qty column to this table. When they choose to add more quantity of the same item they had chosen before, increase the qty.
You will also need a price column in this table. You need to record the price paid for the item during the current order, because the price could change tomorrow.
Re your comment:
INSERT INTO orderitem (order_no, itemid, qty, price)
VALUES (1234, 42, 3, 19.95)
ON DUPLICATE KEY UPDATE qty = qty + VALUES(qty), price = VALUES(price);
Read the documentation about http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html to understand what this does.

One suggestion would be not to define your own id field, unless you have a very specific reason for it. Most databases can keep track of that for you. If you want to define your own you should probably add a unique constraint, and/or auto increment it.
I might not understand the question but it sounds like you want to be able to change the values in your items? I'm not quite sure what you're using but here's sql documentation

Related

Concurrent writes to DB with conditional unique

I am using spring-boot, mysql and JDBC in my application.
I have a table which is like below
CREATE TABLE `post` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`ref` varchar(255) DEFAULT '',
`userId` bigint(20) NOT NULL,
`text` varchar(255),
`count` bigint(20) NOT NULL,
`version` bigint(11) DEFAULT NULL
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
In this table, I have a column count which depends on the combination of unique ref, userId and text columns eg. if the combination of these columns is not present in DB then the count will be 1 but if the combination already exists in DB, the count value will be 0.
I run into the problem, when the two or more users are trying to post with same ref, userId and text at the same time. Out of these request only one should get count as one and other ones as zero.
How can I handle this case when multiple users are trying to post with same values?

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;

simple select query optimise

I have a table defined as follows:
CREATE TABLE IF NOT EXISTS `cards` (
`ID` int(11) NOT NULL,
`Name` varchar(200) NOT NULL,
`WorkerID` varchar(20) NOT NULL,
`pic` varchar(200) NOT NULL,
`expDate` bigint(20) NOT NULL,
`reminderSent` tinyint(4) NOT NULL,
`regNum` varchar(8) NOT NULL,
`cardType` varchar(200) NOT NULL
) ENGINE=MyISAM AUTO_INCREMENT=92 DEFAULT CHARSET=latin1;
ALTER TABLE `cards`
ADD PRIMARY KEY (`ID`), ADD KEY `cardsWorkerID_idx` (`WorkerID`);
But running:
explain
SELECT pic, expDate, Name, ID, cardType, regNum FROM cards WHERE workerID= 18
tells me it is scanning the entire table, even though I added an index to the workerID field. Can anyone explain what I'm missing?
The use of indexes depends on the size of the data. It also depends on the types used for the comparison. If you have a small table, then the SQL engine might decide that a scan is more efficient than using the index. This is particularly true if the table fits on a single data page.
In your case, though, the problem is might be data conversion. Use the appropriate typed constant for the comparison:
SELECT pic, expDate, Name, ID, cardType, regNum
FROM cards
WHERE workerID = '18';

How do I structure a Database with a Sales Table MySql

I have a sales table that I want to record all the sales done by the employee.
The problem i'm having is that I can only store one ProductId that comes from a Products table. What is wrong is that a sale has multiples products and with my current structure I can only store one ProductId. I know my approach is wrong but I just don't know how to properly fix it. The question I have is how do I store multiples products in the Sales Table.
This is my Sales Table columns.
CREATE TABLE `Sales` (
`SaleId` int(11) unsigned NOT NULL AUTO_INCREMENT,
`EmployeeId` int(11) unsigned NOT NULL,
`ProductId` int(11) unsigned NOT NULL,
PRIMARY KEY (`SaleId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Your table and approach is ok.
You just have to insert one row in your sales table per product-id.
The unique key for the sales is the SaleId,
the foreign keys for Employee and Product are also there.
The only thing that's missing in your table is the sale quantity and amount.
It's the typical n:m-issue which is solved by the V-structure.
You need a second table, a Sales-Product table:
CREATE TABLE `SaleProduct` (
`SaleId` int(11) unsigned NOT NULL,
`ProductId` int(11) unsigned NOT NULL,
`Quantity` int(11) unsigned NOT NULL,
PRIMARY KEY (`SaleId`, `ProductId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
and change your Sales Table:
CREATE TABLE `Sales` (
`SaleId` int(11) unsigned NOT NULL AUTO_INCREMENT,
`EmployeeId` int(11) unsigned NOT NULL,
PRIMARY KEY (`SaleId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
The second way I suggest is more "standard" but since I have no idea how do you query the table and the size of it, the first method might(?) be useful
concatenate all prod_id into a single string, get them using regexp
example: prod_id = 13_07_255_23_11
SELECT * FROM Sales WHERE ProductId REGEXP '(^|)23(|$)';
split a single sale into multiple entries, have sales_id and product_id be your combined unique/primary key

Storing customer specific details in MYSQL? Without new tables

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.