Database Design - Catalogue - Range - Product - mysql

Can anyone suggest a database design for the following:
A user can make a catalogue
Within a catalogue a user can make a range - i.e. a range of products
Within a range a user can add multiple products
Within a range a user can add multiple ranges -> range->range->range all with products in them.
I currently have in my database -
catalogue_range with - id, name, description
and
catalogue_product with - id, range_id, name, description
can anyone see what I'm trying to produce?
My aim is to be able to make multiple catalogue ranges within a catalogue range and add multiple products to each of these catalogue ranges.
Here is my current SQL:
`catalogue_range` (
`id` char(40) NOT NULL,
`profile_id` char(40) NOT NULL,
`type` enum('pdf','db') DEFAULT NULL,
`status` enum('new','draft','live') NOT NULL,
`name` varchar(64) NOT NULL,
`description` varchar(1000) NOT NULL,
`updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `profile_id` (`profile_id`)
)
`catalogue_product` (
`id` char(40) NOT NULL,
`catalogue_id` char(40) NOT NULL,
`order` smallint(5) unsigned NOT NULL,
`name` varchar(50) NOT NULL,
`description` varchar(250) NOT NULL,
PRIMARY KEY (`id`),
KEY `catalogue_id` (`catalogue_id`)
)
Thanks in advance.

catalogue(catalogue id, your private attributes)
product(product id, #catalogue id, your private attributes)
range(range id, #range id parent, your private attributes)
product range(#product id, #range id)
You will need stored procedures/applicative algorithms to compile:
the list of product of a range (to calculate recursive sqls mysql doesn't offer analytic functions as oracle does)
the list of ranges of a catalogue/range
Hope it helps.
S.

Assuming that a product can only exist in one catalogue at a time, your design is almost alright as it is. What you are missing is a recursive foreign key on catalogue_range. Add something like the following to your catalogue_range table definition:
`parent_range_id` char(40) NULL,
FOREIGN KEY (`parent_range_id`) REFERENCES catalogue_range(`id`)
The top level range(s) for any given user will have a NULL parent_range_id, others will refer to the containing range. Note that hierarchies aren't necessarily easy to work with in SQL. You may also want to look into techniques for making hierarchical data more SQL-friendly, such as nested sets.

Related

How to generate a unique UID from user's email in RESTful API?

I am creating WebService for a website, in which I have to generate the UID from the user's email/name. And this process should be at the user signup step only. Like the way, we have our twitter unique ID. I have a few questions in my mind:
It should be the responsibility of the client or the WebService?
I think it should be of WebService.
If webservice is responsible, then what should be the logic for generating a UID from myname#example.com.
One solution could be to extract the myname from email and append the user_id to its last. But for auto-generated user_id(MYSQL), this cannot be a solution. Also, the whole idea of using UID is to hide the user_id(integer) from URL in the browser, so this solution will again expose the user_id.
Another solution could be to append some random numbers at the end of myname and if ConstraintViolation occurs, then try with some other number. But this will take a hell lot of time only for the user signup operation.
What is the ideal and efficient way to handle this requirement?
This is my MySql table schema:
CREATE TABLE IF NOT EXISTS `user` (
`user_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`unique_id` VARCHAR(45) NOT NULL,
`email` VARCHAR(45) NOT NULL,
`password` VARCHAR(500) NULL,
`first_name` VARCHAR(45) NOT NULL,
`last_name` VARCHAR(45) NOT NULL,
`created_on` DATETIME NOT NULL,
`gender` VARCHAR(2) NULL COMMENT 'M - male\nF - female\nO - other',
UNIQUE INDEX `unique_id_UNIQUE` (`unique_id` ASC),
UNIQUE INDEX `email_UNIQUE` (`email` ASC),
PRIMARY KEY (`user_id`))
ENGINE = InnoDB;
You can use php uniqid function to create user's unique id and you can save it in your user tabl's column unique_id. It will be unique
You can use below code to generate some unique ids.
<?php
echo uniqid("username");
?>

update table takes long time in mysql?

CREATE TABLE fa (
book varchar(100) DEFAULT NULL,
PRODUCTION varchar(1000) DEFAULT NULL,
VENDOR_LEVEL varchar(100) DEFAULT NULL,
BOOK_NO int(10) DEFAULT NULL,
UNSTABLE_TIME_PERIOD varchar(100) DEFAULT NULL,
`PERIOD_YEAR` int(10) DEFAULT NULL,
promo_3_visuals_manual_drag int(10) DEFAULT NULL,
BOOK_NO int(10) DEFAULT NULL,
PRODUCT_LEVEL_DIST varchar(100) DEFAULT NULL,
PRODUCT_LEVEL_ACV_TREND varchar(100) DEFAULT NULL,
KEY book (BOOK_NO),
KEY period (PERIOD_YEAR)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Index we added to column
Index : BOOK_NO and PERIODIC_YEAR has added
we cant add unique nor primary key to both column as it has plenty of duplicate values in it.
There are 46 millions rows.
We tried partitioning to period year and catno for sub partition, but doesn't worked as it is still takes long time
When i run the update query :
update fa set UNSTABLE_TIME_PERIOD = NULL where BOOK_NO = 0 and periodic_year = 201502;
It taking me more than 7 min , how can i OPTIMIZE the query?
Instead of creating 2 different keys, create single composite key for both the columns like:
KEY book_period (BOOK_NO, PERIOD_YEAR)
Also, first filter the records based on the column which will return the small set of records as compare to other.
If you think BOOK_NO will return less number of records as compare to PERIOD_YEAR, Use BOOK_NO first in where clause else use PERIOD_YEAR first and create the key accordingly.
As Álvaro González said, you should use some sort of key (eg. a Primary Key).
Adding a Primary Key:
CREATE TABLE fa (
<your_id>,
{...},
PRIMARY KEY(<your_id>),
{...}
)
or
CREATE TABLE fa (
<your_id> PRIMARY KEY,
{...}
)
It'd be a good idea to make your PRIMARY KEY AUTO_INCREMENT too for convenience, but this is not essenitial.

Best table/index structure for data with time-varying association

My company uses a number of data acquisition devices (DAQs) to monitor the output of solar panels on a test site. Each DAQ has a unique serial number, and each solar panel has a unique serial number. Occasionally we swap out the panels for new panels, and occasionally the DAQs fail and need to be replaced with new ones with different serial numbers.
My question is, what is the best table structure for queries to see all of the data for a particular solar panel's serial number, given that it can be on different DAQs at different times?
I'm currently using the following table structure:
Table: relationships
id int(11) NOT NULL AUTO_INCREMENT,
daqID char(4) NOT NULL,
dtdateFirst datetime NOT NULL,
dtdateLast datetime NOT NULL,
PanelType varchar(20) NOT NULL,
sgcucode varchar(45) NOT NULL,
serial varchar(15) NOT NULL,
ptype varchar(15) NOT NULL,
PRIMARY KEY (id),
KEY daqID (daqID),
KEY gcuidx (sgcucode),
KEY serialidx (serial),
KEY fullidx (sgcucode,daqID,serial,dtdateFirst,dtdateLast)
) ENGINE=InnoDB AUTO_INCREMENT=135 DEFAULT CHARSET=utf8
Table: data
id int(11) NOT NULL AUTO_INCREMENT,
dtdate datetime NOT NULL,
daqID char(4) NOT NULL,
Varray text NOT NULL,
Iarray text NOT NULL,
Iavg float NOT NULL,
Pmp float NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY id_UNIQUE (id),
UNIQUE KEY dupliData (dtdate,daqID,Iavg),
KEY idxDaqDate (daqID,dtdate),
KEY idxDate (dtdate),
KEY idxPmp (Pmp)
) ENGINE=InnoDB AUTO_INCREMENT=14027571 DEFAULT CHARSET=utf8
The "relationships" table matches the daqID to a panel serial number for a given time span (from "dtdateFirst" to "dtdateLast"). So the important columns for this question are: daqID, dtdateFirst, dtdateLast, serial. Also of some importance is the "sgcucode". This column indicates which test site the modules are on. It is used by a dashboard so that we can cycle through various sites which log data to the same table.
Data is constantly being logged to the "data" table from the DAQ devices. I then use the relationships table to correlate the serial number of the solar panel with the correct daqID for the time in question.
By far the most common query is to collect all of the data in the "data" table for a given day, and display it in a dashboard (shown below).
This is the query I use to do this:
SELECT relationships.serial as title, dtdate as time , Pmp as Value, relationships.ptype as type
FROM data INNER JOIN (relationships) ON (relationships.daqID=data.daqID)
AND dtdate BETWEEN DATE_FORMAT(example_date, '%Y-%m-%d 05:00:00') AND DATE_FORMAT(example_date, '%Y-%m-%d 21:00:00')
WHERE relationships.dtdateFirst <= dtdate
AND relationships.dtdateLast >= dtdate
AND sgcucode="example_code";
Given these conditions, is this the best solution? I probably have redundant indexes, I am still learning about database design, so any suggestions for improvement would be greatly appreciated!

MYSQL group products by compatibilty

I have two tables, one is for kits that can contain various sub-products, and another is a list of sub-products that can't be added together in the same kit.
In simplified form:
CREATE TABLE `kits` (
`subProdID` INT(11) NOT NULL DEFAULT '0',
`kitID` VARCHAR(50) NULL DEFAULT NULL,
`kitName` VARCHAR(512) NULL DEFAULT NULL,
PRIMARY KEY (`subProdID`)
);
CREATE TABLE `subProd_incompatible` (
`IncompID` INT(11) NOT NULL DEFAULT '0',
`subProdID` INT(11) NULL DEFAULT NULL,
`subProdIncompID` INT(11) NULL DEFAULT NULL,
PRIMARY KEY (`VersaIncompID`)
)
In subProd_incompatible, subProdID and subProdIncompID all exist as subProdIDs in kits.
Right now I use procedural code outside the database to produce a list of kits that all contain products that do not conflict according to the subProd_incompatible table, but I'd like to do it in SQL if at all possible.
What I'd like is to
SELECT `kitID`, `kitName`, GROUP_CONCAT(`subProdID`)
FROM ("a subquery") AS Q
GROUP BY `GroupingCriterion`
"a subquery" should return the columns of kits, along with a generated GroupingCriterion.
Here is an idea. For each kit, get the list of the matches to the incompatible table for each subproduct. Then, aggregate by the kit and incompatible id and see if any have two products. If so, you have incompatible products in the kit:
select k.kitid, i.InCompId
from kits k join
subProd_incompatible i
on k.subProdId in (i.subProdId, i.subProdIncompID)
group by k.kitid, i.InCompId
having count(distinct k.subProdId) = 2;

Recommended MySQL data model for similar class objects

We will be creating a MySQL database to reflect the following PHP classes:
Content
Article extends Content
Post extends Content
...
There will be models associated with Content, like Comment, Ignore etc..
The question here is which data model to use.
1) One table per model
2) One large content table (with all class properties as columns), plus single tables for the other models. I believe it's called single table inheritance.
3) A large content table with the common fields, or only the fields we commonly search on (indexes), plus a large content-data table, with the rest of the properties.
4) Another way?
We want to be able to query against the Content, and get recent, location-based or somehow filtered content, regardless of which content type (subclass) it is. Therefore solution 1 is probably out of the question.
This system should be quick and scalable. What is the best way to go about this problem?
I wrote a blog post about this topic that you may find helpful: http://jasonswett.net/blog/composition-a-better-alternative-to-orm-inheritance/
First, I'd like to recommend that you call your table content_item or something instead of content. The reason for this is I believe the table name should match the name of the thing it represents. See this other blog post of mine for details on that.
So you might have a table called content_item, a table called article and a table called post. content_item would have columns like title, slug, etc - everything that every content item will have, whether it be a post, article or something else. Then article would have a content_item_id and then anything else that's specific to articles, but only what's specific to articles. For each article, you'd have a content_item record, then an article record that's attached to that content_item record.
This would give you the ability to query content_item and get results that include all content types.
I've implemented this kind of solution in the past with good success. (I've also tried different approaches and didn't like them as much.)
If they are sufficiently similar objects, i would use option 3... A la drupals node concept or the current wordPress idea of a 'post'
Try something like this perhaps. Your needs for post vs. articles extending content may differ, but instead of a 1..many relationship from content -> post (or article), why not add flexibility to mix/match content within any. If you don't need/desire that, then omit the article_content and post_content tables and simply add a foreign key content_id INT NOT NULL to each of the post and article tables respectively..
CREATE TABLE IF NOT EXISTS content (id INT NOT NULL AUTO_INCREMENT, title VARCHAR(32) NOT NULL, description VARCHAR(255) NULL, body TEXT, published DATETIME NOT NULL, updated DATETIME NULL, owner_id INT DEFAULT '0', status TINYINT(1) DEFAULT '1', PRIMARY KEY (id), INDEX idx_content_status (status));
CREATE TABLE IF NOT EXISTS article (id INT NOT NULL AUTO_INCREMENT, title VARCHAR(32) NOT NULL, excerpt VARCHAR(255) NULL, url_slug VARCHAR(64) NOT NULL, author_id INT NOT NULL, published DATETIME NOT NULL, updated DATETIME NULL, status TINYINT(1) DEFAULT '1', sort_order INT DEFAULT '1', PRIMARY KEY (id), UNIQUE INDEX idx_article_slug(url_slug), INDEX idx_article_search(title, published, status, sort_order));
CREATE TABLE IF NOT EXISTS post (id INT NOT NULL AUTO_INCREMENT, title VARCHAR(32) NOT NULL, comment VARCHAR(128) NULL, author_id INT NOT NULL, published DATETIME NOT NULL, updated DATETIME NULL, status TINYINT(1) DEFAULT '1', PRIMARY KEY (id));
CREATE TABLE IF NOT EXISTS tags (id INT NOT NULL AUTO_INCREMENT, tag VARCHAR(24) NOT NULL, PRIMARY KEY (id));
CREATE TABLE IF NOT EXISTS article_content (article_id INT NOT NULL, content_id INT NOT NULL, status TINYINT(1) DEFAULT '1', sort_order INT DEFAULT '1', INDEX idx_article_content_search(article_id, content_id, status, sort_order));
CREATE TABLE IF NOT EXISTS post_content (post_id INT NOT NULL, content_id INT NOT NULL, status TINYINT(1) DEFAULT '1', sort_order INT DEFAULT '1', INDEX idx_post_content_search(post_id, content_id, status, sort_order));
CREATE TABLE IF NOT EXISTS article_tags (article_id INT NOT NULL, tag_id INT NOT NULL, INDEX idx_article_tag_search(article_id, tag_id));
CREATE TABLE IF NOT EXISTS post_tags (post_id INT NOT NULL, tag_id INT NOT NULL, INDEX idx_post_tag_search(post_id, tag_id));