i have a 2 category system, basically what i want to do is i have 2 tables, top_category and bottom_category, i have created my sidebar which will list all the products using sql query. is there a way i can pull the top_category and bottom_category data in one sql query and have the bottom_category sorted by the foreign key id of top_category so when i loop them in a list they end up in the right nest?
Here are my tables,
CREATE TABLE top_category (
id INT PRIMARY KEY,
NAME VARCHAR(100)
);
CREATE TABLE bottom_category (
id INT PRIMARY KEY,
NAME VARCHAR(100) ,
top_category_id INT REFERENCES top_category
);
And here is my products table, so when i click on a bottom_category link i want it to list the products linked to the bottom_category_id's:
create table product (
id int primary key,
name varchar(100) ,
bottom_category_id int references bottom_category
);
You could write something like
SELECT product.*, bottom_category.name, top_category.name
FROM product
LEFT JOIN bottom_category ON bottom_category.id = product.bottom_category_id
LEFT JOIN top_category ON top_category.id = bottom_category.top_category_id
ORDER BY top_category.id,bottom_category.id
But if you have really big tables then just forget about 3nd normal form and add names for categories into product table. But only if you have really big tables with categories.
UPD
Add ORDER BY
select p.*,
bc.name bc_name,
tc.name tc_name
from product p
left join bottom_category bc on p.bottom_category_id=bc.id
left join top_category tc on bc.top_category_id=tc.id
order by tc.id,bc.id
Related
I have 3 tables: users, games and players. For simplicity's sake, I will say that the tables look like this:
users
id int AUTO_INCREMENT,
PRIMARY KEY (id)
games
id int AUTO_INCREMENT,
PRIMARY KEY (id)
players
game_id int,
user_id int,
FOREIGN KEY (game_id) REFERENCES (games.id),
FOREIGN KEY (user_id) REFERENCES (users.id)
I need to select users.id where players.game_id is 1
so far I have tried
SELECT users.id FROM users, game_users WHERE players.id = 1
and also some SELECT statements with INNER JOINs but to no avail.
if only need user_id don't need JOIN just
SELECT user_id
FROM players
WHERE game_id =1
Now if you need the names a single JOIN will work
SELECT users.name
FROM players
JOIN users
ON player.user_id = users.id
WHERE game_id =1
you must have a join key to join users and games. as
...WHERE game_users.id = 1 AND users.id=game_users.id
And you don't have a table called game_users
I have a design issue with my Mysql MyISAM tables.
One of my tables contains a list of 'categories', (approx 100 rows)
id |catcode |name
-------------------------------
1 #01 lorem
2 #0101 ipsum
3 #02 dolor
Another table contains 'products'. (approx 28.000 rows)
id |cats |name ...
---------------------------
1 #01#02 lorem ...
2 #0101 ipsum ...
3 #02#0101 dolor ...
in my products table, I define the list of categories where they belong to.
that's working perfectly, but now:
I'd like to customize the ordering they appear depending on the category selected.
At the moment, I have a column with the id of the categorie in my product table, but this seams very dirty (ordering_1, ordering_2, ordering_3, ...)
Especially because each product appears in only 2 or 3 different categories, so most of the columns are just empty:
id |cats |name |ordering_1 |ordering_2 |ordering_3 ...
-----------------------------------------------------------------------
1 #01#02 lorem 17 12
2 #0101 ipsum 13
3 #02#0101 dolor 21 8
I'd like to use a third table to determine the ordering, but how?
The basic idea is to have a table with the categories and an 'ordering' list, but one of these lists contains all the products! (the main category) I suppose it would be veeery slow to do a request with 28.000 id in it...
How to handle this?
You should use foreign keys and a join table to link products with categories, instead of your catcode construct.
Create the table structure somewhat like the following (example is for MySQL):
CREATE TABLE product (
id INT NOT NULL,
name VARCHAR(255),
PRIMARY KEY (id)
) ENGINE=InnoDB;
CREATE TABLE category (
id INT NOT NULL,
name VARCHAR(255),
PRIMARY KEY (id)
) ENGINE=InnoDB;
CREATE TABLE product_category (
product_id INT NOT NULL,
category_id INT NOT NULL,
ordering INT NOT NULL,
PRIMARY KEY (product_id, category_id),
FOREIGN KEY (product_id) REFERENCES product (id),
FOREIGN KEY (category_id) REFERENCES category (id)
) ENGINE=InnoDB;
Then to get all products in a particular category sorted as defined for this category, query them like this:
SELECT * FROM product p LEFT JOIN product_category pc ON p.id = pc.product_id
WHERE pc.category_id = 1
ORDER BY pc.ordering ASC;
1 is the category id in this. You could also query by category name for example:
SELECT * FROM product p
LEFT JOIN product_category pc ON p.id = pc.product_id
LEFT JOIN category c ON pc.category_id = c.id
WHERE c.name = "foo"
ORDER BY pc.ordering ASC;
I have 3 tables products and stock and purchases.
Puchases has a primary key of purchaseID.
My products table has a primary key of productID and then columns with details on the product.
My stock table has a primary key of framenumber a foreign key of productID and another foriegn key of purchaseID.
Not all products have any stock.
I would like to join the prodcuts and the stock table by the productID and have a row on the end which counts the amount of null values in the purchaseID column, which represents how many i have in stock.
I have tried a load of queries but seem to always just come up with total amount of entries in stock table or with subqueries not left joining properly.
Some things ive tried
SELECT * , COUNT( * ) AS Count
FROM Products
LEFT JOIN Stock ON Products.productID = Stock.productID
WHERE Stock.OrderID IS NULL AND Stock.framenumber IS NOT NULL
GROUP BY Products.productID
Just gives me the count of all the objects in stock grouped by productID and doesnt join all the products
Schema
CREATE TABLE BikeStock (
FrameNumber VARCHAR(10) PRIMARY KEY,
BikeCode VARCHAR(40),
OrderID INT,
FOREIGN KEY (BikeCode) REFERENCES Bike(BikeCode),
FOREIGN KEY (OrderID) REFERENCES Purchase(OrderID)
);
CREATE TABLE Bike (
BikeCode VARCHAR (40) PRIMARY KEY,
Manufacturer VARCHAR (30),
Model VARCHAR (30),
SubType VARCHAR (30),
Year SMALLINT,
FrameMaterial VARCHAR (20),
Description TEXT,
Gender VARCHAR (6),
Type VARCHAR (20),
Price DECIMAL (11,2)
);
CREATE TABLE Purchase (
DateOfPurchase DATETIME,
OrderID INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(OrderID),
CustomerID INT,
FOREIGN KEY (CustomerID) REFERENCES Customer(CustomerID)
);
SELECT Products.productID , SUM(
case
when (Stock.OrderID IS NULL AND Stock.framenumber IS NOT NULL)
then 1
else 0
end case
) AS Count
FROM Products
LEFT JOIN Stock ON Products.productID = Stock.productID
GROUP BY Products.productID
Failing to find a solution to this question I was wondering if there was a better way of storing data for this problem.
That db structure allows items to be stored in multiple categories, but doesn't allow easy access to the parent category hierarchy.
What I would like is to have a category relationship such as:
Books
> Novels
> Paperbacks
> Hardbacks
And have an item stored against Paperbacks for instance that would also show up in Novels and Books. So the 'categories' actually work more like filters than actual categories.
First of all you need to design your category table with usage of Nested Set architecture. With usage of Nested Sets you will easily select whole branch of categories, and then you will be able to select products for these categories.
So the first table will be:
CREATE TABLE categories (
id int unsigned NOT NULL auto_increment,
name varchar(255) NOT NULL,
left int unsigned NOT NULL,
right int unsigned NOT NULL,
PRIMARY KEY (id)
);
The second table will be:
CREATE TABLE products (
id int unsigned NOT NULL auto_increment,
name varchar(255) NOT NULL,
PRIMARY KEY (id)
);
And the third table will be:
CREATE TABLE product_categories (
category_id int unsigned NOT NULL,
product_id int unsigned NOT NULL,
PRIMARY KEY (category_id, product_id)
);
Now to select all products for whole branch of categories, you need to use query like this:
SELECT p.*
FROM categories AS c1
LEFT JOIN categories AS c2 ON c1.left <= c2.left AND c2.right <= c1.right
LEFT JOIN product_categories AS pc ON pc.category_id = c2.id
LEFT JOIN products AS p ON pc.product_id = p.id
WHERE c1.id = #id
Nested set operations
Add new node
1st step: update already existed categories
UPDATE categories
SET right = right + 2, left = IF(left > #right, left + 2, left)
WHERE right >= #right
2nd step: insert new category
INSERT INTO categories SET left = #right, right = #right + 1, name = #name
Delete existing node
1st step: delete node
DELETE FROM categories WHERE left >= #left AND right <= #right
2nd step: update else nodes
UPDATE categories
SET left = IF(left > #left, left – (#right - #left + 1), left),
right = right – (#right - #left + 1)
WHERE right > #right
ok I have looked around here for a few days now, and I did find this :
MySQL GROUP BY two columns
This has been helpful but hasn't worked for what I am currently working on.
So here is the scope of things I have 3 different tables
table 1 (quantities) consist of:
quantity_id int(11) auto_increment not null (primary key)
product_id int(11) not null
color_id int(11) not null
size_id int (11) not null
quantity int (11) not null
table 2 (colors) consist of:
color_id int(11) auto_increment not null (primary key)
color_name varchar(255)
table 3 (sizes) consist of:
size_id int (11) auto_increment not null (primary key)
size varchar(255)
SELECT s.size, c.color_name, q.quantity, q.size_id, q.product_id, q.color_id
FROM quantities q, colors c, sizes s
WHERE q.color_id = c.color_id
AND q.size_id = s.size_id
AND q.product_id = $pid
GROUP BY q.color_id
ORDER BY q.size_id
$pid is the product id passed by the form.
When I load the results into 2 different select statements: one for color, one for size
I get multiple of the same color or size:
red s
red L
yellow L
I understand that it is literally pulling the rows out of the database and displaying them, but how
do I not repeat myself?
I have tried this query
SELECT s.size, c.color_name, q.quantity, q.size_id, q.product_id, q.color_id
FROM quantities q LEFT JOIN colors c ON q.color_id = c.color_id
LEFT JOIN sizes s ON q.size_id = s.size_id
WHERE q.product_id = $pid
Is there a better way?
Your question is a bit unclear, but the query below will give you color, size and quantity information given a product id by using joins.
Updated with Quantity Constraint
SELECT size, color_name, quantity, sizes.size_id, colors.color_id
FROM quantities
INNER JOIN colors ON quantities.color_id = colors.color_id
INNER JOIN sizes ON quantities.size_id = sizes.size_id
WHERE product_id = ? and quantity > 1
You are getting repeated rows because you are getting all the combinations of color and size. I imagine there are several colors for the same size, which is normal. If you want to force the integrity, change you left join for an inner join so that you only get records that have a match.
SELECT s.size, c.color_name, q.quantity, q.size_id, q.product_id, q.color_id
FROM quantities q INNER JOIN colors c ON q.color_id = c.color_id
INNER JOIN sizes s ON q.size_id = s.size_id
WHERE q.product_id = $pid
UPDATE: If you only want to show quantities >=1 then do this:
SELECT s.size, c.color_name, q.quantity, q.size_id, q.product_id, q.color_id
FROM quantities q INNER JOIN colors c ON q.color_id = c.color_id
INNER JOIN sizes s ON q.size_id = s.size_id
WHERE q.product_id = $pid and q.quantity>=1
If you also only want to show one color and one size and the sum of all qty then do this:
SELECT min(s.size) as size, min(c.color_name) as color, sum(q.quantity) as TotalQty
FROM quantities q INNER JOIN colors c ON q.color_id = c.color_id
INNER JOIN sizes s ON q.size_id = s.size_id
WHERE q.product_id = $pid and q.quantity>=1
If you only store id and name for color it could very well be a php function, so you don't need a color table:
<?PHP
function color($color)
{
$colors['red']=1;
$colors['blue']=2;
$colors['green']=3;
return $colors[$color];
}
//example of usage
$sql ="... where `color`=".color($_POST['color'])." ";
?>
Also since size is a small value (is not a big text for example) then you don't need a size table at all. All you need is a product and a quantity (better rename it to product_info) table:
mysql_query("
CREATE TABLE `Product` (
`product_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`product_name` varchar(30) NOT NULL ,
unique(`product_name`),
PRIMARY KEY (`product_id`)
) ENGINE=InnoDB ROW_FORMAT=COMPACT;")
or die(mysql_error());
mysql_query("
CREATE TABLE `Product_Info` (
`product_id` INT UNSIGNED NOT NULL ,
`color_code` char(3) NOT NULL ,
`prod_col_size` varchar(15) NOT NULL ,
`quantity` mediumint UNSIGNED NOT NULL ,
PRIMARY KEY (`product_id`,color_code,prod_col_size),
CONSTRAINT `prod_id_FK` FOREIGN KEY (`product_id`)
REFERENCES `Product` (`product_id`)
ON DELETE CASCADE
) ENGINE=InnoDB ROW_FORMAT=COMPACT;")
or die(mysql_error());
This is not perfect normalization but you already choosed this way. The queries will be very easy this way. To bother with long joins in a well normalized schema makes sense. To bother for no reason at all!? You only need to make a join in two tables in case you need some filed from the product table.
/*Find how many red items has a specific product*/
select sum(`Product_Info`.`quantity`)
from `Product_Info` where `color_code`='124' and `product_id`='3';