I am using PDO to execute a query for which I am expecting ~500K results. This is my query:
SELECT Email FROM mytable WHERE flag = 1
When I run the query in Microsoft SQL Server management Studio I consistently get 544838 results. I wanted to write a small script in PHP that would fetch these results for me. My original implementation used fetchAll(), but this was exhausting the memory available to php, so I decided to fetch the results one at a time like so:
$q = <<<QUERY
SELECT Email FROM mytable WHERE flag = 1
QUERY;
$stmt = $conn->prepare($q);
$stmt->execute();
$c = 0;
while ($email = $stmt->fetch()[0]) {
echo $email." $c\n";
$c++;
}
but each time I run the query, I get a different number of results! Typical results are:
445664
445836
445979
The number of results seems to be short 100K +/- 200 ish. Any help would be greatly appreciated.
fetch() method fetches one row at a time from current result set. $stmt->fetch()[0] is the first column of the current row.
Your sql query has no ordering and can have some null or empty values (probably).
Since you are controlling this column value in while loop, if the current row's first value is null, it will exit from the loop.
Therefore, you should control only fetch(), not fetch()[0] or something like that.
Also, inside the while loop, use sqlsrv_get_field() to access the columns by index.
$c = 0;
while ($stmt->fetch()) { // You may want to control errors
$email = sqlsrv_get_field($stmt, 0); // get first column value
// $email can be false on errors
echo $email . " $c\n";
$c++;
}
sqlsrv_fetch
Related
Been working at this for 3 days and tried every possible example instructions I could find in those 3 days. I have to ask for help in trying to understand what is not functioning here with regards to the OUTPUT parameter. This is a machine to machine operation, no terminal or webpage human interaction and only exists on an internal private subnet. Caller--> Cisco IVR-->PHP-->MSSQL DB-->PHP-->Cisco IVR--->Caller. I have included my entire PHP working script but I want to focus on why my parameter 3 statement does not function. My MSSQL stored procedure code 1st.
ALTER PROCEDURE [dbo].[pIOGetEligSSNDOB]
#SSN NVARCHAR(9),
#DOB NVARCHAR(8),
#RCODE INT OUTPUT
AS
BEGIN
SELECT * FROM ZASMasterDB.dbo.Eligibility WHERE SSN = #SSN AND DOB = #DOB
IF ##ROWCOUNT >= 1
SET #RCODE = 3
ELSE
SET #RCODE = 5
SELECT 'ReturnCode' = #RCODE
RETURN #RCODE
END
****My working PHP version 7.4.10 script using PDO****
$ID = $_GET['MEMBER_ID']; $DOB = $_GET['DOB'];
$start_time = microtime(true);
$sth = $pdo->prepare("exec dbo.pIOGetEligSSNDOB ?, ?, ? "); // I am using 2 INPUT and 1 OUTPUT
$RCODE = 0;
$row_count = 0;
$sth->bindParam(1, $ID); // Members social security number as INPUT to SQL server
$sth->bindParam(2, $DOB); // Members date of birth as INPUT to SQL server
$sth->bindParam(3, $RCODE, PDO::PARAM_INT|PDO::PARAM_INPUT_OUTPUT,10); //$RCODE to hold the OUTPUT integer
$sth->execute();
$time = round( (microtime(true) - $start_time), 4);
$col_count = 0;
//ARRAYS TO HELP ORGANIZE THE RESULTS
$column_name_array = array();
$rows_array = array();
// THE LOOP!
while($row = $sth->fetch(PDO::FETCH_ASSOC)){
$row_count++; // A MUST for the IVR to know how many records returned
$col_count = count($row);
$column_name_array = array_keys($row);
$row = array_map('trim',$row);
$rows_array[] = join("·", array_values($row)); //Delineation for the IVR
}
$sth = null;
$pdo = null;
?>
<response>
<time><?=$time?></time>
<cols><?=$col_count?></cols>
<rows><?=$row_count?></rows>
<code><?=$RCODE?></code> // This should be the value of the OUTPUT parameter from SQL DB
<column_names>
<?=join("·", $column_name_array)?>
</column_names>
<data>
<?=join("·", $rows_array)?>·
</data>
</response>
This is the result of the above code. It is perfect, except no OUPUT integer from SQL server to the $RCODE which would then display between the code elements.
This is the result of executing it in SQL Management Studio
I tried, changed the int value and still nothing. My PHP wont allow (? OUPUT) in the prepare line as the 3rd thing, doesn't like the ? mark. More research on here shows someone saying you need a DECLARE. So I made my line:
$sth = $pdo->prepare("DECLARE #RCODE INT; exec pIOGetEligSSNDOB ?,?, #RCODE OUTPUT; ");
and still no return value but PHP didn't complain, got my regular data back still. The I tried to use the exact same thing in my prepare line as I use manually when in SQL mgt studio.
$sth = $pdo->prepare("DECLARE #RCODE INT; exec pIOGetEligSSNDOB ?,?, #RCODE = #RCODE OUTPUT;");
and no $RCODE result.
So I had read about the whole "nextRowset" thing while I as pulling my hair out days ago, but then forgot about it while still trying to get something to work. Thank you for reminding me Zhorov!!. So I added the do to my original while and then added the while at the end of all the do, just like in those examples.
do {
while($row = $sth->fetch(PDO::FETCH_ASSOC)){
$row_count++;
$col_count = count($row);
$column_name_array = array_keys($row);
$row = array_map('trim',$row);
$rows_array[] = join("·", array_values($row));
}
} while ($sth->nextRowset());
And now my final XML output is a little differnet, and I do see the integer number 7 but it has been added at the end of my regular data fetch results rather than where I need it to go, and that is between the code elements. Then instead of all my regular column names that usually would display between the column name elements, it now shows the name 'ReturnCode', which I dont want to see. So I definitly have a formatting problem now. I did follow the examples of the nextRowset code exactly like they are explained, but somehow somethings wrong with the final output. This is very difficult when you are still trying to learn PHP and don't quite understand the placements and syntaxes of everything, but I am closer now than I was before.
So I believe I solved my own question here. It seems the 'Free TDS driver' I am using for my PHP PDO code does not support the use of OUTPUT parameters from MSSQL when first having the PHP execute the MSSQL stored procedure that returns the expected result set. The notes I found to support this was right here on Stack, but the link in those notes explaining why the OUTPUT parameter is not supported say that Microsoft did away with the OUTPUT parameter in MSSQL version 7 and up? Ver7 came out in 1998 I think. I don't know if that part is that true, but I can assure you, in my problem here, the OUTPUT parameter would not function at all. So the alternative was to use the nextRowset command in PHP, but NOT in the way that so many examples show. I had to figure it out myself, trial and error. I do not need a do..while loop and I do not need a while at the end of my original loop. Had to make the SQL code look like this.
CREATE PROCEDURE [dbo].[pIOGetEligSSDOB]
#SSN nvarchar(10),
#DOB nvarchar(10)
AS
BEGIN
DECLARE #RCODE int
SELECT * FROM ZASMasterDB.dbo.Eligibility WHERE SSN = #SSN AND DOB = #DOB
IF ##ROWCOUNT >= 1
SET #RCODE = 7
ELSE
SET #RCODE = 8
SELECT #RCODE
END
Then in PHP I keep my original working loop exactly the way it is and I added the 2 lines after the loop code. I first had to advance to the nextRowset like all of the example code showed, but I then have to go into that next rows column and pull out the single digit return code and put it into my $rcode to finally display on my XML output in the elements.
while($row = $sth->fetch(PDO::FETCH_ASSOC)){
$row_count++;
$col_count = count($row);
$column_name_array = array_keys($row);
$row = array_map('trim',$row);
$rows_array[] = join("·", array_values($row));
}
$sth ->nextRowset();
$rcode = $sth->fetchColumn();
So final output looks like:
we faced the same issue with a SP intended to return 3 out parameters cause of side effects on sql server thru a PDO call:
not sure this can help but you can try prefix your call to the SQL Server procedure with a "SET NOCOUNT ON;" statement before shooting
or begin and end yout Transact sql strored procedure by
SET NOCOUNT ON;
...
SET NOCOUNT OFF;
May work in your case ... or not if your SP calls extra procedure(s) where these flags are not correctly set...
We're still looking on a better solution...
I'm using the SQL Server drivers for PHP to access a SQL Server database and I have a problem to update some data using sqlsrv_prpare and sqlsrv_execute functions.
I'm running two queries:
In the first query I'm retrieving some binary data (In SQL Server Management Studio, this query takes about 15 minutes to getting completed);
Then, for each row returned by the first query execution I'm trying to Update some data on the database.
Here's how my code looks like:
$query1 = "SELECT tgt.id, src.file, src.field1 from [Table1] tgt inner join [Table2] src on tgt.id = src.id order by tgt.id";
$query2 = "UPDATE [Table1] SET field1 = ? WHERE id = ?";
$getFiles = sqlsrv_query($con, $query1); //$con is the connection with the database, received by parameter
while($row = sqlsrv_fetch_array($getFiles, SQLSRV_FETCH_BOTH)) {
/* Some code here */
$file = $row[1];
$value = $row[2];
try {
if(!is_null($file)) {
$stmt = sqlsrv_prepare($con, $query2, array(&$value, &$row[0]));
if( $stmt === false ) {
die( print_r( sqlsrv_errors(), true));
}
sqlsrv_execute( $stmt );
}
} catch (Exception $e) {
error_log("\nError: " . $e->getMessage());
}
} //end while
sqlsrv_free_stmt($getFiles);
sqlsrv_close($con);
The problem is that the code inside the loop works fine to the first row, but on the second the update query isn't executed. The sqlsrv_prepare returns the value 1, but the sqlsrv_execute doesn't returns anything.
I'm thinking that the problem could be related to the first query execution time, but I don't know how to check this, considering that no error log is generated, the script just keeps executing forever.
EDIT: Actually, the example was simplified. The values that will be updated on tgt table are calculated using some data that are in src table and other application data. That's the reason why I use the loop, for each row returned by query1 specific values are calculated and used on query2. I already checked that these values are correctly calculated, this is why I thought it's better to simplify the example.
To solve this problem I have to ran the queries separately:
First I ran the query1, made the computation of the data that I needed to update the tgt table and stored them in an array;
Then, using the data stored in array, I ran the query2.
No other changes were needed.
i had a headache over the last few days trying to understand this snippet of code. it's about retrieving data from a database table nothing hard (i'm using mysql) . but i'm trying to understand the code. here is the code:
<?php
include 'PDOconnect.php';
//Query
$result = $connection->query('SELECT * FROM video_games');
//Fetch
$data = $result->Fetch();
while ($data = $result->Fetch()) {
echo $data['name']."<br />";
}
?>
first let me explain, the second line is including the connection code to the database i'm using the PDO way of connecting. the connection is fine . my table is called video_games and it had a column called 'name'. and i'm trying with this code to retrieve all the data from the column 'name'.
1- so what i want to understand is what is the $result variable (line 6) , i've heard it's a Resource. what a resource in mysql means, and what's inside of the variable $result is it the whole table or what exactly ??
2- what the function fetch() does ?? it's confusing .
3- what i know from studying the basic syntax of php is that inside the while condition
the value must be true in order to execute the code inside.
but here there is ($data = $result->Fetch()) .
4- is the fetch() method automatically incremented ?? i mean why it is working successfully inside the while condition, so it must be incrementing over and over again ??
please help my mind is blowing right now.
$result is not a Resource,. it's a PDOStatement. see the docs: http://www.php.net/manual/en/pdo.query.php
Once a PDOStatement is executed (automaticalyl using ->query()) the statement holds the result the database returned.
Everytime you ->fetch() it returns the current row the Statement is pointing to. After that it points it pointer to the next row (so yes, 'automatically incremented).
Now, your code:
Everything inside if and while statements is evaluated using loose comparision (==)
if ( $a ) actually checks or $a == true.
$data = $result->fetch() simply sets a value to $data. Then the while checks or $data == true. IF so, it does what it has to do. (see php == vs === operator for more about comparisons in php)
Now, a little remark on your code: the first row is not outputted since you don't do anything with the first fetched result. Simply remove
//Fetch
$data = $result->Fetch();
So your code would become:
<?php
include 'PDOconnect.php';
//Query
$result = $connection->query('SELECT * FROM video_games');
while ($data = $result->Fetch()) {
echo $data['name']."<br />";
}
A good tutorial about PDO: http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
1 what is the $result variable
It is not a resource. It's actually an object of PDOStatement class
2 what the function fetch() does ?? it's confusing.
A manual page is always at your service. Just type PDO fetch in the browser's address bar and click the first link opened. It is extremely easy and no less powerful.
I have a time dependent script I am working on and used microtime() to find the bottle neck. I determined the time increase is caused by doing a check on 300+ values to see if they exist in a database one at a time at 0.04 seconds a query.
The background of the script is it is a caching script. I need to see if it exists in the DB so I need a true/false (obtained by a rowCount) but i also need a way to relate a false to a value so I can update it. I know using a WHERE tag IN (:ARRAY) would work faster than the individual calls, but I cant think of a way to apply an association of true/false to value in this method.
My current code is below:
//loop through all our values!
//prepare out reusuable statement
$stmt = $db->prepare("SELECT * from cache WHERE value=?");
foreach($values as $tempVal)
{
//find if its in the database
try
{
$stmt->execute(array($tempVal));
$valCount = $stmt->rowCount();
} catch(PDOException $ex) {
echo "PDO error send this to us: " . $ex->getMessage();
}
//update flag
$addToUpdate = 1;
//if its in the database
if($valCount > 0)
{
//get the tag data
$valRes= $stmt->fetch();
//check if cache expired
$addToUpdate = 0;
}
//add to update list
if($addToUpdate)
{
//needs updating
$updateList[] = $tempVal;
//add to not in DB list to minimize queries
if($tagTCount == 0)
{
$notInDB[$tempVal] = $tempVal;
}
}
Any suggestions? I can explain more if anything is not clear.
Thank you,
Nick
So you just issue your query with the complete array, using the IN (?,?,?,?,?...) list:
// abstract, use a PDO wrapper of your choosing
$query = db("SELECT * FROM cache WHERE value IN (??)", $values);
Then iterate over the result list. Only matched $values will return. So build your first list from that:
foreach ($query as $row) {
$updateList[] = $row["value"];
}
To get the list of absent entries, just diff that against your original array:
$notInDB = array_diff($values, $updateList);
You could of course use a second NOT IN query. But doing that differentiation in PHP is simpler.
I am using the sqlsrv driver for IIS so I can connect to a MS SQL server in PHP.
I've managed to convert a lot of my original mysql_ code and all going well, until I tried to SELECT some DateTime fields from the database. They were coming back as Date objects in PHP rather than strings, I found the fix which is adding this to the connection array:
'ReturnDatesAsStrings'=>1
Since doing that though my code is broken when trying to populate my recordset:
function row_read($recordset) {
if (!$recordset) {
die('<br><br>Invalid query :<br><br><bold>' . $this->sql . '</bold><br><br>' . sqlsrv_error());
}
$rs = sqlsrv_fetch_array($recordset);
return $rs;
}
The error is: sqlsrv_fetch_array(): 16 is not a valid ss_sqlsrv_stmt resource
There is such little amount of help on that error in Google so this is my only shot! I just don't get it.
row_read is called from within a While: while ($row = $db->row_read($rs)) {
Any ideas?
Just to add more code and logic - I do a simple SELECT of all my orders, then as it loops through them, I do another 2 SELECT's on the orders table then the customer table. It's falling down when I try these extra 2 'gets':
$this->db->sql = "SELECT * FROM TicketOrders";
$rs = $this->db->query($this->db->sql);
$this->htmlList->path("skin/search.bookings");
if ($this->db->row_count != 0) {
while ($row = $this->db->row_read($rs)) {
// Load the order row
$this->TicketOrders->get($this->db, $row['Id']);
// Load the customer row
$this->Customers->get($this->db, $row['CustomerId']);
Did you pass this resource variable by another function? If yes, you can try by executing the sqlsrv_query and executing sqlsrv_fetch_array in one function; don’t pass the ss_sqlsrv_stmt resource by another function. Hope that it will help.
Does your program involves a nested query function?
If so, the next question is: are you opening the same database in the inner query function?
Try these changes:
comment out the lines that open the database, including the { and } that enclose the function,
change the name of connection and array variables between the outer loop and the inner loop.
In other words, the outer loop may have:
$tring = sqlsrv_query($myConn, $dbx_str1);
while( $rs_row1 = sqlsrv_fetch_array($tring, SQLSRV_FETCH_ASSOC))
and the inner loop would have:
$tring2 = sqlsrv_query($myConn, $dbx_str2);
while( $rs_row2 = sqlsrv_fetch_array($tring2, SQLSRV_FETCH_ASSOC))
sqlsrv_fetch_array need a ss_sqlsrv_stmt resource. There must be something wrong with your SQL.