Issues when using multiple mysqli prepared statements - php

I am trying to insert and update data using 3 prepared statements. There are two problems happening:
1: The SELECT COUNT query checks for if the record exists with a LIMIT 1 and result stored in $check. However, the LIMIT 1 seems to be ignored such as when I have 3 rows matching the WHERE conditions in the SELECT COUNT, $check returns 3 instead of 1.
2: The if ( (int)$check < 1 ) statement works and whenever $check = 0, I see the output from my echo's, however the $stmt_insert->execute(); and $stmt_total->execute(); are not actually inserting and updating the database.
But, when I comment out:
$stmt_check->execute();
$stmt_check->bind_result($check);
$stmt_check->fetch();
then the $stmt_insert and $stmt_total works and the rows are inserted and updated. I'm wondering if there's a way to make all three statements work properly, for it seems some kind of conflict is occurring with my current set up. Thank you for your time.
Here is the full code:
$user_id = 100;
$count = preg_match_all("#<li>(.*?)</li>#is", $html, $matches, PREG_SET_ORDER);
$stmt_check = $mysqli->stmt_init();
$stmt_check->prepare("SELECT COUNT(`user_id`) AS `check` FROM `ua` WHERE `ach_class` = ? AND `user_id` = ? LIMIT 1");
$stmt_check->bind_param('si', $ach_class, $user_id);
$stmt_insert = $mysqli->stmt_init();
$stmt_insert->prepare("INSERT INTO `ua` (`user_id`, `ach_class`, `time_log`) VALUES (?, ?, ?)");
$stmt_insert->bind_param('isi', $user_id, $ach_class, $ach_timestamp);
$stmt_total = $mysqli->stmt_init();
$stmt_total->prepare("UPDATE `ach` SET `total` = `total` +1 WHERE `ach_class` = ? LIMIT 1");
$stmt_total->bind_param('s', $ach_class);
for ($i = 0; $i < $count; $i++) {
$li_block = $matches[$i][1];
preg_match('#img/(.*?)_60.png#is', $li_block, $class_matches);
$ach_class = $class_matches[1];
$ach_class = substr($ach_class, 11, -11);
$ach_timestamp = time();
$stmt_check->execute();
$stmt_check->bind_result($check);
$stmt_check->fetch();
echo $ach_class.' --- Check = '.$check.'<br />';
if ( (int)$check < 1 ) {
$stmt_insert->execute();
echo 'insert<br />';
$stmt_total->execute();
echo 'update<br />';
} else {
echo 'already present<br />';
}
}
$stmt_insert->close();
$stmt_check->close();
$stmt_total->close();

LIMIT works...it limits number of return rows, and your returning 1 row with just one field (just count), so that LIMIT at the end is kind of meaningless, because it will return single result, anyway. Also, why do you have LIMIT 1 in your UPDATE command?
You can use INSERT ON DUPLICATE KEY UPDATE and create UNIQUE index on your columns: ach_class, user_id and you will be fine.

Related

How do I structure an array in this desired format that is run inside a foreach() loop?

In the following scenario, $communityPlayerIds is an array of the Id's of people in a community, and $noPlayers is the count of that array.
e.g
$communityPlayerIds = [2,5,6]
$noPlayers = 3
The following function should do the following:
Run an sql query for the number of times represented by $noPlayers, each time retrieving the desired data of a different $communityPlayerId.
At the moment this is creating one new array, players of 24 items, 8 for each player.
public function getCommunityForm($communityId, $noPlayers, $communityPlayersIds){
$returnValue = array();
$i = 0;
foreach ($communityPlayersIds as $cPI){
$sql = " SELECT player1_result, player1_name, date , results_id FROM `results` WHERE player1_id = '".$cPI."' AND community_id = '".$communityId."' UNION ALL SELECT player2_result, player2_name,date, results_id FROM `results` WHERE player2_id = '".$cPI."' AND community_id = '".$communityId."' ORDER BY date DESC Limit 8";
$result = $this->conn->query($sql);
if (mysqli_num_rows($result) === 0) {
$returnValue[] = ['status' => "nil"];
}
if($result != null && (mysqli_num_rows($result) >= 1)){
while($row = $result -> fetch_array(MYSQLI_ASSOC)){
if(!empty($row)){
$returnValue['players'][$i] = $row;
$i++;
}
}
}
}
return $returnValue;
}
What I want is to return a single array, that has within it 3 separate arrays, 1 for each query run.
How do I do this?
Use two separate counters. Use the $i counter for the queries, and another counter for the rows of each query.
In our code, move the increment of $i to the end of the foreach loop, so it gets incremented only one time each pass through that outer loop.
$i = 0
foreach ($communityPlayersIds as $cPI){
$sql = "...";
// process each query
$i++;
}
Within the body of the foreach loop, when you process the rows returned by a query, use another counter for the rows. Initialize before the loop, and increment as the last step in the loop.
And add another dimension to your result array
$rn = 0;
while($row = $result->fetch_array(MYSQLI_ASSOC)){
//
$returnValue['players'][$i][$rn] = ... ;
rn++;
}
EDIT
As Paul Spiegel notes, the $rn counter isn't strictly necessary. An assignment to an array using empty square brackets will add a new element to an array.
while($row = $result->fetch_array(MYSQLI_ASSOC)){
//
$returnValue['players'][$i][] = ... ;
}
First thing I did was simplify your database query (well, I at least made it more efficient) by getting rid of the UNION with the second query.
Next, I set up a prepared statement. This reduces all the overhead of repeated queries to the database. It's been a few years since I worked with mysqli but I believe it should be working, as long as your ID columns are all numbers. I would hope so, but your original code had quotes around them. If they're strings, change iiiii to sssss, and seriously reconsider your database schema (more on that below.)
You don't need a counter since you already have the player ID, just use that as the array index.
public function getCommunityForm($communityId, $noPlayers, $communityPlayersIds){
$sql = " SELECT IF(player1_id=?, player1_result, player2_result) AS result, IF(player1_id=?, player1_name, player2_name) AS name, date, results_id FROM `results` WHERE (player1_id=? OR player2_id=?) AND community_id=? ORDER BY date DESC Limit 8";
$stmt = $this->conn->prepare($sql);
foreach ($communityPlayersIds as $cPI) {
$stmt->bind_param("iiiii", $cPI, $cPI, $cPI, $cPI, $communityId);
$stmt->execute();
if ($result = $stmt->get_result()) {
while($row = $result->fetch_array(MYSQLI_ASSOC)){
$returnValue['players'][$cPI][] = $row;
}
}
}
}
return $returnValue;
}
And for free, here's the PDO version. I'd strongly recommending looking into PDO. It's more modern and less verbose than mysqli. You'll notice no binding of parameters, we get to used named parameters, and getting an array out of it is much easier.
public function getCommunityForm($communityId, $noPlayers, $communityPlayersIds){
$sql = " SELECT IF(player1_id=:pid, player1_result, player2_result) AS result, IF(player1_id=:pid, player1_name, player2_name) AS name, date, results_id FROM `results` WHERE (player1_id=:pid OR player2_id=:pid) AND community_id=:cid ORDER BY date DESC Limit 8";
$stmt = $this->conn->prepare($sql);
foreach ($communityPlayersIds as $cPI) {
if ($stmt->execute([":pid"=>$cPI, ":cid"=>$communityID])) {
$returnValue['players'][$cPI] = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
return $returnValue;
}
As for your database schema, you should not have a column for player names in your result table. How many times are names repeated in that table? What if a user wanted to change their name? You should instead have a player table, and then use a join to pull in their details.

Checking against the database for a record exist

I'm working on another class project and what I want to do is check the database is a row exists.
I've seen several different methods throughout the forum and not one of them seem to work. I've spent roughly 12 hours on this one thing and it's becoming a time sink.
Here is the last iteration of the code I've tried :
$ciid = $_POST['categoryid'];
$check = $db->prepare("SELECT categoryid, imgid FROM catType WHERE categoryid = $ciid AND imgid = $imgID ");
$check->bindParam(categoryid,$ciid);
$check->bindParam(imgid,$imgID);
$check->execute();
if($check->rowCount() > 0 ){
echo "dqowdhnoqwhd";
} else {
/*run working insert */
}
/* the form that passes the $_POST value */
echo "<p><input type=\"checkbox\" name=\"catname[]\" value=\"".$row['categoryid']."\" />" . $row['cName'] . " </p>";
echo "<p><input type=\"hidden\" name=\"".$imgID."\" value=\"".$imgID."\"/></p>";
As it stands now, I get no errors but it also doesn't tell me that the record is already in the database. When I add a new record , it just duplicates it.
the array :
Array
(
[catname] => Array
(
[0] => 1
)
[111] => 111
[icud] => update category
)
I ran the query in phpMyAdmin and the formula worked for what I wanted it to do.
Your code (e.g. bind_param() usage) suggests you want to use a paramaterized query, but you are not leaving any places in the query for the parameters you try to bind. Try this instead:
$ciid = $_POST['categoryid'];
$stmt = $db->prepare("SELECT COUNT(*) FROM catType WHERE categoryid = ? AND imgid = ?");
$stmt->execute(array($ciid, $imgID));
$count = $stmt->fetchColumn();
if($count > 0) {
echo "dqowdhnoqwhd";
} else {
/*run working insert */
}
Note from the documentation about numRows():
If the last SQL statement executed by the associated PDOStatement was a SELECT statement, some databases may return the number of rows returned by that statement. However, this behaviour is not guaranteed for all databases and should not be relied on for portable applications.
Instead, since you're not fetching anything useful from the database you can just do a COUNT() of the rows that match. The fetchColumn() function will pull the first column of the next row in the result set; handy for pulling a single value out.
What I didn't do was run a foreach loop on the array items that was holding the categoryid. So everything that I may have tried for the past , many hours wouldn't have worked. here is my solution, please feel free to suggest improvements
n = $_POST['catname'];
foreach($n as $row => $value){
$para = $value;
}
$param = $imgID;
$check = $db->prepare("SELECT imgid , categoryid FROM catType WHERE imgid = $param AND categoryid= $para");
$check->execute();
$count = $check->fetchColumn();
//echo var_dump($check);
if($count > 0) {
echo "Category already added";

Returning multiple results when only one present in DB

Alrighty, so this code has been giving me trouble for the past day or so. All of its duties are working perfectly fine, save for the Tax query. (at least thats the only one thats giving me trouble that i can tell of)...
as far as i can tell; the code is giving me trouble because their are multiple TUID and UID's that are the same, but don't go to the same user, (EG an ID that == 1 might be User1 or it might be Group1).
I'm not asking for you guys to fix this code, (It seems everytime i post here, someone says 'oh you just want us to fix your code and blah blah blah blah') I'm just curious if anybody has a good work around for this?
I'm getting duplicate rows returned to the query;
this is what the query returns: http://gmz1023.com/rowline.PNG
{
$sql = "SELECT type FROM bank_transaction LIMIT 25";
$que = $this->db->prepare($sql);
$que->bindParam('uid', $uid);
try{
$que->execute();
ob_start();
if($que)
{
$id = '1';
$transaction = '';
while($row = $que->fetch())
{
/* This is the tricky bit; for each type that is returned, we start another query and pull up that data.<
/* This may need to be turned into seperate functions later on. however, for now, keep trying to figure out how to get the code to work,
/* There's nor eason why it shouldn't work, as its simple as hell. and yet we keep getting multiple rows in the table.
*/
if($row[0] == 'govt')
{
$sql = "SELECT tuid, uid, balance FROM bank_transaction WHERE type = :type AND uid = :uid";
$querty1 = $this->db->prepare($sql);
$type = 'govt';
$querty1->bindParam('type', $type);
$querty1->bindParam('type', $row[0]);
$querty1->bindParam('uid', $uid);
try {
if($querty1->execute())
{
$info = $querty1->fetch();
$to = parent::getUsername($info[0]);
$from = parent::getStoryName($info[1]);
$balance = $info[2];
$transaction .= "<tr><td>{$id}</td><td>{$from}</td><td>{$to}</td><td>{$balance}</td><td>{$row[0]}</td></tr>";
$querty1->closeCursor();
}
}catch(PDOException $e) { echo $e->getMessage();}
}
if($row[0] == 'business')
{
$sql = "SELECT tuid, uid, balance, date FROM bank_transaction WHERE type = :type AND tuid = :uid OR uid = :uid";
$querty1 = $this->db->prepare($sql);
$type = 'business';
$querty1->bindParam('type', $type);
$querty1->bindParam('type', $row[0]);
$querty1->bindParam('uid', $uid);
try {
if($querty1->execute())
{
$info = $querty1->fetch();
$to = $info[0];
$from = $info[1];
$balance = $info[2];
$date = $info[3];
$transaction .= "<tr><td>{$id}</td><td>{$from}</td><td>{$to}</td><td>{$balance}</td><td>{$row[0]}</td><td>{$info[3]}</tr>";
$querty1->closeCursor();
}
}catch(PDOException $e) { echo $e->getMessage();}
}
if($row[0] == 'tax')
{
$sql = "SELECT tuid, uid, balance, date FROM bank_transaction WHERE tuid = :tuid AND type = :type ;";
$querty = $this->db->prepare($sql);
$type = 'tax';
$uid = '2';
$querty->bindParam('type', $type);
$querty->bindParam('tuid', $uid);
try {
if($querty->execute())
{
$info = $querty->fetch();
$to = parent::getStoryName($info[0]);
$from = parent::getUsername($info[1]);
$balance = $info[2];
$transaction .= "<tr><td>{$id}</td><td>{$from}</td><td>{$to}</td><td>{$balance}</td><td>{$row[0]}</td><td>{$info[3]}</tr>";
$querty->closeCursor();
}
}catch(PDOException $e) { echo $e->getMessage();}
}
elseif($row[0] == 'personal')
{
$sql = "SELECT tuid, uid, balance FROM bank_transaction WHERE type = :type AND uid = :uid OR tuid = :uid";
$querty = $this->db->prepare($sql);
$type = 'personal';
$querty->bindParam('type', $type);
$queryt->bindParam('uid', $uid);
try {
if($querty->execute())
{
$info = $querty->fetch();
$to = $info[0];
$from = $info[1];
$balance = $info[2];
$transaction .= "<tr><td>{$id}</td><td>{$from}</td><td>{$to}</td><td>{$balance}</td><td>{$row[0]}</td></tr>";
$querty->closeCursor();
}
}catch(PDOException $e) { echo $e->getMessage();}
}
$id = $id +1;
}
return $transaction;
ob_end_clean();
}
else
{
echo "ERROR!";
}
}catch(PDOException $e) { echo $e->getMessage(); }
}
The Database
tid int(11) No None AUTO_INCREMENT
uid int(11) No None
tuid int(11) No None
balance int(11) No None
type enum('personal', 'govt', 'business', 'tax') latin1_swedish_ci No date datetime
The tax query has a semicolon at the end. It's likely that this causing MySQL to throw an error.
But the entire code example is bizarre. The displayed $id value isn't from the database, it's a loop counter for the rows fetched from first query.
SELECT type FROM bank_transaction LIMIT 25
For each row returned, a check is performed on the value returned for "type", and depending on the value, one of four other queries is executed. The queries are all similar:
"govt"
SELECT tuid
, uid
, balance
, date
FROM bank_transaction
WHERE type = :type
AND tuid = :uid
OR uid = :uid
"business"
SELECT tuid
, uid
, balance
, date
FROM bank_transaction
WHERE type = :type
AND tuid = :uid
OR uid = :uid
"tax"
SELECT tuid
, uid
, balance
, date
FROM bank_transaction
WHERE tuid = :tuid
AND type = :type
"personal"
SELECT tuid
, uid
, balance
FROM bank_transaction
WHERE type = :type
AND uid = :uid
OR tuid = :uid
There's some potential problems with the precedence of AND and OR in some of those queries, and there's also a potential problem with PDO and referencing the same named bind variable more than once (but this "bug" may have been addressed in a more recent version of PDO.)
And the bindParam calls specify the named bind parameter without the leading colon, which is odd. I've never seen that before.
I think the bigger issue with the code is that each time through the outermost loop (it does manage to successfully increment the $id value each time through). But for each type, it executes an identical SQL statement, with identical bind values. And it's a nearly guaranteed that MySQL is returning the same set of rows, in the same order, each time the query is executed.
But only the first row is fetched and processed.
I apologize if this is blunt, but...
This looks as if someone threw a whole pile of code at the problem, without actually having a good conceptual understanding of what needs to be done, and without thinking through a workable algorithm.
The problems with this code are lot bigger than syntax issues.
The outermost query that gets type from the database doesn't look like it has any purpose, except to limit the number of rows returned. (I'm a little surprised there aren't 25 rows in the result; I can only guess that there are either actually 4 rows in the table, or more likely, that query is returning more than 4 rows, but the fifth row returned has type='tax', which is causing the "tax" query to be executed, which is causing a MySQL syntax error.
This code is not "simple" at all. It's overly complex, and unsurprisingly ineffective.
You could use GROUP BY to make selection unique:
SELECT tuid, uid, balance FROM bank_transaction WHERE type = :type AND uid = :uid GROUP BY uid

Row count no error, but wrong result

$rowcount = 'SELECT COUNT(1) FROM (select * from isk.edi_site where postal_code = 123456)';
$stmt= oci_parse($conn, $rowcount);
oci_execute($stmt);
$num_row = oci_fetch_assoc($stmt);
$num = count($num_row, COUNT_RECURSIVE);
echo $num;
It counts and return the number "1". When I use the same SQL query in Oracle SQL developer, it echos out 4000+ count. Where would my mistake be? The column count works well..
A COUNT() query only returns a single row so you need to grab it from the oci_fetch_assoc() call. What you're doing is counting the rows in the result set which will always be 1.
$row = oci_fetch_assoc($stmt);
echo $row['COUNT(1)'];
Or give the count an alias:
SELECT COUNT(1) mycount FROM ...
$row = oci_fetch_assoc($stmt);
echo $row['MYCOUNT'];

Select statement within a while statement

First, I coded this, which looks inside a table, gets the last 10 entries, and displays them. The output is as expected, a list of the 10 last entries in the database.
$query = "SELECT dfid FROM downloads_downloads ORDER BY did DESC limit 10";
$dlresult = mysql_query( $query );
$i=0;
$num = mysql_num_rows ($dlresult);
while ($i < $num) {
$dfid= mysql_result($dlresult,$i,"dfid");
echo "<b>filenumber:</b> $dfid <br>";
++$i;
}
But I don't just need the filenumber. I need the actual filename and url from another table. So I added a select statement inside the while statement, using the file number.
But for some reason, this code only displays one filename instead of 10. I know, from the above code, it's getting all 10 file numbers.
$query = "SELECT dfid FROM downloads_downloads ORDER BY did DESC limit 10";
$dlresult = mysql_query( $query );
$i=0;
$num = mysql_num_rows ($dlresult);
while ($i < $num) {
$dfid= mysql_result($dlresult,$i,"dfid");
$query2 = "SELECT file_name, file_name_furl FROM downloads_files WHERE file_id = '$dfid'";
$dlresult2 = mysql_query( $query2 );
$dlfile_name= mysql_result($dlresult2,$i,"file_name");
$dlfile_name_furl= mysql_result($dlresult2,$i,"file_name_furl");
echo "filenumber: $dfid <br>"; //Shows 10, as expected.
echo "filename: $dlfile_name - $dlfile_name_furl <br>"; //Shows only 1?
++$i;
}
I can manually execute the sql statement and retrieve the file_name and file_name_furl from the table. So the data is right. PHP isn't liking the select within the while statement?
Looks like you're only going to ever have 1 row, in your 2nd select statement, because you are just selecting one row, with your where statement.
So, you're only going to ever have row 0 in the 2nd statement, so its only finding row 0 on the first loop. try instead:
$dlfile_name= mysql_result($dlresult2,0,"file_name");
$dlfile_name_furl= mysql_result($dlresult2,0,"file_name_furl");
However, insrtead of making 11 separate queries, try using just one:
$link = new mysqli(1,2,3,4);
$query = "SELECT downloads_downloads.dfid, downloads_files.file_name, downloads_files.file_name_furl FROM downloads_downloads LEFT OUTER JOIN downloads_files ON downloads_files.file_id = downloads_downloads.dfid ORDER BY downloads_downloads.dfid DESC limit 10;
$result = $link->query($query) ;
if((isset($result->num_rows)) && ($result->num_rows != '')) {
while ($row = $result->fetch_assoc()) {
echo "filenumber: $row['dfid'] <br>";
echo "filename: $row['file_name'] - $row['file_name_furl'] <br>";
}
Read up on mysql joins http://www.keithjbrown.co.uk/vworks/mysql/mysql_p5.php
I'm not sure if this is syntax correct, but it gives you the right idea =)

Categories