I have in my page 5 queries that im wishing to have just one query if its possible , i dont know if there is a better strategy to what im doing or its ok .
this is my main query to load the books
$mess = $mysqli->prepare('SELECT * from ( SELECT m.id, cm.voteup,
cm.votedown,cr.book_descr FROM books m
INNER JOIN books_vote cm ON cm.bookid = m.id
INNER JOIN book_info cr ON cr.bookid = m.id
WHERE bookid = ? ORDER BY m.date DESC LIMIT 30)ddd
ORDER BY m.date ASC');
$mess->bind_param("i", $book);
$mess->execute();
$mess->store_result();
$mess->bind_result($id,$voteup,$votedown);
while($row = $mess->fetch()){
// im fetching here in this <div class='areabooks'>
}
Then i reset the query to fetch in other div with IF condition
$mess->data_seek(0);
while($row = $mess->fetch()){
if($voteup - $votedown >= 5){
//I fetch here in this <div class='areabooks2'>
}
}
Then im cheking the top MAX(voteup - votedown ) with this query
$messtop = $mysqli->prepare('SELECT ..........
INNER JOIN ...
INNER JOIN ( select id ,MAX(voteup - votedown ) as maxe
from books
where voteup - votedown >= 5
group by id ) tt
WHERE bookid = ?
ORDER BY maxe DESC,cm.votedown asc,cm.voteup DESC
limit 1');
$messtop->bind_param("i", $book);
$messtop->execute();
$messtop->store_result();
$messtop->bind_result($id,$voteup,$votedown);
$messtop->fetch();
// then i fetch in this <div class='topbook' >
Then i reset the first query again to fetch in other div with IF condition
$mess->data_seek(0);
while($row = $mess->fetch()){
if($votedown - $voteup >= 5){
//I fetch here in this <div class='areabooks3' >
}
}
Then im getting the top MAX(votedown - voteup ) with this query
$messtop = $mysqli->prepare('SELECT ..........
INNER JOIN ...
INNER JOIN ( select id ,MAX(votedown - voteup) as maxe
from books where votedown - voteup >= 5
group by id ) tt
WHERE bookid = ?
ORDER BY maxe DESC,cm.voteup ASC,cm.votedown DESC
limit 1');
$messdown->bind_param("i", $book);
$messdown->execute();
$messdown->store_result();
$messdown->bind_result($id,$voteup,$votedown);
$messdown->fetch();
// then i fetch in this <div class='topbottonbook' >
What im looking for is if there is a strategy to use just one query and fetch it in all these divs , those divs are separated like that .
<first div>
<second div>
<3rd div>
<4th div>
5th div
How to simplify this thanks ?
I doubt if the 3 queries can be combined into one.
First query:
Postpone the JOIN until you have the 30 'latest' bookids. That is, move the JOINs out to the outer SELECT.
Also have INDEX(id, date). But I am confused. Ordinarily id is the PRIMARY KEY, hence unique. Yet the query implies that one book has multiple dates. What gives? (Please provide SHOW CREATE TABLE.)
The mysql commandline tool (or Workbench or phpmyadmin) is your friend. Manually run the queries using one of them. I think that you will find errors. Please fix.
Related
I'm trying to substitute my join SQL code to a different code without any of JOIN statements for faster data retrieval. However, i'm getting the error below.
#1242 - Subquery returns more than 1 row
What i would like to do, get all rows from one table 'tbl_my_itemlist' and JOIN to other more tables, tbl_register and tbl_register without using JOIN statements.
The Code using JOIN statement (works fine).
SELECT
tbl_screenshots.screenshot_image_url,
mit.my_itemlist_id,
mit.item_name,
mit.item_initial_cost,
mit.item_offer_cost,
mit.offer_date_from,
mit.offer_date_to
FROM
(
SELECT
my_itemlist_id
FROM
tbl_my_itemlist
WHERE
offer_date_from >='2020-10-20' AND offer_date_to <= '2020-10-30' AND
item_deleted_status = 'active'
) mlist
JOIN tbl_my_itemlist mit ON
mit.my_itemlist_id = mlist.my_itemlist_id
RIGHT JOIN tbl_screenshots ON mit.my_itemlist_id =
tbl_screenshots.my_itemlist_id
RIGHT JOIN tbl_register ON tbl_register.register_id = mit.register_id
GROUP BY
mit.my_itemlist_id
ORDER BY mit.offer_date_to ASC LIMIT 2
The code i'm substituting the JOIN statement code with.
SELECT
mit.my_itemlist_id,
mit.item_name,
mit.item_initial_cost,
mit.item_offer_cost,
mit.offer_date_from,
mit.offer_date_to,
(
SELECT
reg.business_name
FROM
tbl_register reg
WHERE
reg.register_id = mit.register_id
) reg_sql,
(
SELECT
sshots.screenshot_image_url
FROM
tbl_screenshots sshots
WHERE
sshots.my_itemlist_id = mit.my_itemlist_id
) sshots_sq
FROM
tbl_my_itemlist mit
WHERE
mit.offer_date_from >= '2020-10-20' AND mit.offer_date_to <= '2020-10-30' AND mit.item_deleted_status = 'active'
GROUP BY
mit.my_itemlist_id
ORDER BY
mit.offer_date_to ASC
LIMIT 2
I'm trying to build an SQL query that can retrieve data from million records within very short period of time as compared to using the JOIN statement.
I have a query regarding join . Basically there are two tables product and product_boost . The table product_boost has the product_id as foreign key which is also in product table .
I want to get the data using join which is available in both the tables, and if not only data from first table will come.
I am using right outer join, here is my query
SELECT * FROM `vefinder_product`
RIGHT OUTER JOIN `vefinder_product_boost` ON `vefinder_product_boost`.`product_id`=`vefinder_product`.`product_id`
WHERE `vefinder_product`.`status` = 1
AND `vefinder_product`.`post_type` != 5
AND `vefinder_product`.`country` IN('348')
AND `vefinder_product`.`product_stock` >0
AND `vefinder_product`.`product_in_stock` = 1
AND `vefinder_product_boost`.`target_age_from` >= 20
AND `vefinder_product_boost`.`target_age_to` <= 40
ORDER BY `vefinder_product`.`is_boosted` DESC,
`vefinder_product`.`is_sponsered` DESC,
`vefinder_product`.`created_date` DESC LIMIT 21
How can i achive the desired thing , because this is not working. I am using codeigniter php.
Use Left join instead, if you want to get all the data from first (leftmost) table.
Any Where conditions on tables other than the first table (leftmost), should be shifted to ON condition in Left Join. Otherwise, Where would filter out unmatched rows also (null in the right side tables).
Try the following instead:
SELECT *
FROM `vefinder_product`
LEFT OUTER JOIN `vefinder_product_boost`
ON `vefinder_product_boost`.`product_id`=`vefinder_product`.`product_id` AND
`vefinder_product_boost`.`target_age_from` >= 20 AND
`vefinder_product_boost`.`target_age_to` <= 40
WHERE `vefinder_product`.`status` = 1 AND
`vefinder_product`.`post_type` != 5 AND
`vefinder_product`.`country` IN('348') AND
`vefinder_product`.`product_stock` >0 AND
`vefinder_product`.`product_in_stock` = 1
ORDER BY `vefinder_product`.`is_boosted` DESC,
`vefinder_product`.`is_sponsered` DESC,
`vefinder_product`.`created_date` DESC
LIMIT 21
Use left join and put where condition in ON cluase
SELECT * FROM `vefinder_product`
left OUTER JOIN `vefinder_product_boost` ON `vefinder_product_boost`.`product_id`=`vefinder_product`.`product_id`
and `vefinder_product`.`status` = 1
AND `vefinder_product`.`post_type` != 5
AND `vefinder_product`.`country` IN('348')
AND `vefinder_product`.`product_stock` >0
AND `vefinder_product`.`product_in_stock` = 1
AND `vefinder_product_boost`.`target_age_from` >= 20
AND `vefinder_product_boost`.`target_age_to` <= 40
ORDER BY `vefinder_product`.`is_boosted` DESC,
`vefinder_product`.`is_sponsered` DESC,
`vefinder_product`.`created_date` DESC LIMIT 21
you can use third party software like SQLyog.
it is very simple for join query just build query with UI and assign relation to that fields between tables.
in sqlyog you can get data from multiple tables not only two tables.
because i am currently using this software for time saving.
I have this nice and neat way of loading posts for my blog website to fit specified page:
$end = $count - ($page * $ppp); //count = select max(id) from art;
$start = $count- ($page * $ppp) - ($ppp-1);
$nxtpage = $page +1; //this is set beforehand in case no posts exists
$prvpage = $page == 0 ? 0 : $page -1;
$sql = "SELECT
a.id AS id,
a.nazwa AS nazwa,
a.data AS data,
a.wstep AS wstep,
a.imgs AS imgs,
a.zdj AS zdj,
GROUP_CONCAT(t.nazwa) all_tags
FROM
art a INNER JOIN tagart ta ON a.id = ta.id INNER JOIN tags t ON t.idt = ta.idt
WHERE a.id BETWEEN $start AND $end
GROUP BY a.id
ORDER BY a.id desc";
This way I can load only a specified numer of posts depended by blogs page (pagination).
There is a pretty big problem with it tho.
Lets say my client make a mistake like writing BLACK PPL somewhere in one article half a year ago, and now he has to delete it.
Or even better, has to delete about 10 posts from it. When middle posts are deleted, the whole alrorithm gets messed up, because it scans posts based it their ID.
So my question here for you is what better way of picking the posts I could use, that would always get the correct order of the posts?
It looks like you're trying to implement your own way of doing LIMIT, which is a MySQL feature that handles pagination. Instead of manually defining your start and end ID's, you should be looking to order your posts and then only fetching the next X posts, no matter what their ID's are. Here's how you would do that
$start = ($page - 1) * $ppp;
$sql = "SELECT
a.id AS id,
a.nazwa AS nazwa,
a.data AS data,
a.wstep AS wstep,
a.imgs AS imgs,
a.zdj AS zdj,
GROUP_CONCAT(t.nazwa) all_tags
FROM
art a INNER JOIN tagart ta ON a.id = ta.id INNER JOIN tags t ON t.idt = ta.idt
GROUP BY a.id
ORDER BY a.id DESC
LIMIT $start,$ppp";
LIMIT is used as either
LIMIT 5 #Fetch first 5 items
or
LIMIT 5,10 #Starting from the 5th item, fetch the next 10 items
Instead of using max(id) to determine the number of posts, use count(id) or count(*) to actually count them. If a post gets deleted, the count can take that into account.
In the select query use limit to select the range of posts to show.
As you already figured out, you should not be relying on MAX(id) to count the number of records.
I would just use a separate count query to get the count. It's simple and relatively inexpensive:
SELECT COUNT (id) FROM art
And as others have already mentioned, use LIMIT to paginate instead of limiting by id.
Can anyone tell me how to make this query faster?
$session_id = '000000000015';
$start = 0;
$finish = 30;
try {
$stmt = $conn->prepare("SELECT TOPUSERS.ID, TOPUSERS.USERNAME, TOPUSERS.NAME, TOPUSERS.NAME2, TOPUSERS.PHOTO, TOPUSERS.FB_USERID, TOPUSERS.IMAGE_TYPE, TOPUSERS.TW_USERID, TOPUSERS.TW_PHOTO,
COALESCE((SELECT COUNT(USERS_BUCKETS.ID) FROM USERS_BUCKETS WHERE USERS_BUCKETS.USERID=TOPUSERS.ID),0) AS NUM_ALL,
COALESCE((SELECT SUM(CASE WHEN USERS_BUCKETS.STATUS='Completed' THEN 1 ELSE 0 END) FROM USERS_BUCKETS WHERE USERS_BUCKETS.USERID=TOPUSERS.ID),0) AS NUM_DONE,
COALESCE((SELECT COUNT(USERS_LIKES.ID) FROM USERS_LIKES WHERE USERS_LIKES.USERID=TOPUSERS.ID),0) AS NUM_LIKES,
(SELECT USERS_BUCKETS.BUCKETID FROM USERS_BUCKETS WHERE USERS_BUCKETS.USERID=TOPUSERS.ID ORDER BY USERS_BUCKETS.DATE_MODIFIED DESC LIMIT 1) AS RECENT_BUCKET,
(SELECT BUCKETS_NEW.BUCKET_NAME FROM BUCKETS_NEW WHERE BUCKETS_NEW.ID=RECENT_BUCKET) AS REC,
COALESCE((SELECT COUNT(ID) FROM FOLLOW WHERE FOLLOW.USER_ID=TOPUSERS.ID),0) AS FOLLOWING,
COALESCE((SELECT COUNT(ID) FROM FOLLOW WHERE FOLLOW.FOLLOW_ID=TOPUSERS.ID),0) AS FOLLOWERS,
(SELECT IF(TOPUSERS.NAME = '',0,1) + IF(TOPUSERS.BIO = '',0,1) + IF(TOPUSERS.LOCATION = '',0,1) + IF(TOPUSERS.BIRTHDAY = '0000-00-00',0,1) + IF(TOPUSERS.GENDER = '',0,1)) as COMPLETENESS,
CASE WHEN ? IN (SELECT USER_ID FROM FOLLOW WHERE FOLLOW_ID = TOPUSERS.ID) THEN 'Yes' ELSE 'No' END AS DO_I_FOLLOW_HIM
FROM TOPUSERS
LEFT JOIN FOLLOW ON TOPUSERS.ID = FOLLOW.FOLLOW_ID
LEFT JOIN USERS_BUCKETS ON USERS_BUCKETS.USERID=TOPUSERS.ID
LEFT JOIN BUCKETS_NEW ON BUCKETS_NEW.ID=USERS_BUCKETS.BUCKETID
WHERE NOT TOPUSERS.ID = ?
GROUP BY TOPUSERS.ID ORDER BY TOPUSERS.RANDOM, TOPUSERS.USERNAME LIMIT $start, $finish");
When I run this in a browser it takes about 7 seconds to load. Without a few lines (the COALESCE in the middle, the two SELECTS above and the line below them) the time is reduced to 3-4 seconds.
The result of the query is a list of people with names, profile picture and some data.
TL,DR: you need to rewrite the query.
You need to rewrite your query to make it more efficient. I had to rewrite a similar query at work last week and here is what I have done.
The structure of your query should look like this to be efficient:
select ...
...
from ...
join ...
where ...
what you have now is something like:
select ...
inner select
inner select
from ...
join ...
where ...
That's the inner selects that kill your query. You need to find a way to move the inner select into the from section. Especially that you already query the tables.
What you need to understand is that your inner selects run for every records you have. So if you have 10 records, it would be alright (speed wise). But with hundred or thousand of records, it would be very slow.
If you want more information on your query run it with the explain keyword in from of it.
All,
I am trying to display the latest post from my authors.
The posts are in one table, the author in another. The queries used were: join on a column called content_id, and I am trying to work with MAX(item_date) which is the date the post was published.
Here are some queries I am running and the output, I seem to be getting everything except the latest posts from all authors:
First (and this bit is working) we need to get information about the author:
$query="SELECT content_id, title, source_image, url FROM content_detail where approved='y'";
$result1 = $mysqli->query($query) or die($mysqli->error.__LINE__);
// GOING THROUGH THE DATA
if($result1->num_rows > 0) {
while($fetch=mysqli_fetch_array($result1)) {
$title=$fetch['title'];
$feed_rss=$fetch['url'];
$content_id=$fetch['content_id'];
$source_image=$fetch['source_image'];
Now on that basis lets get the posts, round 1:
$query2 = "SELECT item_id, item_title, MAX(item_date) as item_date from posts WHERE content_id='$content_id' GROUP BY content_id";
$result2 = $mysqli->query($query2) or die($mysqli->error.__LINE__);
if($result2->num_rows > 0) {
while($rows2=mysqli_fetch_array($result2)) {
$item_id = $rows2['item_id'];
$item_title = $rows2['item_title'];
$item_date = $rows2['item_date']; `
This pulls back an in interesting set of results, the latest date of a post from an author, but not the latest title!
I have tried GROUP BY item_title and content_id to no avail.
The following pulls back just records that have more than one date in the database, the problem is (as this is new) some authors only have one post, these are not being displayed:
$query2 = "SELECT item_id, item_title, item_date FROM posts AS a WHERE content_id = content_id AND item_date = (
SELECT MAX(item_date)
FROM rssingest AS b
)
";
I have tried:
ORDER BY DATE DESC LIMIT 1
at the end of my queries to no avail.
item_date is a DATE type in the table.
Any help would be greatly appreciated!
Thanks,
You are running this query to get the maximum date:
SELECT item_id, item_title, MAX(item_date) as item_date
from posts
WHERE content_id = '$content_id'
GROUP BY content_id;
Of course you don't get the right item_title. It is not mentioned in the group by clause and has no aggregation function. So, you are using a MySQL extension (this query would fail in other databases).
You can get the most recent title using the substring_index()/group_concat() trick:
SELECT max(item_id),
substring_index(group_concat(item_title separator '|' order by item_date desc), '|', 1) as last_title,
MAX(item_date) as item_date
from posts
WHERE content_id = '$content_id'
GROUP BY content_id;
This will return one row. But, if there is only one row that you want, you can do:
SELECT p.*
from posts p
WHERE p.content_id = '$content_id'
ORDER BY item_date desc
LIMIT 1;
The previous version will generalize if you add a group by statement.
You need to join your posts table with itself to grab only the newest post for each content_id (which I believe is the author).
$query2 = "SELECT posts.item_id, posts.item_title, posts.item_date from posts
join (select max(item_id) max_id, content_id from posts group by content_id) maxid
on maxid.content_id=posts.content_id and maxid.max_id=posts.item_id
WHERE content_id='$content_id' ";
This opens the door for further optimization. You could grab the authors and their last post in one run.
SELECT content_detail.content_id,
content_detail.title,
content_detail.source_image,
content_detail.url ,
posts.item_id,
posts.item_title,
posts.item_date
FROM content_detail
join posts on content_detail.content_id=posts.content_id
join (select max(item_id) max_id, content_id from posts group by content_id) maxid
on maxid.content_id=posts.content_id and maxid.max_id=posts.item_id
where content_detail.approved='y'
But this could be an overstatement. You should of course try it yourself :)