Copy link to clipboard
Copied
Hi All,
Very new to PHP. Have a series of 3 Prepared Statements (see code below) I'm attempting to run through. This page fires from a link on a page that lists the individual candidates and all that works OK. Prepared Statement 1 works and displays the data in the particular row of the wanted columns, Access background thus I'd call it record and fields, but I think it's called row and columns here. Prepared Statement 2 which quires on a cross-reference table (we have a many-to-many relationship between candidates and positions, thus need for the cross-reference table) works and I can tell the array $selected_positions loads since I can see the position_id data in the <body> of the file using this:
<?php
foreach ($selected_positions as $item) {
echo $item.'<br>';
}
?>
Can't take this array $selected_positions and use it as the parameter in Prepared Statement 3, at least not how I'm attempting to do it. So, obviously Prepared Statement 3 is failing thus no way is an array I called $the_positions which is supposed to contain position ids, position titles and position numbers of the position_id that are in array $selected_positions. I can tell Prepared Statement 3 is failing because nothing shows in this table which is in the <body> of the file:
<table class="table table-striped">
<tr>
<th>Position ID</th>
<th>Position Number</th>
<th>Title</th>
</tr>
<?php while ($stmt->fetch()) { ?>
<tr>
<td><?= $position_id; ?></td>
<td><?= $position_number; ?></td>
<td><?= $title; ?></td>
</tr>
<?php } ?>
</table>
Here's the PHP script:
<?php
require_once '../includes/session_timeout_db.php';
?>
<?php
require_once '../includes/connection.php';
// initialize flag
$OK = false;
$conn = dbConnect('read');
// initialize statement
$stmt = $conn->stmt_init();
if (isset($_GET['candidate_id'])) {
$sql = 'SELECT candidate_id, last_name, first_name, company, mas_number, last_modified, notes
FROM candidates WHERE candidate_id = ?'; }
if ($stmt->prepare($sql)) {
// bind the query parameter
$stmt->bind_param('i', $_GET['candidate_id']);
// execute the query, and fetch the result
$OK = $stmt->execute();
//bind the results to variables
$stmt->bind_result($candidate_id, $last_name, $first_name, $company, $mas_number, $last_modified, $notes);
$stmt->fetch();
// free the database resources for the second query
$stmt->free_result();
}
// get positions associated with Candidate
$sql = 'SELECT position_id FROM pos2cands WHERE candidate_id = ?';
if ($stmt->prepare($sql)) {
// bind the query parameter
$stmt->bind_param('i', $_GET['candidate_id']);
// execute the query, and fetch the result
$OK = $stmt->execute();
$stmt->bind_result($position_id);
// loop through the results to store them in an array
$selected_positions = [];
while ($stmt->fetch()) {
$selected_positions[] = $position_id;
}
}
// find position data from array
$sql = 'SELECT position_id, position_number, title
FROM positions WHERE position_id = ?';
if ($stmt->prepare($sql)) {
// bind the query parameter
$stmt->bind_param('i', $_GET[$position_id]);
// execute the query, and fetch the result
$OK = $stmt->execute();
//bind the results to variables
$stmt->bind_result($position_id, $position_number, $title);
// loop through the results to store them in an array
$the_positions = [];
while ($stmt->fetch()) {
$the_positions[] = $position_id;
}
}
// get error message if query fails
if (isset($stmt) && !$OK) {
$error = $stmt->error;
}
if (!$stmt) {
$error = $conn->error;
} else {
$numRows = $stmt->num_rows;
}
?>
Thanks in advancel
You want to use the value from query 1 or query2 as a parameter in query 3, right? Instead of building an array, you can simply use the value returned by each row the query returns. I use PDO, not MySQLi, so I can't quickly knock out the MySQLi example for you.
while ($result = $sql->fetch(PDO::FETCH_ASSOC)) {
$field = $result['field'];
// Now we can use the value of $field as a parameter for the next query.
// The curly bracket that closes the while loop is placed after the last query
// so no
...Copy link to clipboard
Copied
In case you don't know, we have a new coding forum for questions like this:
Nancy O.
Copy link to clipboard
Copied
How do I get this over to Coding Corner
Can someone move it or do I copy paste the original? Thanks!
Copy link to clipboard
Copied
A forum moderator would have to move it for you.
Nancy O.
Copy link to clipboard
Copied
I'll shoot a message to Preran. Thanks for the help.
Copy link to clipboard
Copied
So is it a case of 'position_id' not being 'set'? if you actually use a position_id value you know exists do you get anything returned?
FROM positions WHERE position_id = 3';
Copy link to clipboard
Copied
When I put in position_id = 7, see code below:
// find position data from array
$sql = 'SELECT position_id, position_number, title
FROM positions WHERE position_id = 7';
if ($stmt->prepare($sql)) {
// bind the query parameter
$stmt->bind_param('i', $_GET['position_id']);
// execute the query, and fetch the result
$OK = $stmt->execute();
//bind the results to variables
$stmt->bind_result($position_id, $position_number, $title);
// loop through the results to store them in an array
$the_positions = [];
while ($stmt->fetch()) {
$the_positions[] = $position_id;
}
}
The data about position id 7 does not show up in the table see code below for my table:
<table class="table table-striped">
<tr>
<th>Position ID</th>
<th>Position Number</th>
<th>Title</th>
</tr>
<?php while ($stmt->fetch()) { ?>
<tr>
<td><?= $position_id; ?></td>
<td><?= $position_number; ?></td>
<td><?= $title; ?></td>
</tr>
<?php } ?>
</table>
Os, I'm going to see how I move this thread over to this new Code Corner. Either I copy paste the original or someone can move it. Thanks for your reply.
Copy link to clipboard
Copied
Instead of this
query1
foreach {
}
query 2
query 3
you should do this:
query1
foreach {
query 2
query 3
}
I think you can get the idea from that.
Alternatively you could use left joins
Copy link to clipboard
Copied
Rob,
I know I'm dealing with arrays in query 2 and 3, but I don't quite follow the revised foreach structure to my current structure of these 3 Prepared Statements. As I noted, I'm a beginner and have been studying David Powers' PHP Solutions but I'm sure David would not wish to come anywhere near this train wreck of code. Even though query 1 and 2 did work, 3, as structured, doesn't have a prayer.
Any additional comments on your approach of
query1
foreach {
query 2
query 3
}
would be greatly appreciated.
Thanks Rob.
Copy link to clipboard
Copied
You want to use the value from query 1 or query2 as a parameter in query 3, right? Instead of building an array, you can simply use the value returned by each row the query returns. I use PDO, not MySQLi, so I can't quickly knock out the MySQLi example for you.
while ($result = $sql->fetch(PDO::FETCH_ASSOC)) {
$field = $result['field'];
// Now we can use the value of $field as a parameter for the next query.
// The curly bracket that closes the while loop is placed after the last query
// so no need to populate an array with the values
}
Your approach is doable with some changes to the way you traverse the array, but it's unnecessarily complicated.
You might be able to use a single query to get all the data if you used left joins. With this approach, you start with the table that MUST return a result or the table that requires no dependencies from the other tables. The structure is like this:
SELECT field1, field2, field3 FROM (SELECT * FROM table1 WHERE field3=param1)A
LEFT JOIN (SELECT * FROM table2 WHERE A.field4=table2.field4)B
LEFT JOIN table3 ON table3.field5=B.field5 ORDER BY field1
The A and B above are aliases for the table subsets. You can image an implied equal sign ()=A
Copy link to clipboard
Copied
Thanks Rob.
Yes, I'm intending to use the value from query 2 as the parameter in query 3. Query 1 is just a basically independent select query to display the row contents of a particular candidate.
Query 2 selects all the positions from a cross-reference table that the particular candidate is associated with. Select query 3 is intended to then provide the title and position number of those positions that the candidate is associated with (i.e. their resume has been sent to that particular position).
I'd like to stay away from unnecessarily complicated.
Thanks again for all the assistance. I'll start working on this Thursday.
Copy link to clipboard
Copied
Ray, in response to your private message, I doubt that you need to move from MySQLi to PDO. Each method has its own personality and strengths, but both are very capable.
I think most people are using MySQLi. I may be the oddball using PDO.
Copy link to clipboard
Copied
Rob Hecker2 wrote:
I think most people are using MySQLi. I may be the oddball using PDO.
I don't think you're the oddball. I think MySQLi (improved) is easier for people who came from MySQL.
Summary | PDO | MySQLi |
---|---|---|
Databases | 12 different drivers | MySQL only |
API | OOP | OOP + Procedural |
Connection | Easy | Easy |
Named Parameters | Yes | No |
Object Mapping | Yes | Yes |
Prepared Statements (client-side) | Yes | No |
Performance | Fast | Faster (2.5%) |
Stored Procedures | Yes | Yes |
Copy link to clipboard
Copied
I knew named parameters were not in MySQLi (and I use them always), but I didn't know MySQLi doesn't work with stored procedures.
Together that tips the scale. PDO is the way to go.
Copy link to clipboard
Copied
In an earlier response you said, "I use PDO, not MySQLi, so I can't quickly knock out the MySQLi example for you." That got me researching the two in greater detail. I realize David Powers' book PHP Solutions, at Chapter 11 discusses MySQLi and PDO and it was my mistake, at that point, not to consider my particular circumstances before proceeding further.
I've built and operated Access databases, obviously not on a web UI, for over two decades. They are critical to our operations. Seeing, as David noted, that PDO is database neutral it appears, in my case, PDO has more potential in the long run than coding in MySQLi. Of course bringing in Access is down the road; but again, under PDO it provides me the correct frame as I understand it.
Thanks again for your timely response to my original question which got me rethinking in which direction to go. So, I get re-fit to PDO coding, I'll very likely be back with questions.
Copy link to clipboard
Copied
Access is not used much for web apps anymore. Too slow.
Nancy O.
Copy link to clipboard
Copied
We use various Access databases for the operations. The Access database for our sales/marketing function I was considering as a web based CMS approach thus giving a more collaborative environment and trying to manage the chaos better. So, it'd be a backroom scenario; but, slow would be major problem.
Really setup around MS business suite professional edition thus the Access heavy orientation. Is Microsoft that far off the main path to be problems? I know they missed the entire mobile device thing, but Access is total road kill when it comes to a web platform?
Do we have to continually sync Access to some other web-based database?
Copy link to clipboard
Copied
Miscrosoft Sequel Server will read access files, right? How about using it?
Copy link to clipboard
Copied
Here's what I found going out to Google. Hope the link comes through Import or link Access to SQL Server data
If not the phrase to Google was: will Miscrosoft Sequel Server read access files
This is down the road stuff; but, it appears a link is an option which would give it real-time sync. The article notes: When you link to the SQL Server data, you are connecting directly to the source data so any later changes that are made to data in Access are reflected in the SQL Server, and vice versa.
At the bottom of the page it states it applies to Access 2010, I've got 2013. Hopefully going forward they will not deprecate it.
I assume Miscrosoft Sequel Server, as a web database, does not present speed issues?
Copy link to clipboard
Copied
I'm so far removed from my Access days, I honestly couldn't tell you which (if any) of the current server-side dbases are most compatible with it. I think you would have to convert your Access queries to SQL. Then import the SQL data into MySQL dbase with PhpMyAdmin. I sure as heck wouldn't want to do this more than once.
Nancy O.
Copy link to clipboard
Copied
Hi Rob,
I've moved the code of this site I'm working on from MySQLi to PDO. I've placed the while loop, I believe it is called, in the cascading 3 query script and made certain to place the } after the 3rd query.
When I used $sql in
while ($result = $sql->fetch(PDO::FETCH_ASSOC)) {
$field = $result['field'];
I got this: Fatal error: Call to a member function fetch() on string in . . .
So I replaced $sql with $stmt which didn't throw the error, but probably was an incorrect move. Query 1 still displays the column data of the particular row through this code in the <body>:
<br>
<strong>Name:</strong>
<?php
echo $first_name . ' ' . $last_name;
?>
<br>
<strong>Company:</strong>
<?php
echo $company;
?>
<br>
<strong>MAS#:</strong>
<?php
echo $mas_number;
?>
<br>
<strong>Last Modified:</strong>
<?php
echo $last_modified;
?>
<br>
<strong>Notes:</strong>
<pre><?php echo $notes; ?></pre>
<br>
But after query 1 things fall apart. You can see in the code, at the very bottom of this response, that I've placed the while loop between query 1 and query 2. I can not get results of query 3 to display in the table in the <body> That table is:
<table class="table table-striped">
<tr>
<th>Position ID</th>
<th>Position Number</th>
<th>Title</th>
</tr>
<?php foreach ($stmt as $row) { ?>
<tr>
<td><?= $row['position_id']; ?></td>
<td><?= $row['position_number']; ?></td>
<td><?= $row['title']; ?></td>
</tr>
<?php } ?>
</table>
Any assistance or examples of proper PDO would be greatly appreciated. What we have is a many-to-many issue. That is, a candidate will be associated with multiple positions and a position will be associated with multiple candidates, thus the need for cross reference table pos2cands.
I'm clearly a beginner certainly dazed and confused. Yet I would assume anytime one has a many-to-many issue the cascading query approach is one of the options since interface with the cross reference table is essential. I know you discussed a single query with left joins, but I wanted to further investigate your while loop approach. Display of the information, or the R in CRUD, is obviously vital. In my research, so far, I haven't seen much discussion regarding display of data in a many-to-many scenario; but, I'm certain I haven't been correctly studying the matter.
Thanks again for any assistance. Below is the code:
<?php
require_once '../includes/session_timeout_db.php';
?>
<?php
require_once '../includes/connection.php';
// initialize flag
$OK = false;
// create database connection
$conn = dbConnect('read', 'pdo');
// get details of selected record
if (isset($_GET['candidate_id'])) {
// prepare SQL query
$sql = 'SELECT candidate_id, last_name, first_name, company, mas_number, last_modified, notes
FROM candidates WHERE candidate_id = ?';
$stmt = $conn->prepare($sql);
// pass the placeholder value to execute() as a single-element array
$OK = $stmt->execute([$_GET['candidate_id']]);
// bind the results
$stmt->bindColumn(1, $candidate_id);
$stmt->bindColumn(2, $last_name);
$stmt->bindColumn(3, $first_name);
$stmt->bindColumn(4, $company);
$stmt->bindColumn(5, $mas_number);
$stmt->bindColumn(6, $last_modified);
$stmt->bindColumn(7, $notes);
$stmt->fetch();
// use value of query 2 as parameter in query 3
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
$field = $result['field'];
// query 2 get positions associated with Candidate
$sql = 'SELECT position_id FROM pos2cands WHERE candidate_id = ?';
$stmt = $conn->prepare($sql);
// pass the placeholder value to execute() as a single-element array
$result = $stmt->execute([$_GET['candidate_id']]);
// bind the results
$stmt->bindColumn(1, $position_id);
// query 3 find position data
$sql = 'SELECT position_id, position_number, title
FROM positions WHERE position_id = ?';
$stmt = $conn->prepare($sql);
// pass the placeholder value to execute() as a single-element array
$result = $stmt->execute([$field]);
// bind the results
$stmt->bindColumn(1, $position_id);
$stmt->bindColumn(2, $position_number);
$stmt->bindColumn(3, $title);
}
}
// get error message if query fails
if (isset($stmt) && !$OK) {
$error = $stmt->error;
}
elseif (!$stmt) {
$error = $conn->error;
}
?>
Copy link to clipboard
Copied
OK, some of the code you listed is not how I do things, but is not wrong. Take for instance. . .
$stmt->bindColumn(4, $company);
I prefer to use associative relationships, like this:
$sql->bindValue("invoice_set", INVOICE_SET);
After all, what if the data table changes and company is no longer the forth column?
I am really busy this weekend, even though it is a holiday weekend, so right now all I can do is paste in some sample code to show what my code looks like.
This is my connection script:
<?php
$dsn ='mysql:dbname=mydata;host=localhost;port=3306';
$user='username';
$password='password';
try {
$dbh = new PDO($dsn, $user, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
//$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->query=("set names utf8");
$dbh->exec('SET CHARACTER SET utf8');
$dbh->exec('SET character_set_server=utf8');
}
catch (PDOException $e) {
die('Connection failed: '.$e->getMessage());
}
I work with Chinese, Japanese and other languages, which is why the script above makes sure everything is UTF-8 copacetic.
Note that the same script is used locally and remotely with the exception of the ERRMODE line
So now we have $dbh defined as the object, so we will refer to it when we send a query to the database
// get translations
$sql=$dbh->prepare("SELECT label, translation FROM language_labels WHERE language_id=:language_id");
$sql->bindValue("language_id", $language_selected);
try{
$sql->execute();
}catch(PDOException $e)
{echo $e;}
while ($result = $sql->fetch(PDO::FETCH_ASSOC)){
${$result['label']."_lbl"} = $result['translation'];
}
// end translations
The try/catch is used to catch errors but will only display them in the page if the ERRORMODE is verbose.
If we are going to run another query within the curly braces of the while loop, then we can't use $sql as an object name because it is currently active. Otherwise, I always use $sql for each query in a script because each new object definition cancels the previous one of the same name.
With PDO, there are several fetch options but fetching the associative array with fetch(PDO::FETCH_ASSOC) is what I use most of the time.
I am not addressing here your issue regarding the table relationships. My preference is, where possible, to use a join. Joins in SQL are extremely useful to get comfortable with. You can even create joins on the same table. For instance, a single query could aggregate all of a customer's credits as well as aggregate all of their debits.
Copy link to clipboard
Copied
Thanks Rob for getting back to me on this holiday weekend. Hopefully you'll have some time to have it really be a holiday.
You've certainly provided very helpful information in the code samples and your discussion of how you approach script content. This is highly educational and informative. Being a beginner, I'm just trying to soak it all in. Yet, I think I'm comprehending the overall concepts.
I will study more your discussion of left joins in your Sept 1 response. It appears, as you noted, joins has great potential to deal with table relationship issues.
After I review your response, I'm certain I will have questions (hopefully not too stupid of questions) but I am a beginner. Trustfully my pestering you at that point will not be a problem.
Thanks again for your response on this holiday weekend. .
Copy link to clipboard
Copied
I'm trying to remember where I learned PDO. I have a little kindle ebook called "Learning PHP Data Objects." My recollection is that it was OK as an introduction.
Of course there really isn't much one needs to know about PDO or MySQLi. The real challenge is SQL. I own a book titled "SQL for MySQL Developers" by van der Lans. It's very comprehensive. Maybe too comprehensive. But it's a good resource.
Copy link to clipboard
Copied
Thanks for the book titles. I will put them in my resource library.