I'm an old school developer and just getting in the WWW programming world. I'm developing an application with HTML, CSS, PHP and MSSQL Server 2008 R2 for the company i'm working with.
In my application I'm using stored procedures to insert, modify, delete or query information from/to the database. Not using TSQL instructions at all, just executing stored procedures from the PHP code.
I'm using PHP 5 and SQLSRV driver for database interaction.
Everything working fine so far, but now I'm stuck on the Insert piece... If everything is ok, the SP inserts the record, if not, it doesn't... but i'm not seeing the result until i query the table again just to see if the record is there or not.
Im using the following code in PHP to run the SP that inserts the record in the table:
function spinserta($tabla, $columnas, $valores, $cnct) {
$stmt = 'Exec spinsert #tabla=?,#columnas=?,#valores=?';
$params = array($tabla,$columnas,$valores);
$result = sqlsrv_query($cnct,$stmt,$params) ;
return $result;
}
if the transaction is not succesful, im not getting anything in the $result variable and would like to have the resulting message from the SP in order to display an error message to the user.
How to get the resulting message from the SP (no matters if it is an error or not)?
Thanks in advance!
This is some code that I have in one of my applications. See if it helps:
//Query SQL
$tsql = "Exec spinsert #tabla=?,#columnas=?,#valores=?";
$params = array($tabla,$columnas,$valores);
//Execute the stored query
$stmt = sqlsrv_query($conn, $tsql, $params);
if ($stmt === false)
{
echo "<h3>Error in query preparation or execution.</h3>";
ListErrors();
die;
}
else {
echo "Insert Successful";
}
// this should help for the non-insert/update case
$arr = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
var_dump($arr);
after hours of researching.... finally got the concept! here is the thing: the original PHP code was:
function spinserta($tabla, $columnas, $valores, $cnct) {
$stmt = 'Exec spinsert #tabla=?,#columnas=?,#valores=?';
$params = array($tabla,$columnas,$valores);
$result = sqlsrv_query($cnct,$stmt,$params) ;
return $result;
}
and the original SP was:
ALTER PROCEDURE [dbo].[spinsert]
#tabla varchar(50),
#columnas varchar(8000),
#valores varchar(8000)
AS
BEGIN
SET NOCOUNT ON;
declare #orden varchar(8000)
declare #return_value int
set #orden='Insert into ' + #tabla + ' (' + #columnas + ') values (' + #valores + ')';
execute (#orden);
return
END
very straight forward... When the php code was executed and the SP succeded, the variable $result was loaded with "Resource id#14", if the SP failed, the $result value was null.
Things were working well!!! But not the way i wanted. Then i found this article: http://msdn.microsoft.com/en-us/library/ms178592.aspx
Based on that I modified the SP:
ALTER PROCEDURE [dbo].[spinsert]
#tabla varchar(50),
#columnas varchar(8000),
#valores varchar(8000)
AS
BEGIN
SET NOCOUNT ON;
declare #orden varchar(8000)
declare #return_value int
begin try
set #orden='Insert into ' + #tabla + ' (' + #columnas + ') values (' + #valores + ')';
set #return_value=0;
execute (#orden);
end try
begin catch
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT #return_value = ERROR_NUMBER()
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
end catch
return #return_value
END
and the PHP code:
function spinserta($tabla,$columnas,$valores,$cnct) {
$tsql = 'Exec spinsert #tabla=?,#columnas=?,#valores=?';
$params = array($tabla,$columnas,$valores);
$stmt = sqlsrv_query($cnct,$tsql,$params) ;
$errors=sqlsrv_errors();
if ($stmt === false or $stmt===0) {
foreach( $errors as $error ) {
$stmt=str_replace("'","","Error: ". $error['code']. " - " . $error['message']);
}
} else {
$stmt="1";
}
return $stmt;
}
There were two problems with my original approach, 1 at database engine side, the SP was not really generating a system error, even though the statement failed. With the Try-Catch, technique, pluse the RAISEERROR concept, the SP was finally generating the system error when the statemente failed. After this, it was just matter of minor adjustments to the PHP code.
With this approach, the validation of information sent to the database, is done at Database Engine side, eliminating the need of writing a lot of code, just to validate the fields in the forms at the submission time. what is needed is to ensure database tables, relationships, constraints, integrity and others are well applied, and the database will protect itself against incorrect data. If errors with information provided in the form are submited, the database will reject them and the code will show to the user the proper errors behind.
I would like to see if something similar is doable with MySQL..., i think so!
Many thanks to Maximus2012!!! Cheers!!!
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 have stored procedures written in CLR (C#) that are sitting in SQL Server DB. When I run those procedures I get return value as well as output. Procedure code is:
using(SqlConnection conn = new SqlConnection("context connection=true"))
{
SqlPipe pipe = SqlContext.Pipe;
conn.Open();
string table = tableName.ToString();
string columns = columnList.ToString();
string values = valueList.ToString();
string types = typeList.ToString();
cmd.Connection = conn;
cmd.CommandText = #"
INSERT INTO " + table + " (" + columns + ") VALUES(" + values + ");SELECT SCOPE_IDENTITY();";
pipe.ExecuteAndSend(cmd);
return 48;
Now when I run this code in SQL Management I get:
now in PHP app I want to access either SCOPE_IDENTITY() value sent to output or custom return value (both will work in my case).
$params = array(
array(&FW::$session->requestUniqueId, SQLSRV_PARAM_IN),
array(&$this->rootTable, SQLSRV_PARAM_IN),
array(&$columns, SQLSRV_PARAM_IN),
array(&$values, SQLSRV_PARAM_IN),
array(&$types, SQLSRV_PARAM_IN)
);
$sql="EXEC sp_data_insert #requestId=?, #tableName=?, #columnList=?, #valueList=?, #typeList=?";
$prep=sqlsrv_prepare($this->conn,$sql,$params);
if($prep!==false)
{
$res=sqlsrv_execute($prep);
if($res)
{
//HOW TO ACCESS RETURN VALUE OR OUTPUT VALUE YIELD BY SQL PROCEDURE HERE ?
}
}
Can anyone tell me how to access return value 48 returned by CLR stored procedure or/and output value (SCOPE_IDENTITY)?
I know I can add output paramter but this would be a pain, since I would have hundrets of procedures to change. I need to access these values without changing SQL CLR.
Problem is that I cannot make this work in PHP. What can be wrong? This code does execute the stored procedure (I see data in DB changed), but I cannot access return value in PHP:
$sql="DECLARE #ret int EXEC #ret=$stored_procedure; SELECT #ret";
$prep=sqlsrv_prepare($this->conn,$sql,$params);
if($prep!==false)
{
$res=sqlsrv_execute($prep);
if($res)
{
while($row=sqlsrv_fetch_array($prep))
{
print('row='.print_r($row,true));
}
}
}
You don't need to change the SQLCLR stored procedure, you need to get rid of it entirely. It is absolutely pointless in this usage. Not only do you gain nothing, but you now have a system that is far more convoluted and harder (i.e. costlier) to maintain than the already convoluted nature of the yet-another-generic-query-mechanism.
You could do all of this in T-SQL with sp_executesql (though still not a great idea) and it would be clearer as to how to get the SCOPE_IDENTITY() value.
You already know how to get the return value as you are doing it in the SSMS screen shot. You just need to re-SELECT that variable after the EXEC so that it will be a result set $res.
You might could capture the SCOPE_IDENTITY() by creating a local temporary table (or table variable) with a single INT column and then doing INSERT INTO #tmp ([ID]) EXEC proc;, similar to:
GO
CREATE PROC #ttt
AS
SELECT 1
UNION ALL
SELECT 56788;
RETURN 55;
GO
DECLARE #T TABLE (Col1 INT);
DECLARE #Y INT;
INSERT INTO #T ([Col1])
EXEC #Y = #ttt;
SELECT #Y, *
FROM #t;
I have a stored procedure in SQL Server 2012 which first checks if data exists, if it does then it sets the Output parameter #Return to 1 and runs a select query and if not, sets #Return to 0 and returns a different select query.
When testing this stored procedure to ensure the data is accurate it is perfect and returns the data I am expecting. The problem lies on the PHP side when trying to read the output parameter it is showing ��t_rrr when it should be showing a 1 or 0. I believe the problem may be in the Predefined Constant in the sqlsrv_query but i cannot seem to get it working. Here is my code:
PHP:
if(isset($_GET['accno'])) {
$search = $_GET['accno'];
$return = "";
$tsql_callSP = "EXEC Intranet.CustomerSearch #Search=?, #Return=?";
$params = array(
array($search, SQLSRV_PARAM_IN),
array($return, SQLSRV_PARAM_OUT, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR),SQLSRV_SQLTYPE_INT),
);
/* Execute the query. */
$stmt = sqlsrv_query( $conn, $tsql_callSP, $params);
if( $stmt === false )
{
echo "EXEC Intranet.CustomerSearch Failed\n";
die( print_r( sqlsrv_errors(), true));
}
while($res = sqlsrv_next_result($stmt))
{
// make sure all result sets are stepped through,
// since the output params may not be set until this happens
}
echo $return;
if($return == 0) { ?>
//1st Result Set
while($row = sqlsrv_fetch_array($stmt)) {
}
<?php } elseif($return == 1) { // End if Return 0 ?>
//2nd Result Set
while($row = sqlsrv_fetch_array($stmt)) {
}
<?php } // End if Return 1 ?>
SQL Stored Procedure:
USE [Intranet]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [Intranet].[CustomerSearch]
#Search nvarchar(10)
,#Return int output
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS
(
SELECT KeyCode
FROM Autopart.dbo.Customer
WHERE KeyCode = #Search
)
BEGIN
SELECT
Customer.KeyCode
,Customer.X13
,Customer.Name
,Customer.Addra
,Customer.Addrb
,Customer.Addrc
,Customer.Addrd
,Customer.Addre
,Customer.PCode
,Customer.CTitle
,Customer.Climit
,Customer.Ptype
,Customer.Stel
,Customer.SCont
,Customer.ACont
,Customer.XString5
,Customer.Locked
,Customer.Email
,Customer.StopStatus
,CusNotes.Comment
FROM
Autopart.dbo.Customer
LEFT OUTER JOIN Autopart.dbo.CusNotes ON Autopart.dbo.Customer.KeyCode = Autopart.dbo.CusNotes.Account
WHERE
(Customer.KeyCode = #Search)
AND (CusNotes.Seqno = 1 OR CusNotes.Seqno IS NULL)
SET #Return = 1
END ELSE BEGIN
SELECT TOP 100
KeyCode
,Name
,PCode
,Addra
,Addrb
,Addrc
FROM
AUTOPART.dbo.Customer
WHERE
(KeyCode LIKE '%'+#Search+'%'
OR Name LIKE '%'+#Search+'%'
OR PCode LIKE '%'+#Search+'%')
SET #Return = 0
END
END
I have tried changing the PHP and SQL types around but can't get the desired result. What is strange is if I create a stored procedure that is an INSERT or UPDATE statement the OUTPUT returns correctly.
EDIT:
SQL Server Stored Procedure Collation is Latin1_General_CI_AS
I happened to have SQL Server and PHP installed on my VirtualBox, so I played around.
Few things:
Init parameters with the value of a proper type.
Use & when passing parameters.
Read all results that the stored procedure returns using sqlsrv_next_result. This advice is taken from sqlsrv_prepare in the example at the bottom.
Here is the code:
$search = "qweryt";
$return = 123;
$tsql_callSP = "EXEC Intranet.CustomerSearch #Search=?, #Return=?";
$params = array(
array(&$search, SQLSRV_PARAM_IN),
array(&$return, SQLSRV_PARAM_OUT),
);
$stmt = sqlsrv_query($conn, $tsql_callSP, $params);
if ($stmt === false)
{
echo "EXEC Intranet.CustomerSearch Failed\n";
die( print_r( sqlsrv_errors(), true));
}
else
{
while($res = sqlsrv_next_result($stmt))
{
// make sure all result sets are stepped through,
// since the output params may not be set until this happens
}
}
echo $return;
Verified with PHP 5.4.28, SQL Server Express 2014, Microsoft driver for PHP SQLSRV 3.2
When you have a stored procedure that does only INSERT or UPDATE, i.e. doesn't return result of SELECT, then sqlsrv_next_result happen to be not needed to get values of the output parameters. If there is SELECT together with output parameters, it effectively means that procedure returns several result sets and you need to read all of them.
Actually, here is a very similar question.
Edit
If I understood you correctly, you want to retrieve the value of the OUTPUT parameter first. Then you want to retrieve the result of the SELECT statement.
As far as I understand, it is not possible. SQL Server returns result of SELECT statement in the first result set, which you can read using sqlsrv_fetch_array. Then you move to the second result set using sqlsrv_next_result. At this moment the value of the OUTPUT parameter is transferred to your PHP variable, because SQL Server sends the values of OUTPUT parameters in the last result set. After you call sqlsrv_next_result the first result set is no longer available when you do sqlsrv_fetch_array.
By the way, when you said
What is strange is if I create a stored procedure that is an INSERT or
UPDATE statement the OUTPUT returns correctly.
, it makes perfect sense. When stored procedure has no SELECT statements and it has SET NOCOUNT ON;, then the only result set that is returned is for OUTPUT parameters, so your PHP variable is assigned with correct value without explicit calls to sqlsrv_next_result.
So, you have at least two options.
1) Read the full result of SELECT into a temporary array using sqlsrv_fetch_array. Then use sqlsrv_next_result to get the value of the OUTPUT parameter. Then process result of the first SELECT saved in your array based on the received value of the OUTPUT parameter.
2) Do not use OUTPUT parameter at all. With such parameter(s) SQL Server returns multiple result sets implicitly. Since you have multiple result sets any way, return two result sets explicitly. In your stored procedure at first do SELECT that returns just one row with one number, then your main SELECT:
ALTER PROCEDURE [Intranet].[CustomerSearch]
#Search nvarchar(10)
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS
(
SELECT KeyCode
FROM Autopart.dbo.Customer
WHERE KeyCode = #Search
)
BEGIN
SELECT CAST(1 AS int) AS ReturnCode;
SELECT
Customer.KeyCode
,Customer.X13
,Customer.Name
,Customer.Addra
,Customer.Addrb
,Customer.Addrc
,Customer.Addrd
,Customer.Addre
,Customer.PCode
,Customer.CTitle
,Customer.Climit
,Customer.Ptype
,Customer.Stel
,Customer.SCont
,Customer.ACont
,Customer.XString5
,Customer.Locked
,Customer.Email
,Customer.StopStatus
,CusNotes.Comment
FROM
Autopart.dbo.Customer
LEFT OUTER JOIN Autopart.dbo.CusNotes ON Autopart.dbo.Customer.KeyCode = Autopart.dbo.CusNotes.Account
WHERE
(Customer.KeyCode = #Search)
AND (CusNotes.Seqno = 1 OR CusNotes.Seqno IS NULL)
;
END ELSE BEGIN
SELECT CAST(0 AS int) AS ReturnCode;
SELECT TOP 100
KeyCode
,Name
,PCode
,Addra
,Addrb
,Addrc
FROM
AUTOPART.dbo.Customer
WHERE
KeyCode LIKE '%'+#Search+'%'
OR Name LIKE '%'+#Search+'%'
OR PCode LIKE '%'+#Search+'%'
;
END
END
In PHP code call sqlsrv_fetch_array first time to read ReturnCode. Then call sqlsrv_next_result to switch to the second result set. Then call sqlsrv_fetch_array to read result of the main SELECT.
$search = "qweryt";
$tsql_callSP = "EXEC Intranet.CustomerSearch #ParamSearch=?";
$params = array
(
array(&$search, SQLSRV_PARAM_IN)
);
$stmt = sqlsrv_query($conn, $tsql_callSP, $params);
if( $stmt === false )
{
echo "EXEC Failed\n";
die( print_r( sqlsrv_errors(), true));
}
echo "First result set:\n";
while ($row = sqlsrv_fetch_array($stmt))
{
var_dump($row);
}
echo "Next result:\n";
$next_res = sqlsrv_next_result($stmt);
var_dump($next_res);
echo "Second result set:\n";
while ($row = sqlsrv_fetch_array($stmt))
{
var_dump($row);
}
This is due to a character encoding problem with PHP output. Try using
header ('Content-type: text/html; charset=UTF-8');
or
header ('Content-type: text/html; charset=ISO-8859-1');
Also, you can check this nice article.
Using PHP v. 5.4, I am trying to connect to a SQL 2008 database and insert into and then return data from a SQL user defined table type.
The SQL table data type is defined as follows:
create type ScreensTableType as table(ElementCode decimal(6,2), ElementYear int, MinimumValue float, MaximumValue float, AndOr bit)
And a pared down version of my PHP code is:
<?php
$sqlStr='';
$serverName = "Server"; //serverName\instanceName
$connectionInfo = array( "Database"=>"Db");
$conn = sqlsrv_connect( $serverName, $connectionInfo);
if(!$conn ) {
echo "Connection could not be established.<br />";
die( print_r( sqlsrv_errors(), true));
}
$sqlStr="declare #ScreensParam [ScreensTableType] insert #ScreensParam values (5101.00,2011,0,100,1),(5103.00,2011,0,100,1) select * from #ScreensParam";
// print $sqlStr;
$getFormData=sqlsrv_query($conn, $sqlStr);
if( $getFormData === false )
{
if( ($errors = sqlsrv_errors() ) != null)
{
foreach( $errors as $error)
{
echo "SQLSTATE: ".$error[ 'SQLSTATE']."\n";
echo "code: ".$error[ 'code']."\n";
echo "message: ".$error[ 'message']."\n";
}
}
}
while( $row = sqlsrv_fetch_array( $getFormData, SQLSRV_FETCH_ASSOC) ) {
print '####'.$row['ElementCode'];
}
?>
The connection works and no errors are returned. However nothing at all is returned. If I print and run the $sqlStr in Management Studio it works fine. If I change the $sqlStr to "select top 10 * from someExistingDbTable" then I get data returned.
Does anybody have any ideas on how to do this or what I might be doing wrong?
Thanks very much,
Dan
Part 2
Here's what I'm trying to do - the sp returns requested financial data from a web form. A user chooses the following options from a form:
"financial item", "financial year", "minimum value", "maximum value", "and/or"
Revenue, 2010, 1000000, 10000000, 'and'
EPS, 2011, 0.5, 1.5, 'or'
There could be 1 row or 50 rows depending on the users request. I want to load those items into a UDTT. In the stored procedure I create a dynamic query. A cursor iterates over the rows of the UDTT and creates the conditions in the where clause of the dynamic query. The data is then returned.
The problem I'm having is I don't seem to be able to get PHP to return any results when I use the UDTT. I just used the simplified example above to see if anyone could get data returned.
I should add that when I print the "real" string I'm passing from PHP to SQL the stored procedure does return data. And that my connection to SQL is good.
Third and final part
I should have prefaced the original question by saying I'm a complete novice when it comes to PHP. Not bad at SQL though.
OK here's what I've tried:
SQL with no UDTT
drop procedure testUDTT
go
create PROCEDURE [testUDTT]
as
SET NOCOUNT ON
select XmlRequest = '###Data Returned###'
go
if I run the following PHP code data is returned:
$select="exec testUDTT";
$sqlResponse=sqlsrv_query($conn, $select);
while( $row = sqlsrv_fetch_array( $sqlResponse, SQLSRV_FETCH_ASSOC) ) {
print '####'.$row['XmlRequest'];
}
If I change $select to:
$select="declare #ScreensParam [ScreensTableType] exec testUDTT";
data is returned.
However if I change $select to:
$select="declare #ScreensParam [ScreensTableType] insert #ScreensParam values (5101.00,2011,0,100,1),(5103.00,2011,0,100,1) exec testUDTT";
NO data is returned and there are no errors???. I suspect SQL wants a "GO" between the "insert" and "exec" statements although this works fine when I run from SSMS.
I was thinking about this another way as well. If the above stored procedure had one input param like an int eg:
create PROCEDURE [testUDTT]
#InputInt int
as
SET NOCOUNT ON
if #InputInt > 0
select XmlRequest = '###Data Returned###'
my PHP code could look something like:
$inputInt=1;
$select="exec testUDTT #InputInt=?";
$params = array(
array($inputInt, SQLSRV_PARAM_IN)
);
$sqlResponse=sqlsrv_query($conn, $select, $params);
Now If I had this stored procedure using a UDTT:
create PROCEDURE [testUDTT]
#ScreensParam ScreensTableType READONLY
as
SET NOCOUNT ON
select XmlRequest = '###Data Returned###'
from #ScreensParam
go
and I try to break the PHP call to sql into 2 parts (not sure if this is correct, but gave it a try):
$add='declare #ScreensParam [ScreensTableType] insert #ScreensParam values (5101.00,2011,0,100,1),(5103.00,2011,0,100,1)';
$select='exec testUDTT #ScreensParam='.$screensParam;
sqlsrv_query($conn, $add);
$sqlResponse=sqlsrv_query($conn, $select);
I get the following error: SQLSTATE: 42000 code: 137 message: [Microsoft][SQL Server Native Client 11.0][SQL Server]Must declare the scalar variable "#ScreensParam". as I guess the UDTT is not in scope.
Also I wonder what the PHP syntax should be for passing #ScreensParam in the array? Would $select become exec testUDTT #ScreensParam=? where "?" = '#ScreensParam'. Don't know?
So I've found a solution creating XML instead of using UDTT which is acceptable. And I've spent more than enough time trying to get it to work. I have a sneaking suspicion that what I want to do can't be done with PHP currently.
If anyone has/finds a solution though, I'd love to know.
Thanks for the help Fuzzy Button. It was fun trying.
Using PHP v. 5.4, I am trying to connect to a SQL 2008 database and insert into and then return data from a SQL user defined table type.
The SQL table data type is defined as follows:
create type ScreensTableType as table(ElementCode decimal(6,2), ElementYear int, MinimumValue float, MaximumValue float, AndOr bit)
And a pared down version of my PHP code is:
<?php
$sqlStr='';
$serverName = "Server"; //serverName\instanceName
$connectionInfo = array( "Database"=>"Db");
$conn = sqlsrv_connect( $serverName, $connectionInfo);
if(!$conn ) {
echo "Connection could not be established.<br />";
die( print_r( sqlsrv_errors(), true));
}
$sqlStr="declare #ScreensParam [ScreensTableType] insert #ScreensParam values (5101.00,2011,0,100,1),(5103.00,2011,0,100,1) select * from #ScreensParam";
// print $sqlStr;
$getFormData=sqlsrv_query($conn, $sqlStr);
if( $getFormData === false )
{
if( ($errors = sqlsrv_errors() ) != null)
{
foreach( $errors as $error)
{
echo "SQLSTATE: ".$error[ 'SQLSTATE']."\n";
echo "code: ".$error[ 'code']."\n";
echo "message: ".$error[ 'message']."\n";
}
}
}
while( $row = sqlsrv_fetch_array( $getFormData, SQLSRV_FETCH_ASSOC) ) {
print '####'.$row['ElementCode'];
}
?>
The connection works and no errors are returned. However nothing at all is returned. If I print and run the $sqlStr in Management Studio it works fine. If I change the $sqlStr to "select top 10 * from someExistingDbTable" then I get data returned.
Does anybody have any ideas on how to do this or what I might be doing wrong?
Thanks very much,
Dan
Part 2
Here's what I'm trying to do - the sp returns requested financial data from a web form. A user chooses the following options from a form:
"financial item", "financial year", "minimum value", "maximum value", "and/or"
Revenue, 2010, 1000000, 10000000, 'and'
EPS, 2011, 0.5, 1.5, 'or'
There could be 1 row or 50 rows depending on the users request. I want to load those items into a UDTT. In the stored procedure I create a dynamic query. A cursor iterates over the rows of the UDTT and creates the conditions in the where clause of the dynamic query. The data is then returned.
The problem I'm having is I don't seem to be able to get PHP to return any results when I use the UDTT. I just used the simplified example above to see if anyone could get data returned.
I should add that when I print the "real" string I'm passing from PHP to SQL the stored procedure does return data. And that my connection to SQL is good.
Third and final part
I should have prefaced the original question by saying I'm a complete novice when it comes to PHP. Not bad at SQL though.
OK here's what I've tried:
SQL with no UDTT
drop procedure testUDTT
go
create PROCEDURE [testUDTT]
as
SET NOCOUNT ON
select XmlRequest = '###Data Returned###'
go
if I run the following PHP code data is returned:
$select="exec testUDTT";
$sqlResponse=sqlsrv_query($conn, $select);
while( $row = sqlsrv_fetch_array( $sqlResponse, SQLSRV_FETCH_ASSOC) ) {
print '####'.$row['XmlRequest'];
}
If I change $select to:
$select="declare #ScreensParam [ScreensTableType] exec testUDTT";
data is returned.
However if I change $select to:
$select="declare #ScreensParam [ScreensTableType] insert #ScreensParam values (5101.00,2011,0,100,1),(5103.00,2011,0,100,1) exec testUDTT";
NO data is returned and there are no errors???. I suspect SQL wants a "GO" between the "insert" and "exec" statements although this works fine when I run from SSMS.
I was thinking about this another way as well. If the above stored procedure had one input param like an int eg:
create PROCEDURE [testUDTT]
#InputInt int
as
SET NOCOUNT ON
if #InputInt > 0
select XmlRequest = '###Data Returned###'
my PHP code could look something like:
$inputInt=1;
$select="exec testUDTT #InputInt=?";
$params = array(
array($inputInt, SQLSRV_PARAM_IN)
);
$sqlResponse=sqlsrv_query($conn, $select, $params);
Now If I had this stored procedure using a UDTT:
create PROCEDURE [testUDTT]
#ScreensParam ScreensTableType READONLY
as
SET NOCOUNT ON
select XmlRequest = '###Data Returned###'
from #ScreensParam
go
and I try to break the PHP call to sql into 2 parts (not sure if this is correct, but gave it a try):
$add='declare #ScreensParam [ScreensTableType] insert #ScreensParam values (5101.00,2011,0,100,1),(5103.00,2011,0,100,1)';
$select='exec testUDTT #ScreensParam='.$screensParam;
sqlsrv_query($conn, $add);
$sqlResponse=sqlsrv_query($conn, $select);
I get the following error: SQLSTATE: 42000 code: 137 message: [Microsoft][SQL Server Native Client 11.0][SQL Server]Must declare the scalar variable "#ScreensParam". as I guess the UDTT is not in scope.
Also I wonder what the PHP syntax should be for passing #ScreensParam in the array? Would $select become exec testUDTT #ScreensParam=? where "?" = '#ScreensParam'. Don't know?
So I've found a solution creating XML instead of using UDTT which is acceptable. And I've spent more than enough time trying to get it to work. I have a sneaking suspicion that what I want to do can't be done with PHP currently.
If anyone has/finds a solution though, I'd love to know.
Thanks for the help Fuzzy Button. It was fun trying.
OK after switching over to an XML solution, I was searching out how to do something else in PHP and serendipitously stumbled upon a question about using SQL temp tables with PHP and the response utilized the sqlsrv_next_result() function. So this now works beautifully:
$sqlStr=
<<<SQL
declare #ScreensParam [ScreensTableType]
insert #ScreensParam values (5101.00,2011,-1000,1000,1),(5103.00,2011,0,100,1)
exec xml_testIndustry '1,2', 2009, 2011, #ScreensParam
SQL;
$getFormData=sqlsrv_query($conn, $sqlStr);
echo "Rows affected: " . sqlsrv_rows_affected($getFormData).'<br/>';
$next_result = sqlsrv_next_result($getFormData);
if( $next_result ) {
while( $row = sqlsrv_fetch_array( $getFormData, SQLSRV_FETCH_ASSOC)){
echo $row['XmlRequest'].": <br />";
}
} elseif( is_null($next_result)) {
echo "No more results.<br />";
} else {
die(print_r(sqlsrv_errors(), true));
}
I've been thinking this over.
First, I played with
$sqlStr="DECLARE #test varchar(max) SET #test='123' SELECT * from table";
and can confirm that multiple statements can indeed run OK :)
Second...
EDIT
Playing some more in http://sqlfiddle.com/#!3/b1596/73 it appears you can pass a table of values into a SQL command as a defined table type and execute an SP with the temporary table as a param... (it took a lot of experimenting but I eventually found that sqlfiddle is very fussy about semi-colons and sometimes doesn't treat comments as comments!)
create type ScreensTableType1 as table
(ElementCode decimal(6,2), ElementYear int, MinimumValue float,
MaximumValue float, AndOr bit);
An SP to demo a table parameter, that outputs what's passed in :
CREATE PROCEDURE testSP
( #ParamTab ScreensTableType1 Readonly )
AS
begin
SELECT * FROM #ParamTab
end;
Should be able to load multiple rows of data into the SP, in PHP...
One way : with a CTE :
DECLARE #S1 as ScreensTableType1
With data_in as
(
SELECT
5101.00 as ElementCode, 2011 as ElementYear,
0 as MinimumValue, 100 as MaximumValue, 1 as AndOr
UNION ALL
SELECT 5103.00,2011,0,100,1
)
INSERT #S1 SELECT * from data_in
Exec testSP #S1
.. or multiple INSERTs :
DECLARE #S2 as ScreensTableType1
insert #S2 values (5199.00,2011,0,100,1)
insert #S2 values (5177.00,2011,0,100,1)
EXEC testSP #S2