Database structure for product attributes on orders - mysql

I'm making a POS and currently kind of stuck on the order proccess due to the product attributes.
As of now the DB looks something like this:
tbl_products
tbl_attributes (attr id, name, extra price)
tbl_sales ( where i store the time, total price etc)
tbl_salelines ( where i store what items have being order for a certain sale).
Problem is storing the attributes for an item order.
There isn't a fix amount of attributes one item can have. It could go from 1 to 10-15
therefor i don't think making some columns in the tbl_salelines with attr_1 attr_2 etc etc would solve this.
How should i approach this?

Can't you use a foreign key in tbl_attributes to youre tbl_salelines table. So you can have 1 saleline and for that saleline you can add multiple attributes which have a relation to the saleline?
Example:
tbl_saleline 1
Id -> 1
tbl_attributes
**Attribute 1**
Id -> 1
name -> 'attribute 1'
**SaleLine_Id -> 1 (FK->tbl_saleline)**
**Attribute 2**
Id -> 2
name -> 'attribute 2'
**SaleLine_Id -> 1 (FK->tbl_saleline)**

Related

Database architecture: When records having different number of attributes (columns)

Suppose I have these records
ID 1: has attributes A,B,D
ID 2: has attributes B,C
ID 3: has attributes F
ID 4: has attributes C,G
.....(Attributes will not duplicate in the same record)
Total estimated number of records: ~180,000
Total number of attributes: 70, increasing
Example of queries I'm going to do:
SELECT * from table WHERE (has attribute B)
SELECT * from table WHERE (has attributes B & D)
SELECT * from table WHERE (has 2 attributes)
SELECT * from table WHERE (has >=3 attributes)
SELECT count(*) from table WHERE (has attribute B)
What is the best database architecture?
Design 1: Storing attributes as 1s & 0s
ID|A|B|C|D|E|F|...
1|1|1|0|1|0|0|...
2|0|1|1|0|0|0|...
3|0|0|0|0|0|1|...
Problems:
New columns needed to be added periodically when new attribute appears
Much redundant data (0s), as more than 80% of data has only 1 attribute, and less than 0.01% of records will have more than 8 attributes.
Design 2: Store attributes as a CSV string
ID|Attributes
1|A,B,D,
2|B,C,
3|F,
Problems:
Slow query when I do
SELECT * from table WHERE attributes LIKE '%B,%' AND attributes LIKE '%D,%'
Design 3: Each attribute has its own table storing record IDs
Table Attribute A
ID
1
4
5
...
Table Attribute B
ID
1
7
10
...
Table Attribute C
ID
2
8
9
...
Problems
Many tables
New tables needed to be added periodically
How to do SELECT * from table WHERE id (appears in exactly 3 tables)?
These are the designs I can think of, please propose any good architecture.
Actually, none of your designs are optimal (the third is the best), and I recommend a single junction table which relates ID valued to their attributes, e.g.
ID | attr
1 | A
1 | B
1 | D
2 | B
2 | C
3 | F
4 | C
4 | G
This is the most normalized approach. To see why this design is optimal, see how easy it is to find all IDs which have attribute B:
SELECT DISTINCT ID
FROM yourTable
WHERE attr = 'B';
It is also fairly straightforward to find all IDs having both attributes B and D:
SELECT ID
FROM yourTable
WHERE attr IN ('B', 'D')
GROUP BY ID
HAVING MIN(attr) <> MAX(attr);
Your first two suggestions would make it much harder to write these queries (give it a try), and in general it is bad practice to store CSV in database tables. Your third suggestion does store the relationships correctly, but it unnecessarily spreads out data across multiple tables.
A more general form of the above query which can easily be extended to any number of IDs is:
SELECT ID
FROM yourTable
WHERE attr IN ('B', 'D')
GROUP BY ID
HAVING COUNT(DISTINCT attr) = 2;

How can I get all products which have selected attributes in Prestashop

I am working on module development for Prestashop. Now I have situation in which I have to fetch all products which have selected attributes. There is a interface, where dropdown list of all active attributes are showing. And user does selects attributes as per need. Now, I want to find all the products on the basis of selected attributes.
Below are the table structure:
Product Table:
id_product id_shop ean upc quantity price
1 1 abc 50 16.99
2 1 def 25 25.99
Product Combination Table
id_attribute id_product
1 1
13 1
5 1
1 2
10 2
Can anyone please help on how can I fetch products on the basis of selected attributes??
Isn't this really basic My SQL ?
select * from product
where
(
select count(*) from
product_combination
where product_combination.id_attribute in (X,Y,Z)
and product.product_id = product_combination.product_id
) = 3
where X,Y,Z are the attributes the user selected, and 3 is the count of attributes selected.
If this is more involved, I think you need to edit your question to provide some more details as to exactly what the technical issue is. If its just that you dont know SQL then this isn't really the place to post your query.

Grabbing all of the information from a tree type table

I'm trying to extract ids from a table that acts as a category tree. The two main columns in that table are the ID and the PARENT_ID.
The tricky part with this table is that the ID can also be the PARENT_ID for another ID.
I wanted to figure out a way to grab all of the IDs and PARENT_IDs under one "tree" but can only get a couple levels in.
Here's where I'm at:
select *
from categories
where id = 2
or parent_id = 2
or parent in (select id
from categories
where parent_id = 2
or parent_id = id)
I've done something similar to this, but you'll need an extra column in your table, which is a concatenation of the all of its parents up the tree. The table would look like:
ID: ParentID: ID2:
1 1 1
2 1 1_2
3 1 1_3
4 1_2 1_2_4
5 1_3 1_3_5
6 1_3_5 1_3_5_6
The parentID doesn't necessarily have to be the concatenated parent ID, but I find that most useful. ID2 is the complete information of where the item is in the tree. If you want all of the branches below a single point, the query is easy:
select * from `table` where id2 like '1\_2\_%';
will give you all of the items underneath the id2='1_2' item.

Table with multiple items in one field

I'm pretty much a MySql newbie so sorry if this sounds daft or I ramble off topic.
I am building a table to list all the data about exhibitions. This comprises fields with data like venue, dates, times, etc and one field with a list of items exhibited. This field will have a delimited list of numbers. They will between 1 and 50 numbers in the range 1 to 999, currently the num_max is 170.
I realise that it would be better practice to hold this data in separate tables but that complicates the uploading process, would require a new table being created for each new exhibition and give rise to more opportunity for errors.
Assuming that this strategy is correct, my real problem is in processing the data.
How do I extract the list of numbers then use it to get an array of product numbers from the master product table?
You should only need one more table that holds items to be included in an exhibit. This table could have several rows for one exhibit, like so:
exhibit_id item_id description
______________________________________________
1 1 painting
1 2 statue
Then, all you would need to do is join the two tables together.
Otherwise, if you did not want to do this, use the php explode method to turn a delimited string into an array.
$data = mysql_query($query);
while ($row = mysql_fetch_assoc($result)) {
$items = $row['items'];
//the explode method in php allows you to turn a delimited string into an array.
$items_array = explode(',', $items);
//loop through each of the items in this exhibit
foreach($items_array as $current_item) {
//do something with $current_item
}
}
If you are going to hold a field that has data such as "2,45,67,126" then you're going to have to process that using the language in which you're extracting it, perhaps PHP.
The 'real' solution would be to have a unique identifier on the table holding the exhibitions and have a second table with the items. So for example, you'd have an id of '42' for the exhibition then a second table (called, perhaps 'items') holding:
id item
42 2
42 45
42 67
42 126
That shouldn't complicate your upload process too much and then you can easily return all the items in an exhibition using:
SELECT item FROM items WHERE id=$exhibition_id
Actually, it wouldn't require a new table for each exhibit, just adding rows to an existing table.
The rows in the child table would be "related" to the parent table by a foreign key.
For example
exhibit
( id int auto_increment primary key comment 'PK unique identifier for exhibit'
, exhibit_venue
, exhibit_date
et al.
)
exhibit_item
( exhibit_id int comment 'fk to exhibit.id'
, item_id int comment 'fk to item.id'
, primary_key (exhibit_id, item_id)
, foreign_key exhibit_items_fk1 (exhibit_id) references exhibit (id)
, foreign_key exhibit_items_fk2 (item_id) references item (id)
)
item
( id int auto_increment primary key comment 'PK unique identifier for item'
, name
, description
, size
, weight
et al.
)
exhibit
id venue date
---- ------- -----------
123 Fox 2014-02-24
124 Ice 2014-03-01
item
id name description
---- -------- -----------
41 madonna painting
42 david sculpture
43 mona lisa painting
exhibit_item
exhibit_id item_id
---------- -------
123 41
123 42
123 43
If you need to store a relative sequence or position (relative order) of the items within an exhibit, you can add another attribute to the exhibit_item table, to store an integer representing the position.
To get all items for one exhibit:
SELECT i.id
, i.name
, i.description
FROM exhibit_item s
JOIN item i
ON i.id = s.item_id
WHERE s.exhibit_id = 123
ORDER BY s.position
If it's handier for you to return a comma separated list of the item ids within an exhibit, as a single string...
SELECT GROUP_CONCAT(s.id ORDER BY s.position) AS item_list
FROM exhibit_item s
WHERE s.exhibit_id = 123

MySQL Query Distinct ID over multiple lines

MySQL table 'Features'
prop_id name
----------------------------
1 Wifi
2 Off Road Parking
1 Off Road Parking
2 Close to beach
3 Close to Pub
1 Close to Pub
Prop_id is the id in another table of the property
what i would like to do is get the id's of all the properties where they have 'Wifi' and 'Close to pub'
so in this case i would like it to only return 1
Hope that i have made sence!
There are several ways to achieve this, one ugly way is:
select prop_id from features
where name = 'Wifi' and prop_id in (
select prop_id from features where name = 'Close to Pub'
)
Use SELECT DISTINCT.
SELECT DISTINCT prop_id FROM table WHERE name="Wifi" or name="Close to pub"