I have the following query:
SELECT * FROM `product` INNER JOIN `shop`
ON `product`.shop_id= `shop`.id;
I wanted to get all of the products from all the shops I have, but I wanted to get 3 products max from each shop. Is there a way to specify MAX on each joins?
Here's my product table:
Here's my shop table:
Try this:
SELECT *
FROM (SELECT *
FROM (SELECT *, IF(#shop = (#shop:=p.shop_id), #id:=#id + 1, #id := 1) temp
FROM `product` p, (SELECT #shop:=0, #id:=1) AS A
ORDER BY p.shop_id, p.updated DESC) AS B
WHERE temp <= 3) AS C
INNER JOIN `shop` s ON C.shop_id= s.id;
Query:
SELECT *
FROM `product` p
INNER JOIN `shop` s
ON `p`.shop_id= `s`.id
WHERE p.id IN (SELECT p2.id
FROM `product` p2
WHERE p2.shop_id = s.id
ORDER BY p2.updated DESC
LIMIT 3)
OR maybe:
SELECT *
FROM `product` p
INNER JOIN `shop` s
ON `p`.shop_id= `s`.id
WHERE EXISTS (SELECT p2.id
FROM `product` p2
WHERE p2.shop_id = s.id
ORDER BY p2.updated DESC
LIMIT 3)
Specifying limits within a subquery is a bit challenging in MySQL (not impossible, but a bit complicated).
If you just want the three most recent product ids for each shop, and you can live with them on one row, then you can use group_concat(). The query is much simpler:
SELECT shop.*,
substring_index(group_concat(product.id order by product.updated desc), ',', 3) as ThreeProducts
FROM `product` INNER JOIN
`shop`
ON `product`.shop_id= `shop`.id
group by shop.id;
The results will place the product ids in a single field like this: '1,2,3'.
It is important to know the tables definitions in terms of primary keys, foreign keys, etc to come up with a SQL to solve the problem. From the images it is not clear if product.id is unique or not. I suspect there is possibly a data model definition issue here.
If the tables are not normalized to a necessary extent, it will be very difficult (sometime not possible) to read appropriate data back.
A reasonably normalized tables should look like.
Product(id primary key, ....)
Shop(id primary key,....)
and a relation table say.
Shop_Product (shop_id references Shop(id), prod_id references Product(id), ...)
It will be helpful to help you out if you could send table definitions.
try to use limit in your code. It may work
Related
I have 2 tables. Persons and Images.
One person may have many images.
I want to select a person with his primary image BUT(!), if none of the images marked as isPrimary=1 bring the first one.
Only one or less images may be isPrimary=1
SELECT
*,
(
SELECT id
FROM Image
WHERES personId=r.id AND isPrimary=1
LIMIT 1
) AS primaryImageId
FROM Persons r
ORDER BY id DESC;
I did it with subselect, join is also good...
Thanks
You can use:
SELECT r.*,
(SELECT i.id
FROM Image i
WHERES i.personId = r.id
ORDER BY i.isPrimary DESC, i.id ASC
LIMIT 1
) AS primaryImageId
FROM Persons r
ORDER BY id DESC;
This orders the images, with the primary one first -- and then takes the first image.
You should learn to qualify all column references -- this is especially important when using correlated subqueries. I assume that the alias r makes sense on the persons table in your native language. Otherwise, use something sensible such as p.
I have a database with tree tables,
person: id, bio, name
book: id, id_person, title, info
file: id, id_book, location
Other information: Book is about ~50,000 rows, File is about ~ 300,000 rows.
What I'm trying to do is to select 12 different authors and select just one book and from that book select location from the table file.
What I tried is the following:
SELECT DISTINCT(`person`.`id`), `person`.`name`, `book`.`id`, `book`.`title`, `book`.`info`, `file`.`location`
FROM `person`
INNER JOIN `book`
ON `book`.`id_person` = `person`.`id`
INNER JOIN `file`
ON `file`.`id_book` = `book`.`id`
LIMIT 12
I have learned that the DISTINCT does not work the way one might expect. Or is it me that I'm missing something? The above code returns books from the same author and goes with the next one. Which is NOT what I want. I want 1 book from each one of the 12 different authors.
What would be the correct way to retrieve this information from the database? Also, I would want to retrieve 12 random people. Not people that are stored in consecutive order in the database,. I could not formulate any query with rand() since I couldn't even get different authors.
I use MariaDB. And I would appreciate any help, especially help that allows to me do this with great performance.
In MySQL, you can do this, in practice, using GROUP BY
SELECT p.`id`, p.`name`, b.`id`, b.`title`, b.`info`, f.`location`
FROM `person` p INNER JOIN
`book` b
ON b.`id_person` = p.`id` INNER JOIN
`file` f
ON f.id_book = b.id
GROUP BY p.id
ORDER BY rand()
LIMIT 12;
However, this is not guaranteed to return the non-id values from the same row (although it does in practice). And, although the authors are random, the books and locations are not.
The SQL Query to do this consistently is a bit more complicated:
SELECT p.`id`, p.`name`, b.`id`, b.`title`, b.`info`,
(SELECT f.location
FROM file f
WHERE f.id_book = b.id
ORDER BY rand()
LIMIT 1
) as location
FROM (SELECT p.*,
(SELECT b.id
FROM book b
WHERE b.id_person = p.id
ORDER BY rand()
LIMIT 1
) as book_id
FROM person p
ORDER BY rand()
LIMIT 12
) p INNER JOIN
book b
ON b.id = p.book_id ;
Hello everyone I am working on phpmyadmin database. Whenever I try to execute query it takes too much time more than 10 mins to show results. Is there any way to speed it up. please response.
The query is
SELECT ib.*, b.brand_name, m.model_name,
s.id as sale_id, br.branch_code,br.branch_name,r.rentry_date,r.id as rid
from in_book ib
left join brand b on ib.brand_id=b.id
left join model m on ib.vehicle_id=m.id
left join re_entry r on r.in_book_id=ib.id
left join sale s on ib.id=s.in_book_id
left join branch br on ib.branch_id=br.id
where ib.id !=''
and ib.branch_id='65'
group by ib.id
order by r.id ASC,
count(r.in_book_id) DESC ,
ib.purchaes_date ASC,
ib.id ASC
there are almost 7 tables
make sure you got an index on every key you use to join the tables.
from http://dev.mysql.com/doc/refman/5.5/en/optimization-indexes.html:
The best way to improve the performance of SELECT operations is to create indexes on one or more of the columns that are tested in the query. The index entries act like pointers to the table rows, allowing the query to quickly determine which rows match a condition in the WHERE clause, and retrieve the other column values for those rows. All MySQL data types can be indexed.
.. this of course also applies to the JOIN conditions.
You don't list any such indexes, however, I would start with the following suggested indexes
table index
in_book ( branch_id, id, brand_id, vehicle_id )
brand ( id, brand_name )
model ( id, model_name )
re_entry ( in_book_id, id, reentry_date )
sale ( in_book_id, id )
branch ( id )
Also, with MySQL, you can use a special keyword "STRAIGHT_JOIN" which tells the engine to query in the order you have selected the tables... Although you are doing LEFT JOINs, I don't think it will matter as it appears the secondary tables are all lookup type of tables and in_book is your primary. But as just a try it would be..
SELECT STRAIGHT_JOIN (...rest of query...)
I have come across a query which, while working, is hard to understand(make changes) and in my opinioin is un-optimized.
SELECT cp.`order` AS `order`, cp.parent_id, cp.id AS category_id, cp.stub, cp.name as category_name, dc.deals_in_cat, d.*
FROM category_parent cp,
(
SELECT id, title, subtitle, image, image_m, discount, itemid, price, new_price, catalog_id, property_id, seller_id, category FROM deals
WHERE deals.category = 1
AND itemid NOT IN (156785431)
ORDER BY e_order LIMIT 8
) d,
(
SELECT a.`id` AS parent_id, COUNT( DISTINCT c.`itemid` ) AS deals_in_cat
FROM `category_parent` AS a
LEFT JOIN `navigation_filters_weightage` AS d ON a.`id` = d.`cat_id`,
`deals_parent_cat` AS b,
`deals` AS c
WHERE a.`parent_id` = b.`id`
AND c.`category` = a.`id` GROUP BY a.id ORDER BY b.`order` ASC , a.`order` ASC
) AS dc
WHERE cp.id = d.category
AND cp.active = '1'
AND dc.parent_id = cp.id;
Can you please suggest ways on making it more simpler.
Thanks
As noted in comments, indexes are probably a big factor for your query.
I would start by confirming you have at least the following indexes available
table index
deals (category, e_order, itemid )
category_parent (active, id )
Typically, I would have the itemID before the order by since it is part of the WHERE clause, but since you are getting all EXCEPT one ID, I think the order-by clause column would help out more.
One additional note... Your "dc" query for getting counts is doing the counts for ALL entries, but your outer query is only considering "active=1". I would add this qualifier in your "dc" query as well via
WHERE a.Active='1' AND -- rest of your criteria
Finally, being a website, doing counts on the fly repeatedly is always going to be a big performance hit. As suggested in other posts and again here, you may be better off by adding a column to your category_parents table for "Deals_In_Cat" and have it updated via a trigger whenever any deals are added or removed. This way, you get the count done ONCE when added/deleted, but all future references no longer requires the count being applied. This will probably be the best thing you can apply for performance.
Given the following (very simplified) mysql table structure:
products
id
product_categories
id
product_id
status (integer)
product_tags
id
product_id
some_other_numeric_value
I am trying to find every product that has an association to a certain product_tag, and that a relation to at least one category whichs status-attribute is 1.
I tried the following query:
SELECT *
FROM `product` p
JOIN `product_categories` pc
ON p.`product_id` = pc.`product_id`
JOIN `product_tags` pt
ON p.`product_id` = pt.`product_id`
WHERE pt.`some_value` = 'some comparison value'
GROUP BY p.`product_id`
HAVING SUM( pc.`status` ) > 0
ORDER BY SUM( pt.`some_other_numeric_value` ) DESC
Now my problem is: The SUM(pt.some_other_numeric_value) returns unexpected values.
I realized that if the product in question has more then one relation to the product_categories table, then every relation to the product_tags table is counted as many timed as there are relations to the product_categories table!
For example: If product with id=1 has a relation to product_categories with ids = 2, 3 and 4, and a relation with the product_tags with ids 5 and 6 - then if I insert a GROUP_CONCAT(pt.id), then it does give 5,6,5,6,5,6 instead of the expected 5,6.
At first I suspected it was a problem with the join type (left join, right join, inner join, and so on), so I tried every join type that I know of, but to no avail. I also tried to include more id-fields into the GROUP BY clause, but this didnĀ“t solve the problem either.
Can somebody explain to me what is actually going wrong here?
You join a "main" (product) table to two tables (tags and categories) via 1:n relationships, so this is expected, you are creating a mini cartesian product. For those products that have both more than one associated tags and more than one associated categories, multiple rows are created in the result set. If you Group By, you have wrong results in aggregate functions.
One way to avoid this is to remove one of the two joins, which is a valid startegy if you don't need results from that table. Say you don't need anything in the SELECT list from the product_categories table. Then you can use a semi-join (the EXISTS subquery)to that table:
SELECT p.*,
SUM( pt.`some_other_numeric_value` )
FROM `product` p
JOIN `product_tags` pt
ON p.`product_id` = pt.`product_id`
WHERE pt.`some_value` = 'some comparison value'
AND EXISTS
( SELECT *
FROM product_categories pc
WHERE pc.product_id = pc.product_id
AND pc.status = 1
)
GROUP BY p.`product_id`
ORDER BY SUM( pt.`some_other_numeric_value` ) DESC ;
Another way to circumvent this problem is - after the GROUP BY MainTable.pk - to use DISTINCT inside the COUNT() or GROUP_CONCAT() aggregate functions. This works but you can't use it with SUM(). So, it's not useful in your specific query.
A third option - which works always - is to first group by the two (or more) side tables and then join to the main table. Something like this in your case:
SELECT p.* ,
COALESCE(pt.sum_other_values, 0) AS sum_other_values
COALESCE(pt.cnt, 0) AS tags_count,
COALESCE(pc.cnt, 0) AS categories_count,
COALESCE(category_titles, '') AS category_titles
FROM `product` p
JOIN
( SELECT product_id
, COUNT(*) AS cnt
, GROUP_CONCAT(title) AS category_titles
FROM `product_categories` pc
WHERE status = 1
GROUP BY product_id
) AS pc
ON p.`product_id` = pc.`product_id`
JOIN
( SELECT product_id
, COUNT(*) AS cnt
, SUM(some_other_numeric_value) AS sum_other_values
FROM `product_tags` pt
WHERE some_value = 'some comparison value'
GROUP BY product_id
) AS pt
ON p.`product_id` = pt.`product_id`
ORDER BY sum_other_values DESC ;
The COALESCE() are not strictly needed there - just in case you chnage the inner joins to LEFT outer joins.
you cant order by a sum function
instead you could do it like that
SELECT * ,SUM( pt.`some_other_numeric_value` ) as sumvalues
FROM `product` p
JOIN `product_categories` pc
ON p.`product_id` = pc.`product_id`
JOIN `product_tags` pt
ON p.`product_id` = pt.`product_id`
WHERE pt.`some_value` = 'some comparison value'
GROUP BY p.`product_id`
HAVING SUM( pc.`status` ) > 0
ORDER BY sumvalues DESC