Which is a better db design when inserting records?
**Option 1.**
uid|itemid| qty | price | amount
1 |13| 2 | 100 |200
**Option 2.**
uid| itemid| qty | price | amount
1 |13| 1 | 100 | 100
2 |13| 1 | 100 | 100
In option 2 then retrieve rows via mysql query and calculate using id ...
It seems like you want to retain the original price of each item ordered. What I'd do is go with option 1, then create another column in your table that holds a string that identifies each item in the order.
For example, if I were selling fruit, a banana could have iZ as a unique key. And an apple could have 6U as a unique key. When a user places an order, these unique keys would be inserted into your new column. So when you see this column's data is iZ6U you'd know that the user ordered an apple and a banana.
Related
I am designing a system where I have multiple shops where each shop should have its own set of sequential numbers for its invoices. Obviously my primary ID column will be sequential for all invoices in the system so obviously I will need another column to store this "shop specific" invoice number. What is the best manner to store and get the next ID for this shop specific number? For example would it be safe to simply get it right from the invoices table doing something like: SELECT MAX(INV_NUM) FROM INVOICES WHERE SHOP_ID = # and add one, and subsequently create the new invoice record? Or would there be issues with the timing if 2 instances of my script were to run at the same time? For example the first script fetches the next ID, but before it gets the chance to create the new invoice record the second instance requests the next ID and gets the same one as the first script... I was then thinking about just storing the last used number in a separate table and as soon as I request the next invoice number immediately write the new next order number and persist it so that the time between fetching the next order number and creating the record that the next request would rely on is kept to an absolute minimum... literally 3 lines of code:
$nextId = $shop->getLastId() + 1;
$shop->setLastId($nextId);
$em->persist();
Invoices
------------------------------
| ID | INV_NUM | SHOP_ID |
------------------------------
| 1 | 99 | 1 |
| 2 | 100 | 2 |
| 3 | 100 | 1 |
Shops
-------------------
| ID | LAST_ID |
-------------------
| 1 | 100 |
| 2 | 100 |
If you're using Doctrine, which I assume you are since you're using Symfony then you can lifecycle events to listen for changes in your entities. Before saving you can then update your second column to the incremented value.
Regarding race conditions, to be sure you don't have bad data in your database you can put a unique constraint on your shop ID and invoice number.
I am looking to make PHP code that selects the best option in a data table. What is considered "best" would be based off of the variables/columns. I understand that I would need to start a mysqli query and create a couple of loops to search through the database, but I am not entirely sure how to implement something like this.
To give a more in-depth explanation of what I am talking about, here as an example.
(START EXAMPLE)
Lets say I have a database and there is a table with items in it. There are 3 columns: Item ID, Type, On Sale. I want to make it so that a user is able to pick out the best option based on those variables. In addition to finding the "best" option, it selects the one that is first listed in that order (in this case lowest Item ID).
Imagine this table:
Item ID | Type | On Sale
---------------------
1 | Chair | 0
2 | Table | 1
3 | Chair | 1
4 | Oven | 0
5 | Table | 1
6 | Oven | 0
The level of important goes like Type>On Sale>Item ID (lowest).
A user is looking for a chair. Item 3 is selected because it is his item and it is the first one also on sale.
A user is looking for a table. Item 2 is selected over Item 5 because it is listed higher (or in this case, has a lower Item ID)
A user is looking for an oven. Item 4 is selected because no ovens are on sale. Because no options are on sale, it selects the lowest Item ID of the ovens listed.
(END EXAMPLE)
So how should I go about this? Any answers would be greatly appreciated!
select * from table_name where Type = type_specified_by_user order by On Sale, Item ID
I'm working on a website which will be like a marketplace where a registered seller could sell different kind of items. For each item there are common attributes and optional attributes. Take a look to the following, I'll try to explain.
Scenario
The seller add a new item (e.g. iPhone 6 16 gb black)
He builds the insertion specifying item attributes (e.g. price, shipping price, condition, images, description, etc..). This kind of attributes are required and common for any item.
Once all required attributes are filled, the seller have the ability to specify other kind of attributes that are related only with that item (e.g. RAM, capacity, size, weight, model year, OS, number of cores, etc..). This kind of attributes are optional. The seller specify key (e.g. capacity) and value (e.g. 16 gb) and them are related only for that single item. Another iPhone 6 16 gb black sold by another seller may have different attributes.
Actually we have a table called items which contains all the items for sale, and another table called item_attr which contains common item attributes. So an item could be related to 0, 1 or more optional attributes.
We are working on two kind of approaches to store optional values for each item, but both could bring problems.
Case A
Create a new table called item_additional_attr where each record
will represents an additional attribute for a single item. There will
be a one-to-many relationship between items and
item_additional_attr. This seems to be the most "database-friendly" solution, but I'm worried about the size of this
table could have. If items contains 100.000 records and each
item is related to an average of 5 optional attributes,
item_additional_attr will contains 500.000 records. Of course that will be a huge table.
Case B
Create a new field type TEXT or BLOB into item_attr called
optional_attributes. This field will contains an array of optional attributes and will be handled in PHP. Of course the array will be
stored as serialized or json encoded. I think this kind of approach could bring problems with some queries, but it could be handled without problems in PHP.
I'm giving priority to webserver/db performance, but I would also avoid problems with queries. Moreover additional attributes will be used only to show technical specs in a table, never for filtering/sorting. So, in your opinion, what is the best way to achieve that?
You may want to try using EAVs (entity attribute value) tables. Basically you will maintain several tables. One table should store the list of items. Other tables should maintain attributes that all have similar data types. I created a simple schema to demonstrate:
+---------+------------+
| item_id | item_name |
+---------+------------+
| 1 | Cell Phone |
| 2 | Shirt |
+---------+------------+
2 rows in set (0.00 sec)
+---------+--------------+----------------+-----------------+
| item_id | attribute_id | attribute_name | attribute_value |
+---------+--------------+----------------+-----------------+
| 1 | 2 | storage | 8GB |
| 1 | 3 | color | Gray |
| 2 | 4 | size | XL |
| 2 | 6 | shirt_color | Red |
+---------+--------------+----------------+-----------------+
4 rows in set (0.00 sec)
+---------+--------------+----------------+-----------------+
| item_id | attribute_id | attribute_name | attribute_value |
+---------+--------------+----------------+-----------------+
| 1 | 2 | price | 49 |
+---------+--------------+----------------+-----------------+
1 row in set (0.00 sec)
The first table is a list of items. The second table is a list of the items' attributes of type varchar. The third table list items' attributes of type int. This will allow a scalable database that disperses attributes to multiple tables. The only draw back is the amount of join you will need to do in order to get an item and all of its attributes. A textual caching scheme could be used via php in order to store item information for an increase in performance.
I need to store and retrieve items of a course plan in sequence. I also need to be able to add or remove items at any point.
The data looks like this:
-- chapter 1
--- section 1
----- lesson a
----- lesson b
----- drill b
...
I need to be able to identify the sequence so that when the student completes lesson a, I know that he needs to move to lesson b. I also need to be able to insert items in the sequence, like say drill a, and of course now the student goes from lesson a to drill a instead of going to lesson b.
I understand relational databases are not intended for sequences. Originally, I thought about using a simple autoincrement column and use that to handle the sequence, but the insert requirement makes it unworkable.
I have seen this question and the first answer is interesting:
items table
item_id | item
1 | section 1
2 | lesson a
3 | lesson b
4 | drill a
sequence table
item_id | sequence
1 | 1
2 | 2
3 | 4
4 | 3
That way, I would keep adding items in the items table with whatever id and work out the sequence in the sequence table. The only problem with that system is that I need to change the sequence numbers for all items in the sequence table after an insertion. For instance, if I want to insert quiz a before drill a I need to update the sequence numbers.
Not a huge deal but the solutions seems a little overcomplicated. Is there an easier, smarter way to handle this?
Just relate records to the parent and use a sequence flag. You will still need to update all the records when you insert in the middle but I can't really think of a simple way around that without leaving yourself space to begin with.
items table:
id | name | parent_id | sequence
--------------------------------------
1 | chapter 1 | null | 1
2 | section 1 | 1 | 2
3 | lesson a | 2 | 3
4 | lesson b | 2 | 5
5 | drill a | 2 | 4
When you need to insert a record in the middle a query like this will work:
UPDATE items SET sequence=sequence+1 WHERE sequence > 3;
insert into items (name, parent_id, sequence) values('quiz a', 2, 4);
To select the data in order your query will look like:
select * from items order by sequence;
I am trying to find a good solution to accomplish the following:
I have a table which includes the name of various products, such as:
"Tide - Original Scent".
In addition, I also have the amount in there for e.g. 50 fl oz.
The problem I have right now is, that the product not only comes in containers of 50 fl oz but also in different sizes such as 75 fl oz and 100 fl oz. For each of these I have new rows in the product table:
id| productName | amount | unit
1 |"Tide - Original Scent" | 50 | "fl oz"
2 |"Tide - Original Scent" | 75 | "fl oz"
3 |"Tide - Original Scent" | 100 | "fl oz"
Now I have a web interface to perform a search on this table and whenever I search for "tide" I get all three rows - which is supposed to be like that of course. However I would like a different behavior and this is where I need your help:
Instead of returning all three rows I would like one row only. I would then need to be able to process it in php so that if the user clicks on "Tide - Original Scent" that the user is then prompted to select the size.
To add even more complexity to the task:
I also have products in the same table named:
"Tide - Mountain Spring".
In this case, it would be great to have some relations set up so I know that "Tide - Original Scent" is linked with "Tide - Mountain Spring". Within php I would then like to not only give the user the choice of selecting the size but also the (in this case) scent.
What would your recommendation be on how I can accomplish this (not the php part)?
How would your database look like?
Do I need to create a table where I map these products? How would this look like if you would create this :)
2 possibilities:
Don't store the sizes in that table - along with the other specific information. Move that to another table. Denormalize your structure.
or
Query but group by the name. For the size column, do a count(amount). If it's more than one, you can then populate a drop down with choices. This is good temporary fix.
SELECT productName, count(amount) AS numOfChoices FROM YOUR_TABLE
WHERE LOWER(productName) LIKE 'tide%'
GROUP BY productName
then after the choice is made
SELECT id, amount FROM YOUR_TABLE
WHERE id = "$selectedId"
to present a choice of sizes that will pin point which one.
I would personally setup my tables like this..
Products Table:
ID| Product ID | Product Name | Description
1 | 0404888282 | Tide - Original Scent | Smells Good
Quantity Table:
ID| Product ID | Size| Price | Quantity
1 | 0404888282 | 50 | 4.99 | 23
2 | 0404888282 | 75 | 5.99 | 120
3 | 0404888282 | 100 | 7.99 | 10
This structure you have a table for each unique item, another for the sizes and quantity of each size. Keeps the structure clean and easy to understand.
"Instead of returning all three rows I would like one row only."
SELECT DISTINCT productName FROM YOUR_TABLE WHERE LOWER(productName) LIKE 'tide%'
And you'll need a functional index on LOWER(productName) for good performance. Alternatively a case-insensitive collation sequence could be used on DBMSes that support that (e.g. MS SQL Server).
"I would then need to be able to process it in php so that if the user clicks on "Tide - Original Scent" that the user is then prompted to select the size."
SELECT amount FROM YOUR_TABLE WHERE productName = whatever_user_selected
"To add even more complexity to the task: I also have products in the same table named:
"Tide - Mountain Spring"."
The query above will also return that.
What you can do is :
$Query = 'SELECT productName, amount, unit FROM products';
$Data = array();
while($Assoc = mysql_fetch_assoc($Query)){
if(!isset($Data[$Assoc['productName']])){
$Data[$Assoc['productName']] = array();
}
$Size = $Assoc['amount'].' '$Assoc['unit'];
$Data[$Assoc['productName']][] = $Size;
}
// Now what you can do is :
foreach($Data as $ProductName => $Amount){
echo $ProductName.' has :<br />';
if(count($Amount) > 0){
foreach($Amount as $Key => $Value){
echo $Value.'<br />';
}
} else {
echo 'Nothing<br />';
}
}
This however doesn't solve the problem on MySQL's side. IT will work in PHP wihtout problem. It's not beautiful but it's working.
For the first problem, you could create two tables:
products - this is where you store all the information about a product except for the specifics such as different sizes, colors, etc.
attributes - you would link to the product and for each attribute you specify a value
products
id | description
---+------------
1 | crazy shirt
2 | clown shoe
attributes
product | name | value
--------+-------+-------
1 | color | green
1 | color | blue
1 | size | medium
2 | size | large
You can optimize the attributes table further by creating a attribute_names table (and even an attribute_values table).
For your second problem, you could either:
create a related product id column inside the products table; this would limit you to only one related product per product.
create a related product table in which you store combinations between two products.
related_products
product_id | related_product_id
-----------+-------------------
1 | 2
1 | 3
That would create a relationship between product 1 and products 2 and 3.
Hope this helps.
Create a foreign key to sublabels, and order them with a counter. This will look like.
Product Table:
Product ID (key)
Brand ID
Price
Brand Table:
Brand ID (key)
Brand
Sublable Table:
ID
Product ID
Order Index
Value
Size Table:
Size ID
Value
Unit
ProductSize Table:
Size ID
Product ID
Then, you'll divide into subcategories using subsequent sublabels.
Products
10 | 6 | 1.99
11 | 6 | 2.99
12 | 6 | 3.99
13 | 6 | 1.99
14 | 6 | 2.99
15 | 6 | 3.99
Brand
6 | Tide
Sublabel
30 | 10 | 1 | Original Scent
30 | 11 | 1 | Original Scent
30 | 12 | 1 | Original Scent
30 | 13 | 1 | Mountain Spring
30 | 14 | 1 | Mountain Spring
30 | 15 | 1 | Mountain Spring
Size Table
1 | 50 | fl.oz.
2 | 75 | fl.oz.
3 | 100 | fl.oz.
Product Size Table
1 | 10
1 | 13
2 | 11
2 | 14
3 | 12
3 | 15