use dynamic offset in a query - php

I have a query
SELECT SQL_CALC_FOUND_ROWS a.memberid, a.category_id, a.content, a.count_cid, a.importance
FROM tb_profilingdata a, tb_member b
WHERE a.memberid = b.memberid AND a.category_id IN ($catstr) AND a.memberid NOT IN ( $seen_txt) AND b.gender != '$gender'
ORDER BY a.memberid, a.category_id LIMIT $offset, 4500
Since my table is very large, i want to limit my query result to a certain limit.
An also choose a dynamic offset, so that i can get random set of values everytime i run the query.
Till now i was calculating random offset based on total number of rows in the table through PHP.
But if the offset value is larger than the total number of rows returned by the query, than the result would be empty.
So is there any way through which i don't have to load the entire table as well as set an appropriate random offset so that i can get random values?

Try this nested query:
SELECT c.* FROM (
SELECT a.memberid, a.category_id, a.content, a.count_cid, a.importance
FROM tb_profilingdata a, tb_member b
WHERE a.memberid = b.memberid AND a.category_id IN ($catstr) AND a.memberid NOT IN ($seen_txt) AND b.gender != '$gender'
ORDER BY RAND() LIMIT 4500
) c ORDER BY c.memberid, c.category_id
Note: you will be unable to get total count of rows in the table using SQL_CALC_FOUND_ROWS.
There exist several ways to optimize the ORDER BY RAND().

Use Turnery operator to get the offset from the url Like following
(isset($_REQUEST['offset']))?$offset=$_REQUEST['offset']:$offset=0;
for random offset use rand function
$offset=rand(0,totalrecord/4500);
and the pass this offset in your query
SELECT SQL_CALC_FOUND_ROWS a.memberid, a.category_id, a.content, a.count_cid, a.importance
FROM tb_profilingdata a, tb_member b
WHERE a.memberid = b.memberid AND a.category_id IN ($catstr) AND a.memberid NOT IN ( $seen_txt) AND b.gender != '$gender'
ORDER BY a.memberid, a.category_id LIMIT $offset, 4500

Related

SQL query is taking a long time to execute

I have researched other questions and tried their solutions, but none helped me. How can I lower the execution time of this query?
SELECT c.id,c.title,c.time,u.username, (SELECT time FROM comments_read as io WHERE io.id=c.id AND io.uid=$logged_userid LIMIT 1) as read_time
FROM comments as c
LEFT JOIN users u ON u.uid=c.uid
WHERE c.deleted=0
ORDER by c.time DESC
LIMIT 20
Thank you so much.
This is your query:
SELECT c.id, c.title, c.time, u.username,
(SELECT io.time
FROM comments_read as io
WHERE io.id = c.id AND
io.uid = $logged_userid
LIMIT 1
) as read_time
FROM comments as c LEFT JOIN
users u
ON u.uid = c.uid
WHERE c.deleted = 0
ORDER by c.time DESC
LIMIT 20;
Indexes can be used to improve performance. I would start with comments(deleted, time, uid, id), users(uid), and comments_read(id, uid, time).
Note: using LIMIT without an ORDER BY is usually discouraged, because the results are unstable. You can get any matching row.
EDIT:
If the above indexes do not help, they might be getting confused by the JOIN. You can do the same thing as:
SELECT c.id, c.title, c.time,
(SELECT u.username FROM users u WHERE u.uid = c.uid) as username,
(SELECT io.time
FROM comments_read as io
WHERE io.id = c.id AND
io.uid = $logged_userid
LIMIT 1
) as read_time
FROM comments as c
WHERE c.deleted = 0
ORDER by c.time DESC
LIMIT 20;
This should take advantage of the above indexes and have no "file sort" step to slow things down.

select 2 random members from the database table MYSQL

I have a query below. On executing it, it returns all the results where i can see multiple records for each single memberid.
I want to select the profiles of two members randomly chosen without any performance issue.
SELECT
a.memberid,
a.category_id,
a.content,
a.count_cid,
a.importance
FROM tb_profilingdata a,
tb_member b
WHERE a.memberid = b.memberid
AND a.category_id IN($catstr)
AND a.memberid NOT IN('$mid',$seen)
AND b.gender = 'male'
ORDER BY a.memberid, a.category_id
I tried some queries for choosing one random record
SELECT
r1.memberid
FROM tb_profilingdata AS r1
JOIN (SELECT
(RAND() * (SELECT MAX(DISTINCT(memberid)) FROM tb_profilingdata)) AS memberid) AS r2
WHERE r1.memberid >= r2.memberid
ORDER BY r1.memberid ASC
LIMIT 1
But it chooses in total 1 record out of tb_profilingdata whereas i want records of one randomly chosen member.
I tried the same query with tb_member, but it is possible that a member present in tb_member might not have its entries in tb_profilingdata..
Please suggest me a good way out with least performance issues.
May be that will help you:
SELECT [something] FROM [source] WHERE [conditions] ORDER BY RAND() LIMIT 2
This query is untested unless you provide some sample data
SELECT
a.memberid,
a.category_id,
a.content,
a.count_cid,
a.importance
FROM tb_profilingdata a
left join (select
memberid
from tb_member
group by memberid
order by rand()) b
on a.memberid = b.memberid
WHERE a.category_id IN($catstr)
AND a.memberid NOT IN('$mid',$seen)
AND b.gender = 'male'
ORDER BY a.memberid, a.category_id

Combine two MYSQL SELECT queries [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to combine two Post/Category tables MYSQL SELECT queries into one
I have two MYSQL queries:
1) "SELECT ID,post_title,post_category,post_perma FROM ".TBL_POSTS."
WHERE published='1' AND page='0' ORDER BY ID ASC LIMIT 50"
2) "SELECT p.cat_ID,p.cat_nicename FROM ".TBL_CATEGORIES." n,
".TBL_CATEGORIES." p
WHERE n.lft BETWEEN p.lft
AND p.rgt AND n.cat_ID='".post_category."' ORDER BY p.lft
I use it like this:
$sql="SELECT ID, post_title, post_category,post_perma
FROM ".TBL_POSTS."
WHERE published='1'
AND page='0'
ORDER BY ID ASC LIMIT 50";
$result=mysql_query($sql);
while($row=mysql_fetch_assoc($result)){
$sql2="SELECT p.cat_ID, p.cat_nicename
FROM ".TBL_CATEGORIES." n, ".TBL_CATEGORIES." p
WHERE n.lft BETWEEN p.lft
AND p.rgt
AND n.cat_ID='".$row['post_category']."'
ORDER BY p.lft";
$result2=mysql_query($sql2);
while($row2=mysql_fetch_assoc($result2)){
$path.='/'.$row2['cat_nicename'];
}
$link.=''.$row['post_title'].'<br>';
$path='';
}
echo($link);
exit;
}
This is how I get path in the link.. now What i want is:
I want to combine both queries so, that I do not have run the second query in while loop ..
It gets very bad because if I receive 100 posts.. second query will run 100 times to fetch the path.
You can JOIN the two tables like so:
SELECT
p.ID,
p.post_title,
c.cat_nicename,
p.post_perma
FROM TBL_POSTS p
INNER JOIN TBL_CATEGORIES c
ON p.CategoryID = c.Cat_ID
WHERE p.published = '1'
AND p.page = '0'
ORDER BY p.ID ASC
LIMIT 50
Try to use MySql JOIN or UNION
You can use a UNION to combine all of them into a single query.
Really, you would need to do something like this so that all of the columns don't get combined into each other. Also, both select statements need to have the same number of columns being selected.
EDIT: Also, in MySQL UNION's you can only have 1 order by and it has to be in the last SELECT, so you will have to adjust your query accordingly.
(SELECT ID,
post_title,
post_category,
post_perma,
NULL AS cat_id,
NULL AS cat_nicename
FROM ".TBL_POSTS."
WHERE published='1'
AND page='0')
ORDER BY ID ASC LIMIT 50
UNION
(SELECT
NULL AS ID,
NULL AS post_title,
NULL AS post_category,
NULL AS post_perma,
p.cat_ID,
p.cat_nicename
FROM ".TBL_CATEGORIES." n,
".TBL_CATEGORIES." p
WHERE n.cat_ID='".post_category."'
AND n.lft BETWEEN p.lft AND p.rgt)
ORDER BY p.lft

Select n rows without LIMIT mysql

I have a problem. I would like to get only 300 rows from table without touching LIMIT. I need LIMIT for pagination. Is this possible in MySQL?
My current query:
SELECT a.title, a.askprice, a.picture, a.description, a.userid, a.id
FROM mm_ads AS a WHERE a.category = 227 AND a.status = 1
ORDER BY id DESC LIMIT 40,20
Edit:
Simple explanation: I need to get from a system last 300 ads but I need to keep pagination, because I don't want to have 300 rows listed in one page..
SELECT *
FROM (
SELECT a.title, a.askprice, a.picture, a.description, a.userid, a.id
FROM mm_ads AS a
WHERE a.category = 227 AND a.status = 1
ORDER BY id DESC
LIMIT 300
) t
LIMIT 40,20
If the purpose is to speed up the query, then you can create a composite index:
ALTER TABLE `mm_ads`
ADD INDEX `mm_ads_index` (`category` ASC, `status` ASC, `id` DESC);
Use SQL_CALC_FOUND_ROWS after your SELECT:
SELECT SQL_CALC_FOUND_ROWS *
EDIT:
And in php, run this row to get the amount of rows:
list($int_rows) = mysql_fetch_row(mysql_query("SELECT FOUND_ROWS()"));
This will go through all the rows, get the total amount, but not fetch all the rows.
EDIT2:
May have misunderstod your question, however this is a common solution for pagination.
Simple solution is
Fits count only amount of result you need and use it in your pagination
then use limit in your query to load data on each page
SELECT a.title, a.askprice, a.picture, a.description, a.userid, a.id
FROM mm_ads AS a WHERE a.category = 227 AND a.status = 1
ORDER BY id DESC LIMIT 40,20
it dosen't matter how large your data base is it only gives you the 20 results (although it search for full database)
One more thing that you can do is just fetch all the 300 rows from database and save it in an array and then paginate the array indexes

PostgreSQL: using row_number() in paged output

I have a PHP script displaying a list of players sorted by their "virtual money":
$sth = $db->prepare("
select u.id,
u.first_name,
u.city,
u.avatar,
m.money,
u.login > u.logout as online
from pref_users u, pref_money m where
m.yw=to_char(current_timestamp, 'IYYY-IW') and
u.id=m.id
order by m.money desc
limit 20 offset ?
");
$sth->execute(array($_GET['offset']));
To show a player position in the list I use a PHP variable $pos which is incremented in a loop while printing their names and further data.
I would like to have that position in the SQL statement instead of PHP for various reasons. So I'm trying the following:
$sth = $db->prepare("
select u.id,
row_number() + ? as pos,
u.first_name,
u.city,
u.avatar,
m.money,
u.login > u.logout as online
from pref_users u, pref_money m where
m.yw=to_char(current_timestamp, 'IYYY-IW') and
u.id=m.id
order by m.money desc
limit 20 offset ?
");
$sth->execute(array($_GET['offset'], $_GET['offset']));
But get the ERROR: window function call requires an OVER clause
I'm trying to add over(m.money) but get a syntax error.
I'm probably misunderstanding the Window Functions doc.
Check the user notes on: http://www.postgresql.org/docs/8.4/interactive/functions-window.html
You will need the Over() to contain the same order by clause as the whole query:
$sth = $db->prepare("
select u.id,
row_number() OVER (order by m.money desc) + ? as pos,
u.first_name,
u.city,
u.avatar,
m.money,
u.login > u.logout as online
from pref_users u, pref_money m where
m.yw=to_char(current_timestamp, 'IYYY-IW') and
u.id=m.id
order by m.money desc
limit 20 offset ?
");
$sth->execute(array($_GET['offset'], $_GET['offset']));
You want row_number() OVER (ORDER BY m.money) + ? etc.

Categories