I got a table with products and a table with reviews of the products. The products-table has the parent- and child-products. The parent-products should get all reviews from the child-products. I did:
DROP TABLE IF EXISTS products;
CREATE TABLE products (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`parent` int(10) unsigned DEFAULT NULL,
`review` decimal(3,2) DEFAULT NULL,
PRIMARY KEY(id)
);
DROP TABLE IF EXISTS reviews;
CREATE TABLE reviews (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`product` int(10) unsigned NOT NULL,
`review` decimal(3,2) DEFAULT NULL,
PRIMARY KEY(id)
);
INSERT INTO products SET id=1, parent=null;
INSERT INTO products SET id=2, parent=1;
INSERT INTO products SET id=3, parent=1;
INSERT INTO reviews SET product=2, review=5;
INSERT INTO reviews SET product=3, review=5;
INSERT INTO reviews SET product=3, review=4;
INSERT INTO products SET id=4, parent=null;
INSERT INTO products SET id=5, parent=4;
INSERT INTO reviews SET product=5, review=4;
INSERT INTO reviews SET product=5, review=2;
UPDATE products
SET products.review=
(SELECT SUM(reviews.review)/COUNT(reviews.review) FROM reviews
LEFT JOIN products p ON p.parent = products.id
)
WHERE products.parent IS NULL;
But with that I'm surprised I'm getting an error:
ERROR 1054 (42S22): Unknown column 'products.id' in 'on clause'
Any suggestions on how to do it correctly? The idea is that product 1 should get a review of 14/3 = 4.66 and product 4 should get a review of 6/2 = 3.
The products is not visible in the subquery. Use following syntax instead:
UPDATE products pp
LEFT JOIN (
SELECT pc.parent, SUM(r.review)/COUNT(r.review) as 'rev'
FROM reviews r
LEFT JOIN products pc on r.product = pc.id
GROUP BY pc.parent
) pcc ON pcc.parent = pp.id
SET pp.review=pcc.rev
WHERE pp.parent IS NULL;
Since you've declared p as an alias for the products table, you need to use it throughout the query. So, in your LEFT JOIN clause just use p.parent instead of products.parent.
UPDATE products
SET products.review=
(SELECT SUM(reviews.review)/COUNT(reviews.review) FROM reviews
LEFT JOIN products p ON p.parent = p.id
)
WHERE products.parent IS NULL;
At its heart, you appear to be looking for this value:
SELECT SUM(r.review)/(SELECT COUNT(*) FROM products) n FROM reviews r;
+----------+
| n |
+----------+
| 4.666667 |
+----------+
So, something like...
UPDATE products x
JOIN (SELECT SUM(r.review)/(SELECT COUNT(*) FROM products) n FROM reviews r) y
SET x.review = y.n
WHERE x.review IS NULL;
Related
I am trying to get the complete row with the lowest price, not just the field with the lowest price.
Create table:
CREATE TABLE `Products` (
`SubProduct` varchar(100),
`Product` varchar(100),
`Feature1` varchar(100),
`Feature2` varchar(100),
`Feature3` varchar(100),
`Price1` float,
`Price2` float,
`Price3` float,
`Supplier` varchar(100)
);
Insert:
INSERT INTO
`Products` (`SubProduct`, `Product`, `Feature1`, `Feature2`, `Feature3`, `Price1`, `Price2`, `Price3`, `Supplier`)
VALUES
('Awesome', 'Product', 'foo', 'foo', 'foor', '1.50', '1.50', '0', 'supplier1'),
('Awesome', 'Product', 'bar', 'foo', 'bar', '1.25', '1.75', '0', 'supplier2');
Select:
SELECT
`SubProduct`,
`Product`,
`Feature1`,
`Feature2`,
`Feature3`,
MIN(`Price1`),
`Price2`,
`Price3`,
`Supplier`
FROM `Products`
GROUP BY `SubProduct`, `Product`
ORDER BY `SubProduct`, `Product`;
You can see that at http://sqlfiddle.com/#!2/c0543/1/0
I get the frist inserted row with the content of the column price1 from the second inserted row.
I expect to get the complete row with the right features, supplier and other columns. In this example it should be the complete second inserted row, because it has the lowest price in column price1.
You need to get the MIN price rows and then JOIN those rows with the main table, like this:
SELECT
P.`SubProduct`,
P.`Product`,
P.`Feature1`,
P.`Feature2`,
P.`Feature3`,
`Price` AS Price1,
P.`Price2`,
P.`Price3`,
P.`Supplier`
FROM `Products` AS P JOIN (
SELECT `SubProduct`, `Product`, MIN(`Price1`) AS Price
FROM `Products`
GROUP BY `SubProduct`, `Product`
) AS `MinPriceRows`
ON P.`SubProduct` = MinPriceRows.`SubProduct`
AND P.`Product` = MinPriceRows.`Product`
AND P.Price1 = MinPriceRows.Price
ORDER BY P.`SubProduct`, P.`Product`;
Working Demo: http://sqlfiddle.com/#!2/c0543/20
Here what I have done is to get a temporary recordset as MinPriceRows table which will give you MIN price per SubProduct and Product. Then I am joining these rows with the main table so that main table rows can be reduced to only those rows which contain MIN price per SubProduct and Product.
Try with this:
SELECT
`p`.`SubProduct`,
`p`.`Product`,
`p`.`Feature1`,
`p`.`Feature2`,
`p`.`Feature3`,
`p`.`Price1`,
`p`.`Price2`,
`p`.`Price3`,
`p`.`Supplier`
FROM `Products` `p`
inner join (select MIN(`Price1`)as `Price1`
From `Products`
) `a` on `a`.`Price1` = `p`.`Price1`
ORDER BY `p`.`SubProduct`, `p`.`Product`;
demo: http://sqlfiddle.com/#!2/c0543/24
This works...
SELECT
P1.`SubProduct`,
P1.`Product`,
P1.`Feature1`,
P1.`Feature2`,
P1.`Feature3`,
P1.`Price1`,
P1.`Price2`,
P1.`Price3`,
P1.`Supplier`
FROM `Products` P1
INNER JOIN `Products` P2 ON P1.SubProduct = P2.SubProduct AND P1.Product = P2.Product
WHERE P1.Price1 < P2.Price1
I do have two tables:
Quest
- (int) id
- (text) characters
User
- (int) id
- (text) characters
Entries look like this:
Quest
id | characters
1 | abcdefgh
2 | mkorti
3 | afoxi
4 | bac
User
id | characters
1 | abcd
Now I want to select the easiest Quest for User. The easiest quest is the one if the most intersections of quest.characters and user.characters. So in this example the list would look like this (for user.id = 1):
questid | easiness
4 | 100
1 | 50
3 | 40
2 | 0
The easiness simply show how many percent was matched. Is it possible with MySQL to make intersections of columns like this? What's the performance like? In fact I do have relations as well (quest -> character and user -> characters), however I guess it's not very performant. As there are a few thousand quests and also a few thousand characters.
Update #1
Okay, relational still seems the way to go, okay. Now my tables look like this:
CREATE TABLE IF NOT EXISTS `quest` (
`questid` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`questid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `questcharacters` (
`questid` int(10) unsigned NOT NULL,
`characterid` int(10) unsigned NOT NULL,
PRIMARY KEY (`questid`,`characterid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `single_character` (
`characterid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`single_char` varchar(10) NOT NULL,
PRIMARY KEY (`characterid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `user` (
`userid` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `usercharacters` (
`userid` int(10) unsigned NOT NULL,
`characterid` int(10) unsigned NOT NULL,
PRIMARY KEY (`userid`,`characterid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
PS.: Don't wonder why single_char has VARCHAR(10) as data type, but I will use multi-byte values and I don't know how MySQL handles them for char(1). So I just was generous there.
Update #2
My query for now is:
SELECT usercharacters.userid, questcharacters.questid
FROM `usercharacters`
LEFT OUTER JOIN questcharacters ON usercharacters.characterid = usercharacters.characterid
GROUP BY questcharacters.questid, usercharacters.userid;
But how to calculate the easiness/overlapping characters? On which field do I have to apply COUNT()?
Update #3
Okay, seems like I got it working with this query (uses a subselect):
SELECT usercharacters.userid as uid, questcharacters.questid as qid, (SELECT COUNT(questcharacters.characterid) FROM questcharacters LEFT OUTER JOIN usercharacters ON questcharacters.characterid = usercharacters.characterid WHERE questcharacters.questid = qid) as questcount
FROM `usercharacters`
LEFT OUTER JOIN questcharacters ON usercharacters.characterid = usercharacters.characterid
GROUP BY questcharacters.questid, usercharacters.userid;
Update #4
SELECT usercharacters.userid as uid, questcharacters.questid as qid, (SELECT COUNT(questcharacters.characterid) FROM questcharacters LEFT OUTER JOIN usercharacters ON questcharacters.characterid = usercharacters.characterid WHERE questcharacters.questid = qid) as user_knows, (SELECT COUNT(questcharacters.characterid) FROM questcharacters WHERE questcharacters.questid = qid) as total_characters
FROM `usercharacters`
LEFT OUTER JOIN questcharacters ON usercharacters.characterid = usercharacters.characterid
GROUP BY questcharacters.questid, usercharacters.userid
ORDER BY total_characters / user_knows DESC;
Only thing missing now: Selecting the easyiness. (As in the ORDER BY clause). Anyone knows how to do this?
So this is my final and working solution:
SELECT usercharacters.userid AS uid,
questcharacters.questid AS qid,
(SELECT Count(questcharacters.characterid)
FROM questcharacters
LEFT OUTER JOIN usercharacters
ON questcharacters.characterid =
usercharacters.characterid
WHERE questcharacters.questid = qid) AS user_knows,
(SELECT Count(questcharacters.characterid)
FROM questcharacters
WHERE questcharacters.questid = qid) AS total_characters,
(SELECT ( Count(questcharacters.characterid) / (SELECT
Count(questcharacters.characterid)
FROM questcharacters
WHERE
questcharacters.questid = qid) )
FROM questcharacters
LEFT OUTER JOIN usercharacters
ON questcharacters.characterid =
usercharacters.characterid
WHERE questcharacters.questid = qid) AS ratio
FROM `usercharacters`
LEFT OUTER JOIN questcharacters
ON usercharacters.characterid = usercharacters.characterid
GROUP BY questcharacters.questid,
usercharacters.userid
ORDER BY ratio DESC;
Do I really need that many sub-selects?
If you actually have questcharacter and usercharacters tables, then that is the best way to go:
SELECT uc.id AS userid,
qc.id AS qcid,
COUNT(*) AS NumCharacters,
COUNT(qc.char) AS Nummatches,
COUNT(qc.char) / count(*) AS Easiness
FROM UserCharacters uc
LEFT OUTER JOIN QuestCharacters qc ON uc.char = qc.char
WHERE uc.id = 1
GROUP BY uc.id, qc.id
ORDER BY easiness DESC
LIMIT 1
If you have them only as strings -- the SQL is not pretty. You have to do a cross join and lots of string manipulation. The best approach is to have things more normalized in the form of a relational database (one row per list element), rather than having lists embedded in strings.
I am fetching data from multiple table having 600 thousand of records. But is taking a lot and lot of time to fetch it.
Please let me know how can i shorten the time to fetch it.
I also have used the LIMIT case, but still no improvement.
My query is:
SELECT DISTINCT
tf_history.thefind_id,
tf_product.product_id,
tf_product.`name`,
tf_product.product_url,
tf_product.image_tpm,
tf_product.image_thefind,
tf_product.image_accuracy,
(SELECT MIN(tf_h.price)
FROM tf_history AS tf_h
WHERE tf_h.thefind_id = tf_history.thefind_id) as price,
oc_product.price AS priceTPM
FROM tf_product
LEFT JOIN tf_history ON tf_product.product_id = tf_history.product_id
AND tf_product.thefind_id = tf_history.thefind_id
LEFT JOIN oc_product ON tf_product.product_id = oc_product.product_id
WHERE tf_product.product_id = #product_id
MY table:
tf_history
history_id int(11) NO PRI auto_increment
thefind_id int(11) NO
product_id int(11) NO
price decimal(15,4) NO
date datetime NO
AND
tf_product
thefind_id int(11) NO PRI auto_increment
product_id int(11) NO
name varchar(255) NO
store_id int(11) NO
product_url varchar(255) NO
image_tpm varchar(255) NO
image_thefind varchar(255) NO
image_accuracy int(3) NO
date datetime NO
But when i use this query:
SELECT * from tf_history
i got the result in 0.641s, then what can be the issue?
When there was less record the, first query was running smoothly.
Finally got the result, by using Indexes
using index will solve your problem.
Move the subselect column into the join
SELECT DISTINCT
th.thefind_id,
tp.product_id,
tp.`name`,
tp.product_url,
tp.image_tpm,
tp.image_thefind,
tp.image_accuracy,
th.price,
op.price AS priceTPM
FROM tf_product tp
LEFT JOIN (SELECT thefind_id, product_id, MIN(price) as price
FROM tf_history
group by thefind_id, product_id) th ON tp.product_id = th.product_id
AND tp.thefind_id = th.thefind_id
LEFT JOIN oc_product op ON tp.product_id = op.product_id
WHERE tp.product_id = #product_id
I cannot seem to get this MySQL query right. My table contains yearly inventory data for retail stores. Here's the table schema:
CREATE TABLE IF NOT EXISTS `inventory_data` (
inventory_id int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
store_id smallint unsigned NOT NULL,
inventory_year smallint unsigned NOT NULL,
shortage_dollars decimal(10,2) unsigned NOT NULL
)
engine=INNODB;
Every store is assigned to a district which in this table (some non-relevant fields removed):
CREATE TABLE IF NOT EXISTS `stores` (
store_id smallint unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
district_id smallint unsigned not null
)
engine=INNODB;
I want to be able to retrieve the shortage dollar amounts for two given years for all the stores within a given district. Inventory data for each store is only added to the inventory_data table when the inventory is completed, so not all stores within a district will all be represented all the time.
This query works to return inventory data for all stores within a given district for a given year (ex: stores in district 1 for 2012):
SELECT stores.store_id, inventory_data.shortage_dollars
FROM stores
LEFT JOIN inventory_data ON (stores.store_id = inventory_data.store_id)
AND inventory_data.inventory_year = 2012
WHERE stores.district_id = 1
But, I need to be able to get data for stores within a district for two years, such that the data looks something close to this:
store_id | yr2011 | yr2012
For the specific result format that you need, you may try the following query:
SELECT `s`.`store_id`, `i`.`shortage_dollars` AS `yr2011`, `i1`.`shortage_dollars` AS `yr2012`
FROM `stores` `s`
LEFT JOIN `inventory_data` `i` ON `s`.`store_id` = `i`.`store_id`
AND `i`.`inventory_year` = 2011
LEFT JOIN `inventory_data` `i1` ON `s`.`store_id` = `i1`.`store_id`
AND `i1`.`inventory_year` = 2012
WHERE `s`.`district_id` = 1
Alternatively, you may as well try the next simpler query.
SELECT `s`.`store_id`, `i`.`inventory_year`, `i`.`shortage_dollars`
FROM `stores` `s`
LEFT JOIN `inventory_data` `i` ON `s`.`store_id` = `i`.`store_id`
WHERE `s`.`district_id` = 1
AND `i`.`inventory_year` IN (2011, 2012)
ORDER BY `s`.`store_id`, `i`.`inventory_year`
Hope it helps!
SELECT
stores.store_id,
inventory_data.inventory_year
inventory_data.shortage_dollars
FROM
(SELECT * FROM stores district_id = 1) stores
LEFT JOIN
(SELECT * FROM inventory_data
WHERE inventory_year IN (2011,2012)) inventory_data
USING (store_id)
;
or
SELECT
stores.store_id,
GROUP_CONCAT(inventory_data.shortage_dollars) dollars_per_year
FROM
(SELECT * FROM stores district_id = 1) stores
LEFT JOIN
(SELECT * FROM inventory_data
WHERE inventory_year IN (2011,2012)) inventory_data
USING (store_id)
GROUP BY stores.id,inventory_year;
I am trying to write a query that looks through all combo_items and only returns the ones where all sub_items that it references have Active=1.
I think I should be able to count how many sub_items there are in a combo_item total and then compare it to how many are Active, but I am failing pretty hard at figuring out how to do that...
My table definitions:
CREATE TABLE `combo_items` (
`c_id` int(11) NOT NULL,
`Label` varchar(20) NOT NULL,
PRIMARY KEY (`c_id`)
)
CREATE TABLE `sub_items` (
`s_id` int(11) NOT NULL,
`Label` varchar(20) NOT NULL,
`Active` int(1) NOT NULL,
PRIMARY KEY (`s_id`)
)
CREATE TABLE `combo_refs` (
`r_id` int(11) NOT NULL,
`c_id` int(11) NOT NULL,
`s_id` int(11) NOT NULL,
PRIMARY KEY (`r_id`)
)
So for each combo_item, there is at least 2 rows in the combo_refs table linking to the multiple sub_items. My brain is about to make bigbadaboom :(
I would just join the three tables usually and then combo-item-wise sum up the total number of sub-items and the number of active sub-items:
SELECT ci.c_id, ci.Label, SUM(1) AS total_sub_items, SUM(si.Active) AS active_sub_items
FROM combo_items AS ci
INNER JOIN combo_refs AS cr ON cr.c_id = ci.c_id
INNER JOIN sub_items AS si ON si.s_id = cr.s_id
GROUP BY ci.c_id
Of course, instead of using SUM(1) you could just say COUNT(ci.c_id), but I wanted an analog of SUM(si.Active).
The approach proposed assumes Active to be 1 (active) or 0 (not active).
To get only those combo-items whose all sub-items are active, just add WHERE si.Active = 1. You could then reject the SUM stuff anyway. Depends on what you are looking for actually:
SELECT ci.c_id, ci.Label
FROM combo_items AS ci
INNER JOIN combo_refs AS cr ON cr.c_id = ci.c_id
INNER JOIN sub_items AS si ON si.s_id = cr.s_id
WHERE si.Active = 1
GROUP BY ci.c_id
By the way, INNER JOIN ensures that there is at least one sub-item per combo-item at all.
(I have not tested it.)
See this answer:
MySQL: Selecting foreign keys with fields matching all the same fields of another table
Select ...
From combo_items As C
Where Exists (
Select 1
From sub_items As S1
Join combo_refs As CR1
On CR1.s_id = S1.s_id
Where CR1.c_id = C.c_id
)
And Not Exists (
Select 1
From sub_items As S2
Join combo_refs As CR2
On CR2.s_id = S2.s_id
Where CR2.c_id = C.c_id
And S2.Active = 0
)
The first subquery ensures that at least one sub_item exists. The second ensures that none of the sub_items are inactive.