I am building a EC system for a client, this client is selling second hand products. My DB scheme is roughly like this
+-------------+ +-------------+ +-------------+
| Category | | Product | | Stock |
+-------------+ +-------------+ +-------------+
| category_id | | category_id | | stock_id |
| path | | product_id | | product_id |
+-------------+ +-------------+ +-------------+
a product is inside a category, a stock is an item of one product, the stock also contain specific information for that: stock serial, state (good condition, a bit broken, junk).
Now I need to make a page to show product in stock in each category for exemple something like this:
TV (big category)
LCD (small category)
SONY super LCD 22'' <- product
Stock 1
Stock 2
Stock 3
Sony super LCD 24''
.... etc ...
PLASMA (category)
Hitachi bla bla plasma
Stock 1
Stock 2
Stock 3
Since the user can browse first level of category I need to add some kind of paging system or the page would get too long and the server would be loaded for nothing.
My initial plan was to pull all the products in the category and children category, those product would have stock and do a limit on that query. When I got those product I would do n number of query to get the actual stock rows.
Since a good SQL query is better then long explanation
SELECT * FROM category c JOIN product p ON c.category_id = p.category_id
WHERE c.deleted = 0
AND p.deleted = 0
AND (c.category_id = 37 or c.path LIKE '/1/37/%')
AND (SELECT count(*) FROM stock s WHERE s.product_id = p.product_id AND s.sell= 1) != 0
LIMIT 10
After I would do 10 queries like this
SELECT * FROM stock s WHERE s.sell = 1 AND s.product_id = pulledid
so here come my question, is there a better way to do it ? Can you some flows in this approach(don't be indulgent I know that's really a naive approach but I cannot come with something else)
Would it be more intelligent to limit on stock ? knowing that the page would have some dynamic html (maybe ajax) to group some that have same price.
Is it better to show:
- one product and all stocks (in most case it won't be more then 5 items)
- page by stock which means the Product A could be on page 1 and page 2.
If someone have experience building this kind of system and can give me some feedback I would be grateful.
Why are you getting the product IDs and then using them individually to query the stock table? It seems like you could just join the stock table.
Using your design you will get more than 10 rows if there is more than one item in stock for a given product ID.
It is not clear how you are going to get the next 10 for the following page. You need a way to select starting at the next 10.
The normal way I would structure a problem like this this would be to create a query that lists all the results. You could do this by joining the stock table into your initial query. To make the paging work you devise a column or set of columns in the output that corresponds to the ordering that you want in your output, then add an order by clause for that. This set should be unique. In your case it could be something like product ID, stock ID, and a unique stock identifier like an autoincremented column or a timestamp. You can then limit your first page query to the page size. For the next page, select only those records where the unique values are greater than the last one on the last page. In your case, you would be using multiple columns so you might not be able to query this way. An alternative would be to select 2 pages worth for page 2 but throw away the first page and display the second page.
The idea is that for each page you are selecting the same overall set of records in the same order but displaying a different subset.
Related
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 wanted to know how to use PHP to show recommend items based on what the user is viewing or has in cart.
I have a view page in which I display all my items and when the user clicks on the item. The ID of the item is posted to the product detail page in which I render the product by using the ID to select from my product table
In my product I have product table
I have something like below with over 40 records. 10 of it are shoes
Id, name category desc
1 leather shoe blah
2 italian shoe blah blah
In my product detail page. I want to echo some recommended items atleast 4 of the items item some divs with just an image and name.
My question
How can I select an item based on the category of the item getting view? for an example in my table, lets says i was viewing item 1 which is in the category columns. I want to show random 4 items in the category columns.
Thanks for your help.
Assuming you have your data set up as such.
items:
+----+-------------+------+
| id | category_id | name |
+----+-------------+------+
categories:
+----+------+
| id | name |
+----+------+
You could use
select *
from items
left join categories
on categories.id = items.id
where item.category_id = $category_id
limit 4
this will pull 4 items from the same category as whatever $category_id is set to.
thats as good an answer as i can give with the data you've given.
I am asked to give a possibility to the administrator of the site to create attributes in the database tables. There are sellers and buyers on the website, and each seller when adding a certain product, fills out the needed fields for the specific product, and then publishes the product. I am kind of confused on how is this going to work. If every product has specific fields, then that would mean that if the site has 2.000 products, I will have 2.000 tables? I've never worked on such thing, so I really don't know how to handle this. Furthermore, on the admin feature to create attributes. Let's say the product is a tomato. The admin adds field for the tomatoes that is called "condition" and it has options such as "frozen", and "fresh". Then, when some of the sellers tries to create tomato product, they will need to choose if the tomato's condition is fresh or frozen. I thought of a possible solution such as creating a table that will hold the text of the , and then another table that will hold the text of the .
product_tomato ( product_id, user_id, name, description, condition)
product_select( select_id, product_id, select_text)
product_option( option_id, select_id, option_text)
So, this is how I imagined the tables for doing this. So, when the admin adds a field to the product table, I will add a column in the product table, then create new row in the prodcut_select table, and then list the possible options in the product_option table. But then I got confused on how to display that on the product page. How am I going to deal with that in the code, when I don't know what are the names of the columns that the admin created?
The wording of the question is very confusing, but I believe I get the gist of what you're saying.
No, you would not make a table for every single product, that would get ridiculous very quickly. You can handle this easily for multiple products with three tables.
Tables:
Product
Product_Attributes
Seller_Product
Let's take your hypothetical example of a tomato with conditions.
The admin decides that his site will now offer tomatoes as a product. He creates a product, and adds it to the product table. Then, he decides that tomatoes should have a "condition" attribute that has two possible values, fresh and frozen. Therefore, he would add two rows to the Product_Attributes table, with three fields (Product, Condition, Value).
Therefore, your tables would now look like this.
Product Table:
*Name |*
Tomato
Product_Attribute Table:
*Product | Attribute | Value*
Tomato | Condition | Fresh
Tomato | Condition | Frozen
Finally, when your sellers added items to the site store or whatever it is, you would have them enter the data into a form that grabbed the conditions and potential values from the Product_Attribute table for that product. In this case, there's only one attribute so they would just fill out the condition. Let's assume that there are two sellers, Jim and Tom, who sell fresh and frozen tomatoes respectively. The final three tables would look like this.
Product Table:
*Name |*
Tomato
Product_Attribute Table:
*Product | Attribute | Value*
Tomato | Condition | Fresh
Tomato | Condition | Frozen
Seller Product Table:
*Seller | Product| Attribute | Value*
Jim | Tomato | Condition | Fresh
Tom | Tomato | Condition | Frozen
This way, you could store a variety of custom fields about products using three tables. You should normalize or denormalize as needed, you may want a table for seller's products only and store their conditions in a separate table. Either way, the method described above would get the job done.
I believe here's a good scheme:
product_data - product ID, category ID, name, price, description
product_meta - product ID, attribute_name, attribute_value
product_variants - product ID, variant ID, variant value
You'd also like separate tables for variant names and category names/descriptions.
Example:
ID | Category_ID | Name | Price
251 | 14 | Tomato | 5.00
ID | Attribute | Value
251 | Condition | Fresh
251 | Color | Red
ID | Variant_ID | Name | Value
251 | 50 | Size | Small
251 | 50 | Size | Huge
So basically you'll have around 5-10 tables (Google 3 steps of DB normalization). All tables are linked together by IDs.
All you'll need to do - retrieve the values using JOIN statement and WHERE product_id condition.
I am building an EC website for a customer and the project manager came with some strange ideas and I am struggling to actually implement what he sold to the client.
Here comes my main issue and a quick summary how the system is setup: product are inside categories, categories could be children of an another category. So the category is presented as a tree on the left sidebar of the website.
The user can browse any category, even non "leaf" category, if the user click on non leaf category a listing like that should be presented for exemple on a level 1 category (same apply to level 2 categories):
big category 1
category level ( 3 or 2 )
product 1
product 2
product 3
category level ( 3 or 2 )
The things should also have some paging and present on 5 product on each page. Plus the category should be ordered in same fashion they appear in the menu on left side ... my DB scheme is like this:
+-------------+ +-------------+
+ category + + product +
+-------------+ +-------------+
+ category_id + + product_id +
+ parent_id + + category_id +
+-------------+ +-------------+
I cannot really figure out how I should code the SQL to make sure the product appear in order they should(like ordering product and categories has menu).
Also I am concerned about the performance of the whole setup, if the user select a non "leaf" category I would have to search all the child category and make a big category IN ( id1, id2, id3 ) and I know by experience long IN statement don't perform well.
If someone have encountered same design/issue and have some advice how to make it I would be grateful.
You could use the Materialized Path design. A directory path is an example of materialized path. That is, a series of ancestor values, concatenated together, with some character ("/" or "," are common) separating them.
So you might have categories:
+---------------------------------------------+
| cat_id | Name | cat_path | depth |
+---------------------------------------------+
| 1 | Electronics | 1/ | 1 |
| 2 | Digital cameras | 1/2/ | 2 |
| 3 | SLR cameras | 1/2/3/ | 3 |
| 4 | Audio | 1/4/ | 2 |
| 5 | Speakers | 1/4/5/ | 3 |
| 6 | Wall Satellites | 1/4/5/6/ | 4 |
| 7 | Computers | 1/7/ | 2 |
+---------------------------------------------+
Now if you want all products that are under Audio, you can do a query like:
SELECT p.*, pc.*
FROM Products p JOIN Categories pc ON (p.cat_id = pc.cat_id)
JOIN Categories c ON (pc.cat_path LIKE c.cat_path||'%')
WHERE c.name = 'Audio';
For example, '1/4/5/6' LIKE '1/4/%' is true, therefore Wall Satellites are included. And same for any other subcategory of Audio.
Re your question about menu rendering: I assume you'd want the menu to render:
- All ancestors of the chosen category
- All siblings of the ancestors of the chosen category
So if you choose 'Speakers', you'd see:
Electronics
Audio
Speakers
Computers
Digital Cameras
But you don't want descendants of Computers or Digital Cameras (i.e. "cousins" of Speakers).
SELECT uncle.name, uncle.depth
FROM Categories chosen
JOIN Categories ancestor ON (chosen.cat_path LIKE ancestor.cat_path||'%')
JOIN Categories uncle ON (ancestor.depth = uncle.depth
AND SUBSTRING(REVERSE(ancestor.cat_path), 3, 100) = SUBSTRING(REVERSE(uncle.cat_path), 3, 100))
WHERE chosen.name = 'Speakers'
ORDER BY uncle.depth, uncle.name;
I'm using a trick to detect uncles: compare the paths, after stripping the last element. To do this, reverse the string and then strip the first element. This should work at least in MySQL and MS SQL Server, but REVERSE() isn't standard and might not be portable to other brands of RDBMS.
Note that you should probably allow for more than one digit for each element in the cat_path, in which case the substring offset should also increase.
From a performance perspective this is a bad design. If a customer accidentally clicks on the toppermost category you would execute a query of your entire inventory. This will probably take an unacceptable amount of time. In web terms this translates to the customer losing patience, clicking over to your rival's site and never visiting your site again.
Of course, premature optimization is the root of all evil and all that, but it is a good idea to avoid doing completely dumb things.
I would also take issue with the whole idea of tree navigation as an approach. It smacks a bit too much of asking your customers to play a game of "Guess how we inventory our stock". Apart from anything else, in many spheres a product can belong to more than one category, so fitting them in a hierarchy is an arbitrary process. At the very least you probably ought to have a data model which supports assigning a product to multiple leaf categories. (This may depend on the nature of what you're selling and the granularity of your categories).
If your boss insists on their way then you still have some options to improve the performance of the query. For instance you could have a table which includes all the products joined by all their parent categories...
cat1 product1
cat1 product2
cat1 product3
cat1 product4
cat1 cat1.1 product1
cat1 cat1.1 product2
cat1 cat1.2 product3
cat1 cat1.2 product4
cat1 cat1.1 cat1.1.1 product1
cat1 cat1.1 cat1.1.2 product2
cat1 cat1.2 cat1.2.1 product3
cat1 cat1.2 cat1.2.2 product4
You would have to maintain this, through triggers or as a materialized view or through some other mechanism (depending on what your database flavour offers). But the overhead of maintaining it would be neglible compared to the performance benefits of not having to re-assemble the product hierarchy for every customer query. Besides it is unlikely you have that much volatility in your inventory.
I'm using PHP and MySQL.
I have a table named quantity. Inside there are records of product name, product price and product quantity. Besides these, there are a few others that helps me select the last records based on date and position, as well as a GROUP BY the field named price because there are different quantities with different prices for the same product. So, I currently select my product specific price and quantity like this:
SELECT `price`,`quantity` FROM (SELECT `price`,`quantity` FROM `quantity` WHERE `product_name` = 'DELL' ORDER BY `date` DESC, `position`) AS `Actions` GROUP BY `price`
This query is a workaround because I need to get data like this:
product_name | price | quantity
DELL | 100 | 30
DELL | 120 | 10
DELL | 130 | 2
Assuming that I have multiple records like these and I need to get the latest of them. Anyway, from this query I need to do the following: I need to select the records whose quantity summed with another product's quantity equals 35. So, by using my query I know that it should stop at line 2 because I can take the 30 products that came with the price of $100 and another 5 products from the line 2 that has price of 120. And then I would need to enter my updates. So, the new data would look like:
product_name | price | quantity
DELL | 100 | 0
DELL | 120 | 5
DELL | 130 | 2
How am I going to achieve this?
Option 1: Use program logic instead of a query:
There is nothing wrong with using the programming layer to do more advanced database interactions. SQL is not an answer to everything... (Also consider a stored procedure).
enough = 35
running_total = 0
START TRANSACTION
while running_total < enough:
select one record order by price limit 1 FOR UPDATE
add to running_total
UPDATE records...
COMMIT
Option 2: Use a query with a running total:
In this option, you obtain a running total using a derived query, and then filter that down to specific records in the outer query. If you intend on updating them, you should wrap this in a transaction with the right isolation level.
SET #running_total = 0;
SELECT
row_id,
product_name,
price,
quantity
FROM
(
SELECT
row_id,
product_name,
price,
quantity,
#running_total := #running_total + quantity AS running_total
FROM
sometable
WHERE
quantity > 0
ORDER BY
quantity
LIMIT
35 /* for performance reasons :) */
) as T1
WHERE
running_total < 35
I would tend to prefer option 1 because it is more "obvious", but perhaps this will give you some food for thought.