How can I get a row's info from another table without having to SELECT each loop?
//get posts from "posts" table
$stmt = $dbh->prepare("SELECT * FROM posts WHERE user_id = :user_id");
$stmt->bindParam(':user_id', $userId);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($result as $row) {
echo $row['post'];
//get poster's full name from "users" table
$stmt = $dbh->prepare("SELECT * FROM users WHERE user_id = :user_id");
$stmt->bindParam(':user_id', $row['poster_id']);
$stmt->execute();
$result2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($result2 as $row2) {
echo $row2['full_name'];
}
}
how can I make this code more efficient and faster?
imagine if i have 1000 posts and each is posted by a different user. i need to get the full name of that user that posted the post. right now, i need to SELECT 1000 times because of those 1000 users. it seems so inefficient right now. how can i make it better?
I heard join might work? what are the solutions?
SELECT * FROM posts
JOIN users ON posts.user_id = users.id
WHERE posts.user_id = :user_id.
You are right, joining the users table onto your posts query will be faster.
Something else you can do to increase performance is to cache the results of your query in something like memcache and then clear the cache when a post is added or deleted. That way you don't need to hit your db every time this data is needed.
Related
$sth = $db->prepare("SELECT * FROM users, profile_post WHERE id='$id'");
$sth->execute();
$row = $sth->fetch(PDO::FETCH_ASSOC);
$stuff = array('Published by' => $row['firstname']. " " .$row['lastname'], 'Content profile:' => $row["content"]);
foreach ($stuff as $key => $value) {
echo "$key: $value\n";
}
This code it's not looping, i've been testing this code with return and arrays
How can i fix it?
It just shows one result, I want all of them from the db.
You're only retrieving one result:
$row = $sth->fetch(PDO::FETCH_ASSOC);
This is the only place where you fetch anything (in this case, a single row) from the database.
Your foreach is looping over the $stuff array, which you have defined with content from $row - and it only contains the Published by and the Content profile: keys.
Instead you want to iterate over the actual fetch call:
$sth = $db->prepare("SELECT * FROM users, profile_post WHERE id='$id'");
$sth->execute();
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
// process each row as you need here
var_dump($row);
}
.. should give you all the rows. BUT. There are two other issue here.
Your SQL query doesn't have a join condition, so you're effectively cross joining users with profile_post, which means that every row in users is joined with every row in profile_post. The total number of rows will the become rows in users * rows in profile_post - this is probably not what you want.
You add the join condition by adding profile_post.user_id = users.id to your WHERE condition.
The second issue is that you're using ->prepare, but you're still using $id when building the query itself. You want to use a placeholder and then give the value when you're executing the statement, supplying the value externally.
$sth = $db->prepare("
SELECT
*
FROM
users, profile_post
WHERE
profile_post.user_id = users.id AND
users.id = :id
");
$sth->execute([':id' => $id]);
Here's my usual way of counting rows...
$query = "SELECT * FROM users";
$stmt = $db->prepare($query);
$stmt->execute();
$count = $stmt->rowCount();
This will count all rows, even if I use a WHERE clause, it'll still count every row that meets that condition. However, let's say I have a table, we'll call it tokensEarned (that's my actual table name). I have the following data...
user_id = 1,2,4,5,8,8,2,4,3,7,6,2 (those are actual rows in my table - clearly, user 1 has 1 entry, 2 has three entries, etc.) In all, I have 12 entries. But I don't want my query to count 12. I want my query to count each user_id one time. In this example, my count should display 8.
Any help on this? I can further explain if you have any specific questions or clarification you need. I would appreciate it. Thank You.
The following query will yield the distinct user count:
$query = "SELECT COUNT(DISTINCT user_id) AS cnt FROM users";
$stmt = $db->prepare($query);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo "distinct user count: " . $row['cnt'];
It isn't possible to get all records and the distinct count in a single query.
Whether you use the query above or you return all the actual distinct rows really depends on whether you need the full records. If all you need are the counts, then it is wasteful to return the data in the records, and what I gave above is probably the best option. If you do need the data, then selecting all distinct rows might make more sense.
You can use distinct in mysql to select only unique fields in your table.
$query = "SELECT distinct user_id FROM users";
$stmt = $db->prepare($query);
$stmt->execute();
$count = $stmt->rowCount();
Change your query to the following, this way you only shows the unique user_id:
$query = "SELECT DISTINCT user_id FROM users";
So I have 5 separate select statements.
<?
$stmt=$db->prepare('SELECT * FROM table1 WHERE username = :username');
$stmt->bindParam(':user', $username);
$stmt->execute();
$row = $stmt->fetchAll();
$stmt=$db->prepare('SELECT * FROM table2 WHERE username = :username');
$stmt->bindParam(':user', $username);
$stmt->execute();
$row = $stmt->fetchAll();
$stmt=$db->prepare('SELECT * FROM table3 WHERE username = :username');
$stmt->bindParam(':user', $username);
$stmt->execute();
$row = $stmt->fetchAll();
$stmt=$db->prepare('SELECT * FROM table4 WHERE username = :username');
$stmt->bindParam(':user', $username);
$stmt->execute();
$row = $stmt->fetchAll();
$stmt=$db->prepare('SELECT * FROM table5 WHERE username = :username');
$stmt->bindParam(':user', $username);
$stmt->execute();
$row = $stmt->fetchAll();
?>
I need to combine all the results, organize all the results based on time with the newest time first, and then echo a message based on which table the result came from.
Note each table has a row time.
Example results:
<p>This result was posted 5 minutes ago and came from table 2</p>
<p>This result was posted 10 minutes ago and came from table 5</p>
<p>This result was posted 1 day 6 hours ago and came from table 1</p>
<p>This result was posted 2 weeks ago and came from table 3</p>
<p>This result was posted 3 weeks ago and came from table 1</p>
UPDATES:
Selecting * is an absolute requirement, which is why using mysql is not a viable solution. Mysql can't do full table joins, and instead mirrors the practice by doing cross joins of the tables. However if you understand the woe's of cross joins, you will understand when I say some of these tables will return thousands of results for a user, making cross joins not viable as cross joins will create countless duplicate rows for each entry, and having thousands of entries makes for millions and tens of millions of rows when doing a cross join. PHP seems to be the only solution.
I need a solution that wil use php to combine the selects and sort them based on time NOT a mysql solution.
You can define a new column in each table to hold the tablename. And then this kind of query can solve your problem:
SELECT username,timestamp,tableName from table1 union
select username,timestamp,tableName from table2 order by 2 desc
I think the problem should be solved where it belongs. This is obviously a db problem.
But as you have other concerns then you can try first fetching all rows from tables to an array. All tables should be selected with an order by descending timestamp field. Define a pointer for every table which shows the newest item in the related table. In your loop find the newest record amongst the rows which these pointers pointing to. and increase this pointer after printing out the info. Hope it helps. Here is an example:
$stmt=$db->prepare('SELECT * FROM table1 WHERE username = :username order by timestamp desc');
$stmt->bindParam(':username', $user);
$stmt->execute();
$row[0] = $stmt->fetchAll();
$stmt=$db->prepare('SELECT * FROM table2 WHERE username = :username order by timestamp desc');
$stmt->bindParam(':username', $user);
$stmt->execute();
$row[1] = $stmt->fetchAll();
// table lengts
$table_len[0]=sizeof($row[0]);
$table_len[1]=sizeof($row[1]);
//initialize every table pointers to 0
$table_pointer[0]=0;
$table_pointer[1]=0;
while ($table_pointer[0]<$table_len[0] ||
$table_pointer[1]<$table_len[1]){
// find the newest time
$time1=strtotime($row[0][$table_pointer[0]]['timestamp']);
$time2=strtotime($row[1][$table_pointer[1]]['timestamp']);
$tableWithNewestTime=findNewestTime($table_len,$table_pointer,$time1,$time2);
$timeDif=time()-strtotime($row[$tableWithNewestTime][$table_pointer[$tableWithNewestTime]]['timestamp']);
echo "This result was posted ".$timeDif." s ego and posted from table".($tableWithNewestTime+1)."<br>";
// increase the table pointer
$table_pointer[$tableWithNewestTime]=$table_pointer[$tableWithNewestTime]+1;
}
function findNewestTime(array $table_len, array $table_pointer, $time1,$time2){
if ($table_pointer[0]>=$table_len[0])
return 1;
if ($table_pointer[1]>=$table_len[1])
return 0;
if($time1<$time2)
return 1;
else
return 0;
}
If I have a table with 3 rows with IDs 1,3,5 because rows with ID 2 and 4 were deleted, how do I make sure I select a row that exists?
$stmt = $db->prepare("SELECT COUNT(*) FROM table");
$stmt->execute();
$stmt->bind_result($numRows);
$stmt->fetch();
$stmt->close();
$random = mt_rand(1,$numRows);
$stmt = $db->prepare("SELECT link FROM table WHERE id=$random");
This won't ever select row with id 5, and also will select one that doesn't exist (2).
If the number of rows are small (and you are sure that it will stay that way), you can use ORDER BY RAND()
(Please note that this will create performance problems with big tables).
Other way is first counting how many rows are there
SELECT COUNT(*) AS total FROM table;
then pick a random number
$rand = rand(1, $total);
and select that row with limit
SELECT * FROM table LIMIT $rand, 1;
U can use a SQLstatement with EXISTS
SELECT link
FROM table
WHERE EXISTS (SELECT link
FROM table
WHERE id = $random);
If you just want a random row and don't care about the id, then you could use:
SELECT link FROM table
ORDER BY RAND()
LIMIT 1
For large numbers of rows (10000+), then you may need to implement another solution, as this query can be slow. This site has a good explanation and alternative solutions
If you want to follow your approach then you have to do some changes in your query.
1.) Query one : select id from table. // It will give you array of existing id.
2.) You have to use array_rand(). and use your second query.
Example :
$stmt = $db->prepare("SELECT ID FROM table");
$result = $stmt->fetchAll();
$random = array_rand(array_flip($result), 1);
$stmt = $db->prepare("SELECT link FROM table WHERE id=$random");
You could select one, randomly order, like this:
SELECT link FROM table ORDER BY RAND() LIMIT 1
UPDATE:
You should benchmark the different solutions you have, but I'm thinking this one could be nice with large amount of rows:
$stmt = $db->prepare("SELECT COUNT(*) FROM table");
$stmt->execute();
$stmt->bind_result($numRows);
$stmt->fetch();
$stmt->close();
$random = mt_rand(1,$numRows);
$stmt = $db->prepare("SELECT link FROM table WHERE id>=$random LIMIT 1");
$stmt->execute();
$stmt->bind_result($link);
if(!$link){
$stmt = $db->prepare("SELECT link FROM table WHERE id<$random LIMIT 1");
$stmt->execute();
$stmt->bind_result($link);
}
I have a forum where users can post questions and can comment and tweet.
I want to get all the comments and tweets of each post.
What i did previously was do that in 3 sets queries.
$data = mysqli_query($con,"select * from posts");
while($row = mysqli_fetch_assoc($data)){
$pid = $row['post_id'];
$dataCo = mysqli_query("SELECT comments.* FROM comments WHERE post_id = $pid");
$dataTw = mysqli_query("SELECT tweets.* FROM tweets WHERE post_id = $pid");
//2 while loop for comments and tweets
}
Can anyone show me how can i do these things in one single query because if a get a lot of posts in 1st query then there will be lots of queries to do.
OR
Maybe there is a faster way to do ?
Maybe you can use Mysql IN clause
http://www.tutorialspoint.com/mysql/mysql-in-clause.htm
In your example you have always 2*n + 1 queries to DB. Where n in number of returned rows in this query
$data = mysqli_query($con,"select * from posts");
If you use mysql IN Clause you will have only 3 queries.
Your queries should looks like
$dataCo = mysqli_query("SELECT comments.* FROM comments WHERE post_id IN (1,2,3,4)");
$dataTw = mysqli_query("SELECT tweets.* FROM tweets WHERE post_id IN (1,2,3,4)");
Numbers "1,2,3,4" are post_id returned in your first question.