mysql optimize query for paging - php

pls can anybody help me width this query?
I cannot find solution for it.
SELECT *,p.cat_id, cat_name FROM (
SELECT cat_id
FROM categories
ORDER BY cat_id
LIMIT 700000, 10
) o
JOIN categories p
ON p.cat_id = o.cat_id
ORDER BY p.cat_id
It is very fast on table with 800 000 records, but what I need is to sort data with order by clause and where claue,too - for paging. If I use order by it si very slow or result is not ordered correctly>
SELECT *, p.cat_id, cat_name FROM(
SELECT cat_id
FROM categories
LIMIT 700000, 10
) o JOIN categories p
ON p.cat_id = o.cat_id
ORDER BY p.cat_name
page 1
LIMIT 700000,5:
id name
12525525 car
15155151 carpet
1521512i zone
page 2
LIMIT 700005,5
id name
12525525 carefull
15155151 excellent
52151222 drive
I need result:
page 1 car
carpet
drive
excellent ... etc.
f.e. , this is very slow ofcourse >
SELECT *, p.cat_id, cat_name
FROM (
SELECT cat_id
FROM categories
**ORDER BY cat_name**
LIMIT 700000, 10
) o
JOIN categories p
ON p.cat_id = o.cat_id
ORDER BY p.cat_name
primary key cat_id, autoincrement
I have indexes on fields in table
Many thanks for help or some ideas

You didn't need to JOIN the same table, and you have to do the ORDER BY in an inner query to be ordered correctly with the WHWRE clause in the outer one:
SET #rownum = 0;
SELECT t.*, t.rank as TableRank
FROM
(
SELECT *, (#rownum := #rownum + 1) as rank
FROM categories c
ORDER BY c.cat_name
) t
WHERE rank BETWEEN ((#PageNum - 1) * #PageSize + 1)
AND (#PageNum * #PageSize)

Related

Getting only a random item of each category

Well i have two table, categories and items and i would like to get a random record of each of 10 random category.
tb_category
category_id PK
category_name
tb_items
item_id PK
category_id FK
My table tb_category has about 40 rows and tb_items has about 5k rows, i'm search performance.
SELECT * FROM
( SELECT c.category_id as cid, c.category_name, i.item_id FROM
tb_category c INNER JOIN
tb_items i ON c.category_id = i.category_id ORDER BY RAND() ) AS ShuffeledItems
GROUP BY ShuffeledItems.cid limit 10
I don't know if that is better way to do it.
Thanks.
I would be inclined to use a correlated subquery:
select c.*,
(select i.item_id
from items i
where i.category_id = c.category_id
order by rand()
limit 1
) as item_id
from tb_category c
order by c.id
limit 10;

Mysql group_concat(id) as ids in a left join and using ids to select all columns in id group

i have a table that contains some articles with it's own ID and shared SKU key.
I've tried to make the query with a left join and using group result to take all ids returned from the query.
My data structure is like that:
id - name - sku - ...
1 - felix - cat
2 - tom - cat - ...
3 - sylvester - cat - ...
4 - red - pen - ...
5 - blue - pen - ...
I tried to use this query:
SELECT * FROM `test`
[LEFT/RIGHT/INNER] JOIN
(
SELECT GROUP_CONCAT(DISTINCT id) AS idsgroup FROM `test` WHERE (attribute_name = 'sku') GROUP BY value_name LIMIT 0, 3
) bind
ON id IN (bind.idsgroup);
this query is wrong, it return only 1 id per group instead all ids selected from concat or in LEFT JOIN case, obviously all rows.
Any suggestion workaround to achieve the right result?
EDIT:
here a fiddle with the structure:
http://sqlfiddle.com/#!9/b6747a
And the query i tried into:
SELECT * FROM `view_test`
INNER JOIN
(
SELECT GROUP_CONCAT(DISTINCT entity_id) AS idsgroup FROM `view_test` WHERE (attribute_name = 'sku') GROUP BY value_name LIMIT 0, 3
) bind
ON entity_id IN (bind.idsgroup);
As this pic show, my result lost some ids, part of the group.
EDIT 2:
after i used FIND_IN_SET() suggested by Kickstart the result is the expected:
SELECT * FROM `view_test`
INNER JOIN
(
SELECT GROUP_CONCAT(DISTINCT entity_id) AS idsgroup FROM `view_test` WHERE (attribute_name = 'sku') GROUP BY value_name LIMIT 0, 3
) bind
ON FIND_IN_SET(entity_id, bind.idsgroup);
The simple fix would appear to be to use FIND_IN_SET for the join. But this is a bit of a hack and will not be that quick.
SELECT *
FROM `view_test`
INNER JOIN
(
SELECT GROUP_CONCAT(DISTINCT entity_id) AS idsgroup
FROM `view_test`
WHERE (attribute_name = 'sku')
GROUP BY value_name
LIMIT 0, 3
) bind
ON FIND_IN_SET(entity_id, bind.idsgroup);
Further not sure why you have a LIMIT on the sub query, especially without an order clause.
Possibly better to use a sub query to just get the DISTINCT entity_id with an attribute_name of sku and join against that.
SELECT *
FROM `view_test`
INNER JOIN
(
SELECT DISTINCT entity_id
FROM `view_test`
WHERE (attribute_name = 'sku')
) bind
ON view_test.entity_id = bind.entity_id
Something like this?
SELECT t.*, group_concat(t2.id) FROM `test` t
LEFT JOIN test t2 ON t2.attribute_name = 'sku' and t.id != t2.id
group by t.id;
row, and the list of all ids that have same SKU

SELECTing Products from products table

I want to select the rows from products table.
The products are season based.
each product row/entity contains a column named id_season
and seasons table looks like
id | season_name | active | created | modified
Season names are Year like 2016,2017,2018 ...
I want to select all the products from 2016 and 2017 which have same code
I have a simple select like
SELECT *
FROM products P
INNER JOIN seasons S ON S.id = P.id_season
WHERE S.active = 1
AND S.season_name IN ( YEAR(GETDATE()), YEAR(GETDATE()) + 1 )
but don't know how to refine it, to match the codes on products from different seasons.
Try with the below code..
;WITH cte_1
AS
(SELECT *,COUNT(p.code) OVER(partition by p.code Order by p.code) cnt
FROM products P
INNER JOIN seasons S ON S.id = P.id_season
WHERE S.active = 1
AND S.season_name IN ( YEAR(GETDATE()), YEAR(GETDATE()) + 1 )) -- or simply put IN ('2016','2017')
SELECT *
FROM cte_1
WHERE cnt>1
or you can use a subquery format as below.
SELECT *
FROM
(SELECT *,COUNT(p.code) OVER(partition by p.code Order by p.code) cnt
FROM products P
INNER JOIN seasons S ON S.id = P.id_season
WHERE S.active = 1
AND S.season_name IN ( YEAR(GETDATE()), YEAR(GETDATE()) + 1 )) t
WHERE t.nt>1
You can use inner query as follows:
SELECT * FROM products
where id_season in (Select id from seasons where
season_name IN ( YEAR(GETDATE()), YEAR(GETDATE()) + 1 ));

How to find top rated 3 product from each category using mysql query

I have Two table :
1) ratemaster
fields are : (1) id (2) userid (3) productid (4) rating
2) productmaster
fields are: (1) id (2) productname (3) category (4)author
note: rating from out of 5
whenever any user give rating to any product, i am inserting record in to ratemaster table like : 1(id) 1002(userid) 995(productid) 4(rating)
for each user i am storing data like above.
product master contain the categoryname , product name.
now i want to get top 3 rated product from each category how would i write mysql query to achieve below output
Output :
try this one-
SELECT a.id,a.productname,a.category,a.rating
FROM (SELECT prd.id,prd.productname,prd.category,rt.rating,
CASE
WHEN #category != prd.category THEN #rownum := 1
ELSE #rownum := #rownum + 1
END AS rank,
#category := prd.category AS var_category
FROM productmaster prd
JOIN ratemaster rt ON rt.productid=prd.id
JOIN (SELECT #rownum := NULL, #category := '') r
ORDER BY prd.category,rt.rating DESC) a
WHERE a.rank<4;
Since you added the PHP tag, I suggest simply calculating the rating of all entries and go on from there
SELECT p.*, SUM(r.rating)/COUNT(r.rating) AS rating
FROM productmaster p
INNER JOIN ratemaster r ON p.id = r.productid
GROUP BY p.id
ORDER BY p.category, rating DESC
This will give you a row for each product with its calculated rating (sum / number of ratings). If you are only interested in the top 3 of each category, do something like this
$top3 = array();
while ($row = /*..fetch logic*/) {
if (array_key_exists($row['categoryId'], $top3)) {
$top3[$row['categoryId']] = array();
}
if (count($top3[$row['categoryId']]) >= 3) {
//top 3 for this category are found because query is ordered
continue;
}
$top3[$row['categoryId']][] = array(
//id, name, ...
);
}
SELECT * from ratemaster JOIN productmaster ON (ratemaster.id = product.id) order by rating DESC LIMIT 3
I think that this should work if I understood it correctly. Hope it works

MySql inner join takes more than 10 seconds

I have two tables posts and followings
posts (id,userid,post,timestamp) 30 000 rows
and
followings(id_me,userid) 90 000 rows
I want to get lattest 10 posts form posts table based on the people i follow and my posts
SELECT p.*
FROM posts as p INNER JOIN
followings as f
ON (f.id_me=(my user id) AND p.userid=f.userid )
OR
p.userid=(my user id)
ORDER BY id DESC LIMIT 10
But it takes about 10-15 seconds to return. Thanks in advance!
First, remove the filter from the join clause, let the join just correlate the joining tables.
(
SELECT p.*
FROM posts as p
INNER JOIN followings as f ON p.userid=f.userid
where f.id_me=(my user id)
UNION
SELECT p.*
FROM posts as p
where p.userid=(my user id)
)
ORDER BY id DESC LIMIT 10
second, verify your indexes if that ids got no indexes it ill perform a full table scan for each cartesian product of both tables (30k x 90k =~ 3700k pairs being compared)
third, if you don't follow yourself you need a union from post you are following and your posts
Using an OR in SQL is a performance killer, try this:
SELEC p.*
FROM posts as p INNER JOIN
followings as f
ON (f.id_me=(my user id) AND p.userid IN (f.userid,(my user id)))
ORDER BY id DESC LIMIT 10
Do this query using union:
(SELECT p.*
FROM posts p INNER JOIN
followings f
ON (f.id_me=(my user id) AND p.userid=f.userid
)
union
(select p.*
from posts p
where p.userid=(my user id)
)
ORDER BY id DESC
LIMIT 10
If the two conditions never overlap, then use union all instead.
An OR condition like that prevents the query optimizer from making use of indexes. Use a UNION instead:
SELECT *
FROM (SELECT p.*
FROM posts as p
INNER JOIN followings as f
ON f.id_me=(my user id) AND p.userid=f.userid
UNION
SELECT *
FROM posts
WHERE userid = (my user id)) u
ORDER BY id DESC
LIMIT 10
It might be just me but I think your WHERE clause is in an inefficient location:
SELECT
p.*
FROM
posts p
INNER JOIN
followings f
ON p.userid=f.userid
WHERE
MyUserID IN (p.userid, f.id_me)
ORDER BY
id DESC
LIMIT
10
I read in comments that you have the required indexes. The problem is the query. Combining OR with a JOIN confuses the poor and (often) dumb optimizer. The LIMIT 10 should be helpful but the optimizer is not (yet) smart enough to make the best plan.
Try this query:
( SELECT p.*
FROM posts AS p
JOIN followings AS f
ON f.id_me = (my_user_id)
AND p.userid = f.userid
ORDER BY p.id DESC
LIMIT 10
)
UNION ALL
( SELECT p.*
FROM posts AS p
WHERE p.userid = (my_user_id)
ORDER BY p.id DESC
LIMIT 10
) AS x
ORDER BY id DESC
LIMIT 10 ;

Categories