Using a PHP Variable in a MySQL Query - php

So I have the following MySQL query that I want to execute through a PHP script:
for($y=0;$y<$usercount;$y++){
$findweight = "replace into weight (user_id, column1)
select user_id,
0.4*column1_a+0.6*column1_bon as calc1
from history_summary where user_id=$y";
if (mysqli_query($link, $findweight)) {
echo "success <br>";
} else {
echo "Error: " . $sql . "<br>" . mysqli_error($link);
}
}
But somehow everytime I execute the script it fails to work. Everytime the script is executed, the text "success" is outputted, which means that the MySQL query should have been successfully executed, but when I check my database, nothing seems to have changed. Yet when I run the query directly such as the following:
replace into weight (user_id, column1)
select user_id,
0.4*column1_a+0.6*column1_bon as calc1
from history_summary where user_id=1
It works just fine. I'm assuming the issue is that the variable $y failed to be passed on properly to the query, but I'm not quite sure. I've tried the other solutions posted on this site but none of them seem to work. If anyone can help out that'd be great!

The DB part of me is crying because you should really be submitting one query instead of multiple queries. Specifically, if $y is a safe variable, you should have something like this:
$sql = "replace into weight (user_id, column1)
select user_id,
0.4*column1_a+0.6*column1_bon as calc1
from history_summary where user_id < $y";
There are other questions I have, such as if you really want REPLACE or you want UPDATE but that is a problem for another day.
BUT, your question was more about debugging, and it seems that the command(s) are executing successfully. Try running your query on many different values of $y. Perhaps you have encountered an edge case

The question is about debugging, so I'll respond to that, but I double-down on #mankowitz points below.
Debugging
Do you have access to the DB server? Can you watch the query log on the server to confirm that it received what you think the code is sending?
Try the old standby of println style debugging and echo out the SQL string before sending. The <pre> and --- help to make sure you are able to see any extraneous characters you might have in your SQL string:
...
echo "About to send SQL: <pre>--- $findweight ---</pre>\n"
if (mysqli_query($link, $findweight)) {
...
Is this code wrapped inside of a SQL transaction that is not (auto)committed when the script ends?
Do other, perhaps simpler queries work? Have you tried the simple SELECT 1 and echoed out the result?
More arcane and improbable given the context of the question: is fsync disabled on the server? If so, then each mysql_query does not guarantee the DB has actually written the contents to disk yet, and you may eventually see the rows get updated.
What does mysqli_query return? The code currently ignores the result other than checking if it's falsey. Store the result and introspect it.
An edge case of mysqli_query. Ten plus years ago, I encountered cases where mysql_query (note the lack of i) would not return FALSE despite a failing query. It was an oddball case of the connected user not having access to manipulate or view the rows in question, highlighting a bug in mysql_query as well as my connection string.
Database interaction
Be smart about DB queries, minimize the communication, and use bulk operations wherever possible. Your code, as structured could be rewritten in a couple of ways, both of which require only a single call to the database:
The WHERE-IN construct allows you to be very precise about which rows you address:
$ids = [];
for ( $y = 0; $y < $usercount; ++$y )
$ids[] = $y;
$ids = implode(',', $ids);
$findweight = "REPLACE INTO weight (user_id, column1)
SELECT user_id, 0.4*column1_a + 0.6*column1_bon
FROM history_summary WHERE user_id IN ($ids)";
Or, since the IDs the code addresses are contained within a contiguous range, do as #mankowitz suggested, removing the need for a for-loop entirely:
$findweight = "REPLACE INTO weight (user_id, column1)
SELECT user_id, 0.4*column1_a + 0.6*column1_bon
FROM history_summary WHERE user_id BETWEEN 0 AND $usercount";
# alternate form
$findweight = "REPLACE INTO weight (user_id, column1)
SELECT user_id, 0.4*column1_a + 0.6*column1_bon
FROM history_summary WHERE 0 <= user_id AND user_id <= $usercount";
If you absolutely must use individual per-row statements, consider a prepared statement so the DB need only parse and plan the statement once:
$findweight = 'REPLACE INTO weight (user_id, column1)
SELECT user_id, 0.4*column1_a + 0.6*column1_bon
FROM history_summary WHERE user_id = ?';
if ($psth = mysqli_prepare($link, $findweight)) {
$y = 0;
mysqli_stmt_bind_param($psth, 'i', $y);
while ( ++$y < $usercount ) {
if ( mysqli_stmt_execute( $psth ) ) {
echo "Successful REPLACE operation for user_id = $y<br>\n";
}
else {
echo "Error in REPLACE operation for user_id = $y<br>\n";
break;
}
}
}

Related

select with update in one query

I have a query like:
SELECT id, name, surname, fromId, toId, msg_text, readed FROM messages WHERE toId = 2;
So I want to update all selected rows.readed = 1. And Query must return all selected rows.
These action must do in one query if possibe.
Sorry for my english
Short answer: No, it is not possible in a single query.
A little less short answer: There is something known as a command-query separation which in short suggests that a command should do something and return nothing and a query should do nothing and return something. I recommend following this principle if you intend on building good software.
I wont get into why this is not possible because I myself am not that much of an SQL guru and I could only guess but I would suggest an easy solution to your problem.
When you get your results then you are most likely processing them in PHP. Assuming the results are sorted in ascending order - on the first iteration grab the minimum id and on the last one grab the maximum id, then run an update query:
UPDATE messages SET readed = 1 WHERE toId = ? AND (id >= <minimum id> AND id <= <maximum id>)
On a side note - name and surname are probably not what you want to store in a messages table.
You can update only with an UPDATE query. An UPDATE query can return only one thing: that is number of affected rows. So, you cannot update and select the value you need in a single query.
Have a look here Update a table then return updated rows in mySQL
You can do that with stored procedure.
Create a stored procedure which executes the update and the select as well.
Eg:
DROP PROCEDURE IF EXISTS myproc;
delimiter //
CREATE PROCEDURE myproc()
begin
set #qry='select id, name, surname, fromId, toId, msg_text, readed from messages where toId = 2';
prepare query from #qry;
execute query;
update messages set readed=1 where toId = 2;
end //
Then you can run the query through this procedure.
Eg:
$sql = "call myproc()";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo "id: " . $row["id"]. " - Name: " . $row["name"]. "<br>";
}
} else {
echo "0 results";
}

Need to update only one tuple, but this is updating the whole column in table

I check if the the url is present first and see if he is up voting first time, if so then i will add the users repute to the upvoted column, but i want to add only to that url not to all urls, this code is adding to all all tuples in "upvotes" column, i want it to add only to a particular tuple.
<!Doctype html>
<html>
<?php
$c=$_GET['a'];// users name
$d=$_GET['b'];// usesrs id
$e=$_GET['c'];// users repute
$ur=$_POST['url'];
// Create connection
$con=mysqli_connect("localhost","root","","repute system");
if(mysqli_connect_errno()){
echo "ERROR ".mysqli_connect_error();
}
$sql = mysqli_query($con,"SELECT * FROM sites");
if (mysqli_num_rows($sql) > 0) {
// output data of each row
while($row = mysqli_fetch_assoc($sql))
{
if($ur == $row['URL'] && $d != $row['id'])
{
$ne = $row['upvotes'] + $e;
$sol = mysqli_query($con, "UPDATE sites SET upvotes = $ne ");
$bew = mysqli_query($con,"INSERT INTO v_sites(teacher_id,URL,vote) VALUES ('$d','$ur','$e')");
echo "Upvoted the site ";
echo $ur;
}
}
} else {
echo "Sorry before upvoting you have to block it first or you are trying to upvote your own report, in which you cant";
}
?>
</html>
You need a WHERE clause that matches the URL:
$stmt = mysqli_prepare($con, "UPDATE sites
SET upvotes = upvotes + 1
WHERE url = ? AND id = ?";
mysqli_stmt_bind_param($stmt, "ss", $ur, $d);
mysqli_stmt_execute($stmt);
You don't need the SELECT or while loop, since MySQL can find the matching rows and update them all by itself.
You also shouldn't have the INSERT query inside the loop, since it's inserting the same row each time.
And you should switch to prepared statements, as shown above, instead of inserting strings into your queries, since your code is subject to SQL injection.
All your rows are being updated because you are not using a where clause.
UPDATE sites SET upvotes = $ne
should be changed to:
UPDATE sites SET upvotes = $ne WHERE id='$d'
However, if $ne is also a string, $ne should also be quoted:
UPDATE sites SET upvotes = '$ne' WHERE id='$d'
Read up on UPDATE:
https://dev.mysql.com/doc/refman/5.0/en/update.html
"My table's name in sites it has 5 columns, which are URL,status,upvotes,downvotes, id and all are varchar with 30 length"
This tells me that id is VARCHAR also; not a good idea but that's up to you. It's best to use int for queries like this should all your id's be numerically-based.
Which is why using quotes WHERE id='$d' around the $d variable will be required.
Your present code is open to SQL injection. Use prepared statements, or PDO with prepared statements, they're much safer.

Running multiple MySQL queries with user defined variable through PHP

I'm trying to run a combination of queries from PHP. Something like this:
SELECT #rownum:=0; INSERT INTO tbl1 (`myId`, `rank`) (SELECT myId, #rownum:=#rownum+1 FROM `tbl2` WHERE 1 ORDER BY rank DESC)
Because of the user-defined var (#rownum) I have to run these queries "together". I've tried doing it either with mysqli or PDO, but both seemed to block this.
The mysqli usage I tried was:
public function multiQuery($query) {
if (#parent::multi_query($query)) {
$i = 0;
do {
$i++;
} while (#parent::next_result());
}
if (!$result) {
printf("MySQLi error:<br><b>%s</b><br>%s <br>", $this->error, $query);
}
return $result;
}
And in PDO I just expected it to work with the query() func.
How can I overcome this?
This might work better:
(set tbl1.rank to autoincrement)
TRUNCATE tbl1;
INSERT INTO tbl1 (myId) SELECT myId FROM tbl2 ORDER BY rank DESC;
It's two separate commands but you won't have to worry about the variable issue. It doesn't answer the question "how to run two different commands in one string" or "how to use variables in a query with PHP" but if you're just trying to re-rank things, that should work.
Truncate will empty tbl1 and reset the autoincrement value back to 1

What Does mysqli_store_result() Actually Do?

I want to understand that What mysqli_store_result Actually does? When I visited the PHP Manual of mysqli_store_result, I found the definiton
mysqli_store_result — Transfers a result set from the last query
The Question is Where it transfers the result set?
Actually I was getting the error "Commands out of sync; you can't run this command now" after executing mysqli_multi_query But When I used the following method, the Error gone.
mysqli_multi_query($connection,$query);
do
{
mysqli_store_result($connection);
}
while(mysqli_next_result($connection));
Now, Should I use this mysqli_store_result($connection) and mysqli_next_result($connection) after each mysqli_query or just after mysqli_multi_query Because I have read in PHP Manaul that
"Although it is always good practice to free the memory used by the
result of a query using the mysqli_free_result() function, when
transferring large result sets using the mysqli_store_result() this
becomes particularly important."
Source: PHP: mysqli_store_result
One More Question Arises When I executed the above mentioned mysqli_multi_query($connection,$query); I put a statement echo 'storing result <br />' like below
do
{
echo 'storing result <br />
mysqli_store_result($connection);
}
while(mysqli_next_result($connection));
Although There were only Two INSERT queries in the $query but It gave the following output
storing result
storing result
storing result
storing result
It means there were four result sets that were transferred. I can't understand this situation.
One Last Question. Does the above mentioned do while process will effect the performance?
Previous comments have stated that mysqli_store_result() is not to be used with INSERT statements, but no one has mentioned the actual appropriate function: mysqli_affected_rows().
If your statement returns a record set and you want to check it numerically, then use mysqli_num_rows().
If dealing with a mixture, this might get you started:
$queries[] = "INSERT INTO TestTable (Column1) VALUES ('TEST1')";
$queries[] = "SELECT * FROM TestTable WHERE Column1 LIKE 'TEST%'";
$queries[] = "INSERT INTO TestTable (Column1) VALUES ('TEST2')";
$queries[] = "SELECT * FROM TestTable WHERE Column1 LIKE 'TEST%'";
$queries[] = "DELETE FROM TestTable WHERE Column1 LIKE 'TEST%'";
if(mysqli_multi_query($con, implode(';', $queries))){
do{
if($result = mysqli_store_result($con)){
echo "Selected rows = " . mysqli_num_rows($result) . "<br><br>";
mysqli_free_result($result);
}else{
$cumulative_rows += $aff_rows = mysqli_affected_rows($con);
echo "Current Query's Affected Rows = $aff_rows, Cumulative Rows = $cumulative_rows<br><br>";
}
} while(mysqli_more_results($con) && mysqli_next_result($con));
}
Outputs:
Current Query's Affected Rows = 1, Cumulative Affected Rows = 1
Selected rows = 1
Current Query's Affected Rows = 1, Cumulative Affected Rows = 2
Selected rows = 2
Current Query's Affected Rows = 2, Cumulative Affected Rows = 4
An important note to anyone new to the topic of database querying: If you are using user-supplied / externally-sourced / untrustworthy data, then you should be using prepared statements with placeholders for security/stability (mysqli_multi_query() DOES NOT AFFORD THIS). Using mysqli_multi_query() seems like a cool, concise way to send a batch of queries, but there are not many compelling reasons/scenarios to use this function over sending queries one-at-a-time in a secure manner.
It actually will fetch the whole resultset from the MySQL. You can then mysqli_data_seek() to move to a particular row within the set. So that means all of the results will be stored on the php-side after the first call, and subsequent calls will just request the results from php

Doing a while / loop to get 10 random results

Hello i'm trying to make a tag script for my website so each time a search engine comes to my website 10 different tags will show on my website.
These tags will be grabbed from the db.
So at the minute i have coded it so it grabs only one. ( because i don't know how to do a while )
Like so
$sql = "SELECT tagname FROM tags ORDER BY rand() LIMIT 10";
$result = mysql_query($sql);
$row = mysql_fetch_object($result);
echo "<a href='index.php'>" .$row->tagname. " </a>";
Is there anyway i can add a while to that so it does it 10 times ? E.g use the same echo but print out 10 results instead of the 1 .... i have changed the limit from 1 to 10 but that did not work... still showing one...
Please, stop using ORDER BY RAND(). Just stop. This operation has complexity of n*log2(n), which means that the time spent on query would grow "
entries | time units
-------------------------
10 | 1 /* if this takes 0.001s */
1'000 | 300
1'000'000 | 600'000 /* then this will need 10 minutes */
If you want to generate random results, create a stored procedure, which generates them. Something like this (code taken from this article, which you should read):
DELIMITER $$
DROP PROCEDURE IF EXISTS get_rands$$
CREATE PROCEDURE get_rands(IN cnt INT)
BEGIN
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
SET cnt = cnt - 1;
INSERT INTO rands
SELECT tags.tagname
FROM tags
JOIN (SELECT (RAND()*(SELECT MAX(tags.id) FROM tags)) AS id) AS choices
WHERE tags.id >= choices.id
LIMIT 1;
END LOOP loop_me;
END$$
DELIMITER ;
And to use it, you would write:
CALL get_rands(10);
SELECT * FROM rands;
As for executing it all on PHP side, you should stop using the ancient mysql_* API. It is more than 10 years old and no longer maintained. Community has even begun process for deprecating them. There should not be any more new code written with mysql_* in 2012. Instead you should use PDO or MySQLi. As for how to write it (with PDO):
// creates DB connection
$connection = new PDO('mysql:host=localhost;dbname=mydb;charset=UTF-8',
'username', 'password');
$connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// executes the procedure and creates select statement
$connection->exec('CALL get_rands(10)');
$statement = $connection->query('SELECT * FROM rands');
// performs query and collects all the info
if ($statement->execute())
{
$tags = $statement->fetchAll(PDO::FETCH::ASSOC);
}
Update
If the requirement is to get not only 10 random results, but actually 10 UNIQUE random results, then it would require two changes to the PROCEDURE:
The temporary table should enforce the uniqueness of entries:
CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) UNIQUE);
It also might make sense to collect just IDs and not the values. Esspecially if what you are looking for are 10 unique articles, not just tags.
When inserting a duplicate value is found, the cnt counter should not decrease. This can be ensured by adding a HANDLER (before definition of LOOP), which would "catch" the raised warning, and adjust the counter:
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET cnt = cnt + 1;
Note, read before the real answer: for the ones that keep downvoting this answer. Read the title (that starts with "Doing a while") and the final part, the question ("Is there anyway i can add a while to that so it does it 10 times ?"). This answer is about iterating the result set, not about the usage of the RAND function! The query doesn't even appear in my answer, and I am also suggesting a different approach at the end:
you just need to wrap your call to mysql_fetch_object in a loop
$result = mysql_query($sql);
while ($row = mysql_fetch_object($result))
{
echo "<a href='index.php'>" .$row->tagname. " </a>";
}
Later edit
Other considerations would be:
if the table hold a very big amount of data (but it doesn't seem that it will) order by rand() can have a bad effect on the performance
consider using pdo (or at least mysqli)
you should have some error handling even if the query seems to be
perfect, at least
if (!$result)
{
echo mysql_error();
die;
}
You are fetching only one of them
You need to fetch all of them one by one in a while
$sql = "SELECT tagname FROM tags ORDER BY rand() LIMIT 10";
$result = mysql_query($sql);
while($row = mysql_fetch_object($result)) {
echo "<a href='index.php'>" .$row->tagname. " </a>";
}

Categories