Problems using MERGE INTO statements in php for a MySQL database - php

I'm trying to use a MERGE INTO statement in my php file to update or insert into a MySQL database for a multiplayer game.
Here's a full description of what I'm trying to accomplish:
The php file is called with the following line from a javascript file:
xmlhttp.open('GET', "phpsqlajax_genxml.php?" + "lat=" + lla[0] + "&heading=" + truckHeading + "&lng=" + lla[1] + "&velocity0=" + vel0 + "&velocity1=" + vel1 + "&velocity2=" + vel2 + "&id=" + playerNumber, true);
This will be sending the php file information to update the database with. Either this will be a new player and the first time this information has been sent, meaning that a new row in the database will be created, or it will be a current player who just needs to have their information updated.
If it is a new player the "id" that is sent will be one that doesn't yet exist in the database.
For some reason the database isn't being updated, nor are new rows being added. I'm thinking it's a syntax error because I don't have much experience using MERGE statements. Could someone with experience with this please let me know what I might be doing wrong?
Here is the code before the MERGE INTO statement so you can understand which variables are which:
$id = $_GET['id'];
$lat = $_GET['lat'];
$lng = $_GET['lng'];
$heading = $_GET['heading'];
$velocity0 = $_GET['velocity0'];
$velocity1 = $_GET['velocity1'];
$velocity2 = $_GET['velocity2'];
id is the column heading, $id is the id being passed in
Here is my current MERGE INTO statement in my php file:
MERGE INTO markers USING id ON (id = $id)
WHEN MATCHED THEN
UPDATE SET lat = $lat, lng = $lng, heading = $heading, velocityX = $velocity0, velocityY = $velocity1, velocityZ = $velocity2
WHEN NOT MATCHED THEN
INSERT (id, name, address, lat, lng, type, heading, velocityX, velocityY, velocityZ) VALUES ($id, 'bob', 'Poop Lane', $lat, $lng, 'Poop', $heading, $velocity0, $velocity1, $velocity2)

PHP's database libraries invariably have their various function calls return FALSE if anything failed during the call. Assuming you're on mysql_/mysqli_, then you shoudl be doing something like this:
$sql = "MERGE INTO ....";
$result = mysql_query($sql);
if ($result === FALSE) {
die(mysql_error());
}
It is poor practice to NOT check the return values from database calls. Even if the query string is 100% syntactically valid, there's far too many ways for a query to fail. Assuming everything works is the easiest way to get yourself into a very bad situation. As well, when things do fail, the lack of error handling will simply hide the actual reason for the error and then you end up on SO getting answers like this.
Oh, and before I forget... MySQL doesn't support "MERGE INTO...", so your whole query is a syntax error. Look into using "REPLACE INTO..." or "INSERT ... ON DUPLICATE KEY UPDATE ..." instead.

Related

PHP PDO Not Finding OUTPUT Parameter From MSSQL Stored Procedure Call

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...

How do I store the result of a TOTAL in MySQL so that it can be immediately inserted back into the table

I want to enter data into a table. Using the data that was just entered, I would like to add up the values of 3 columns and then insert that number back into the table into a fourth column.
My initial code:
<?php
$post = file_get_contents('php://input');
$updatedJSONdata = json_decode($post, true);
$numOfStarsLevelOne = $updatedJSONdata["numOfStarsLevelOne"];
$numOfStarsLevelTwo = $updatedJSONdata["numOfStarsLevelTwo"];
$numOfStarsLevelThree = $updatedJSONdata["numOfStarsLevelThree"];
$db = new PDO('mysql:host=localhost;dbname=gametable', 'root', '');
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
$sql = "INSERT INTO gameData(
numOfStarsLevelOne,
numOfStarsLevelTwo,
numOfStarsLevelThree)
VALUES (
'$numOfStarsLevelOne',
'$numOfStarsLevelTwo',
'$numOfStarsLevelThree')";
$testInsert = $db->prepare($sql);
$testInsert->execute();
My guess is I need to make a second query such as
$sqlTwo = "SELECT (numOfStarsLevelOne
+ numOfStarsLevelTwo
+ numOfStarsLevelThree) as total from gameData";
$secondInsert = $db->prepare($sqlTwo);
$secondInsert->execute();
But I'm not sure how I would be able to store the result of the second query so that it can be inserted back into gameData.
I would like to eventually be able to do another query such as
$sqlThree = "INSERT into gamedata(totalNumOfStars) values("result of second query stored as a variable")";
Have you considered this?
INSERT INTO gameData(numOfStarsLevelOne, numOfStarsLevelTwo, numOfStarsLevelThree)
VALUES ($numOfStarsLevelOne, $numOfStarsLevelTwo, $numOfStarsLevelThree,
$numOfStarsLevelOne + $numOfStarsLevelTwo + $numOfStarsLevelThree
);
Note: I removed the single quotes, because these are presumably numbers. However, you should be using parameters, not munging query strings.
If you want the value to always be up-to-date, then use a trigger. Or, create a view:
create view v_gamedata as
select gd.*,
(numOfStarsLevelOne + $numOfStarsLevelTwo + $numOfStarsLevelThree) as total
from gamedata;
Or, in the most recent versions of MySQL, you can create a calculated column.
'i think this is what you're looking for:
$total1 = $numOfStarsLevelOne + $numOfStarsLevelTwo + $numOfStarsLevelThree;
$sql = "INSERT INTO gameData(
numOfStarsLevelOne,
numOfStarsLevelTwo,
numOfStarsLevelThree,
totalNumOfStars)
VALUES (
'$numOfStarsLevelOne',
'$numOfStarsLevelTwo',
'$numOfStarsLevelThree',
'$total1')";

PHP using SQL Server CLR Stored Procedure how to consume return value

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;

SQL + PHP Update query

I've been trying to update my data according to the user session (UserLogin) but it kept saying: Data type mismatch in criteria expression. The print_r is just for testing purposes.
Thanks in advance,
Z
function Employee1_BeforeShow(& $sender)
{
$Employee1_BeforeShow = true;
$Component = & $sender;
$Container = & CCGetParentContainer($sender);
global $Employee1; //Compatibility
$Page = CCGetParentPage($sender);
$db = $Page->Connections["PettyCashMDB"];
$sql1 = "UPDATE Employee SET Employee.LastActive = Date() WHERE Employee.[EmpID] = ". $_SESSION['UserLogin'];
$db->query($sql1);
print_r($_SESSION['UserLogin']);
$db->close();
Employee1_BeforeShow #67-67106FAD
return $Employee1_BeforeShow;
}
EDIT: I've tried #NanaPartykar 's method and by accident I've noticed that it does get the value from $_SESSION['UserLogin'], just that somehow the datatype is different.
EDIT: It displays the error Data type mismatch but both of them are string and returns string.
Instead of Employee.[EmpID], use Employee.EmpID
You need some quotes:
$sql1 = "UPDATE Employee SET Employee.LastActive = Date() WHERE Employee.[EmpID] = \'". $_SESSION['UserLogin'] . "\'";
Z - There are a bunch of built-in Codecharge functions to assist with getting values from querystring, sessions and controls.
eg: CCGetSession("UserLogin", "default");
http://docs.codecharge.com/studio50/html/index.html?http://docs.codecharge.com/studio50/html/Components/Functions/PHP/Overview.html
and executing SQL with some validating (from 'Execute Custom SQL' help topic):
$db = new clsDBConnection1();
$SQL = "INSERT INTO report (report_task_id,report_creator) ".
"VALUES (". $db->ToSQL(CCGetFromGet("task_id",0),ccsInteger) .",". $db->ToSQL(CCGetUserID(),ccsInteger) .")";
$db->query($SQL);
$db->close();
The $db->ToSQL (and CCToSQL) functions convert and add quotes for relevant data types (ccsText, ccsDate).
There are many examples in the Manual under 'Examples' and 'Programming Reference' for PHP (and ASP, .NET, etc)
http://support.codecharge.com/tutorials.asp
I strongly suggest looking at some of the examples, as Codecharge will handle a lot of the 'plumbing' and adding a lot of custom code will causing problems with the generation of code. In your example, you should add a 'Custom Code' action to the Record's 'Before Show' Event and add your code there. If you add code just anywhere, the entire section of code (eg: Before Show) will change colour and no longer be updated if you change something.
For example, if you manually edited the 'Update' function to change a default value, then no changes through the IDE/Properties will change the 'Update' function (such as adding a new field to the Record).
Finally got it to work, this is the code $sql1 = "UPDATE Employee SET LastActive = Date() WHERE EmpID = '$_SESSION[UserLogin]' "; Thanks to everyone that helped out.

Php selecting specific columns using WHERE

I am aware this is somehow a noobish question, and I already knkow one option of solving it, but still. I have a database and a search form. What I am trying to accomplish is filter rows specific to what user sends me VIA post. I know how to get POST to my php variables, what I am trying to accomplish now is use variable as criteria only if it is not null. So something in the lines of>
SELECT * FROM db
WHERE COL1=ifnotnull($variable from post form)
AND COL# = ifnotnull($anothervariable)
.
.
.
So I want to include in my search only fileds from form which were entered (i.e. are not null)
I am aware this could be done via preprocessing all variables and checking if they are null and then not using them in SQL query, but is there a shorter way doing this on the fly with SQL?
I hope this I explained it thoroughly,
thanks.
I consider preprocessing all variables as the most preferable approach. But here is tricky solution just for your case. (with mysql user defined variables):
...
$dbh = new PDO(...); // a PDO instance representing a connection to a database
$var1 = 0; // sanitized variable from POST array
$var2 = "pen"; // sanitized variable from POST array
$dbh->exec("SET #var1 := $var1; ");
$dbh->exec("SET #var2 := '$var2'; ");
// replace 'id' and 'name' with your column names
$stmt = $dbh->prepare("SELECT * FROM table_name WHERE (id = #var1 OR #var1 = '') AND (name = #var2 OR #var2 = '') ");
$stmt->execute();
....
Such approach takes into account all passed variables which are not empty

Categories