Searching through JSON data in MySQL in PHP - php

The last time I have posted a question about searching JSON data using PHP. After testing the script I wanted to try something else. Using MySQL to search through the data. Since it is faster than looping everything using a PHP script.
I was writing the script in PhpMyAdmin and that has generated the next PHP script for me. But somewhere there is a bug (sad)
"SELECT *
FROM `bigtree_pages`
WHERE `resources` like \'%\"XNCatDesc\": \"%?%\' and `resources` like \'%\"Brand\": \"%?%\' and `resources` like \'%\"ItemDesc\": \"%?%\'"
I want to give three values. The Categorie, the brand, and the ItemDesc (the name). But this throws an error.
You have an error in your SQL syntax; check the manual that
corresponds to your MariaDB server version for the right syntax to use
near '\'%"XNCatDesc": "%'41'%\' and resources like \'%"Brand":
"%'none'%\' and `reso'
To be honest, I don't really know where I have to put my % sign.
For example. I have this in my JSON "Brand": "Bullet",
The value needs to be Brand (since we are searching on the Brand) and the brand is Bullet. What is the best way to write this query?

To use a parameter inside a LIKE expression in a prepared query, you need to form the entire expression and use that as the parameter. Otherwise you run into issues as you have with the insertion of quotes into your value. If you are using mysqli, try something like this (assuming your connection is called $conn and the values you want to search for are called $categorie, $brand and $itemdesc):
$stmt = $conn->prepare("SELECT *
FROM `bigtree_pages`
WHERE `resources` like ? and `resources` like ? and `resources` like ?");
$search_categorie = "%\"XNCatDesc\": \"%$categorie%\"";
$search_brand = "%\"Brand\": \"%$brand%\"";
$search_itemdesc = "%\"ItemDesc\": \"%$itemdesc%\"";
$stmt->bind_param("sss", $search_categorie, $search_brand, $search_itemdesc);
$stmt->execute();
However the problem you will run into is that because of the % surrounding the search values (e.g. $brand) in the query, when searching for brand = X you could match for example
"Brand": "Y", "Other Value": "contains an X"
So instead you should use regular expressions e.g.
$stmt = $conn->prepare("SELECT *
FROM `bigtree_pages`
WHERE `resources` rlike ? AND `resources` rlike ? AND `resources` rlike ?");
$search_categorie = '"XNCatDesc":[[:space:]]+"[^"]*' . $categorie;
$search_brand = '"Brand":[[:space:]]+"[^"]*' . $brand;
$search_itemdesc = '"ItemDesc":[[:space:]]+"[^"]*' . $itemdesc;
$stmt->bind_param("sss", $search_categorie, $search_brand, $search_itemdesc);
$stmt->execute();
If you are running MySQL 5.7 or later, this is better done using the inbuilt JSON_EXTRACT function:
$stmt = $conn->prepare("SELECT *
FROM `bigtree_pages`
WHERE JSON_EXTRACT(`resources`, '$.XNCatDesc') LIKE ? and
JSON_EXTRACT(`resources`, '$.Brand') LIKE ? and
JSON_EXTRACT(`resources`, '$.ItemDesc') LIKE ?");
$search_categorie = "%$categorie%";
$search_brand = "%$brand%";
$search_itemdesc = "%$itemdesc%";
$stmt->bind_param("sss", $search_categorie, $search_brand, $search_itemdesc);
$stmt->execute();

Related

SQL LIKE literally not showing any results (at all)

I tried to make a search system which uses the LIKE operator to search results based on what the user typed. I'm using it with strings. The problem is that it doesn't show any result.
I hope this also helps people with the same confusion as me...
Code:
"SELECT * FROM table WHERE name LIKE ' . $input . ';";
input is a PHP variable from what the user typed.
EDIT: Don't worry about SQL injection, it's all offline.
for the proper use of like you should use wildchar eg :
SELECT * FROM table WHERE name LIKE concat('%', ? ,'%') ;
and you should not use var inside SQL code .. you are at risk for sqlinjectiomn
for avoid this you should take a look at you db driver for prepared statement and binding param
eg for PDO
$st = $conn->prepare("SELECT * FROM table WHERE name LIKE concat('%', ? ,'%')");
$st->bindParam(1, $input, PDO::PARAM_STR, 255);
$st->execute();
Try This
$string = "input";
$sql = "select * from table where name like '%$string%'"
Create a variable and store value what you want to search
$where = "AND name like '%$string%'";
and put it after table name
$sql = "select * from table_name $where";

PHP PDO Query returning an empty array when using bindParam

Kind of stumped by this one. I have been developing for about a week now, maybe two so it might be a noob mistake, but here is what I have:
<?php
$msDB = new PDO('odbc:Driver={SQL Server Native Client 11.0};Server=SOMESERVER;Trusted_Connection=yes;');
try{
//set values for query
$s="4";
$d1="'2014-10-01 00:00:00'";
$d2="'2014-10-31 23:59:59'";
//create query variable
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (t1.SELECTION = :s)
AND (tN.RESPONSE_DATE BETWEEN :d1 AND :d2))";
//run prepare and bindParam
$tbe = $msDB->prepare($q1);
$tbe->bindParam(':s',$s, PDO::PARAM_INT);
$tbe->bindParam(':d1',$d1, PDO::PARAM_STR);
$tbe->bindParam(':d2',$d2, PDO::PARAM_STR);
//execute query
$tbe->execute();
//fetch resulting data
$res = $tbe->fetchAll(PDO::FETCH_ASSOC);}
//error handling
catch (PDOException $e) {
throw new pdoDbException($e);
}
//print the resulting array
print_r($res);
//set initial count
$cnt=0;
//loop through and increment count
foreach($res as $key=>$value){
foreach($value as $v2 ){
$cnt++;
}
}
//return count value
echo "Total:<br/>".$cnt."<br/>";
?>
I am expecting this to return a result set of the number 3. And when I specify the values in the query manually, everything works as expected and it returns the number 3.
If I however use the bindParam method it returns nothing and throws no errors of any sort. It simply returns an empty array.
I can also break up the query set in $q1 and concatenate the values into it, and it also works flawlessly. I have not really used bindParam before, but as far as I can tell, I am using it correctly.
Works:
//create query variable
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (t1.SELECTION = ".$s.")
AND (tN.RESPONSE_DATE BETWEEN ".$d1." AND ".$d2."))";
When I run the query in MSSQL Server Management Studio, it also returns the result set I expect.
Can anyone tell me what I am doing wrong?
The default data type passed by PDO::bindParam and PDO::bindValue is ALWAYS text. Conversion from datatype text in MSSQL is only possible to CHAR, VARCHAR, NCHAR, and NVARCHAR. It is because of the datatype issue that the value has to be converted from text into CHAR or VARCHAR and then into DATETIME from there. This is however an implicit conversion, and depending on the value passed to the query, may result in rounding errors, truncation, or simply a failed conversion.
This, does NOT work:
//create query variable
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (tN.RESPONSE_DATE BETWEEN :d1 AND :d2))";
//run prepare and bindParam
$tbe = $msDB->prepare($q1);
$tbe->bindParam(':d1',$d1, PDO::PARAM_INT);
$tbe->bindParam(':d2',$d2, PDO::PARAM_INT);
This however, does work:
$q1 = 'SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (tN.RESPONSE_DATE BETWEEN CONVERT(datetime,CONVERT(VARCHAR(MAX),?))
AND CONVERT(datetime,CONVERT(VARCHAR(MAX),?))))';
//run prepare and bindParam
$tbe = $msDB->prepare($q1);
$tbe->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$tbe->bindParam(1,$d1);
$tbe->bindParam(2,$d2);
Even if I casted the bound parameters as type INT, they were still passed to MSSQL as text causing the failure.
It would be my suggestion however to simply use the original workaround of just passing in the variables directly like so and have the variables be double quoted strings:
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (t1.SELECTION = '$s')
AND (tN.RESPONSE_DATE BETWEEN '$date1' AND '$date2'))";
The quoted string is much easier to handle, and far less prone to error because it need not be converted at all and is simply passed right along to MSSQL Server without issue.
I noticed a slight performance hit with the converts because of the extra processing by MSSQL Server. The query took about half a second longer to run with the conversion than without.
Cheers to #meda for helping with the resolution!
I get this error today and take a lot of my hours.
After upgrading PHP Version to PHP7, using sqlsrv:database driver instead of dblib:dbname make this problem occurs.
To avoid this, still using dblib:dbname event PHP7 FPM already support with sqlsrv:database driver.

PHP/MySQL - Updating an SQL table with data from another table

In a worker shift monitoring/accounting system I've built, there is an option to delete Staff Members. This works fine but I would like to archive shifts for staff members who are deleted.
I looked into it and it seemed the best way to do this would be to update my shift table, before the delete, into another table. I looked up how to do this via another Stack Overflow post however I'm getting an error: fatal error call to member function on a non-object.
Based on what I can find, this error is caused when you try to pass a null value, which has left me confused as the value I'm trying to pass is a GET and is working fine when I test it.
$sql = "
UPDATE table_archive
SET table_shift.shift_date = table_archive.shift_date,
table_shift.start_time = table_archive.start_time,
table_shift.end_time = table_archive.end_time,
table_shift.total_hours = table_archive.total_hours,
table_shift.rate_of_pay = table_archive.rate_of_pay,
table_shift.uniqueid = table_archive.uniqueid,
table_shift.addedBy = table_archive.addedBy,
table_shift.paidRate = table_archive.paidRate,
table_shift.totalPaid = table_archive.totalPaid
FROM table_shift, table_archive
WHERE table_shift.uniqueid = ?
";
$stmt = $connection->prepare($sql);
$deleteid = htmlentities($_GET['id']);
$stmt->bind_param('s', $deleteid);
$stmt->execute();
I'm stuck as to why this wont pass, the GET cant be a null value as the test delete I'm using at the moment passes the same variable and works fine. mysqli_query($connection,"DELETE FROM table_staff WHERE uniqueid='$deleteid'")
It may be that I'm using the SQL code wrongly or there is some silly thing I've forgotten but this has me stumped. Failing to fix this code, any other suggestions as to how to achieve the intended function are welcome.
You can't UPDATE FROM. Your syntax is wrong.
Instead, use this:
INSERT INTO table_archive
SELECT * FROM table_shift WHERE table_shift.uniqueid = ?
Is the use of bindParam correct?
If you use a ? it should look like this:
SELECT ...
WHERE column_name = ?
$sth->bindParam(1, $value_in_php, PDO::PARAM_INT);
If it's not ? but :param_name use this:
SELECT ...
WHERE column_name = :param
$sth->bindParam(':param', $value_in_php, PDO::PARAM_INT);
Your error sounds not like an SQL error, but a PHP error.
And if you want to update the table_archive table the SQL doesn't look correct. It should imho be like this:
UPDATE table_archive
SET table_archive.shift_date = table_shift.shift_date
, to_table_that_should_be_updated = from_table_with_value_to_update
FROM table_shift
WHERE table_shift.uniqueid = ?

Passing an operator as a parameter to odbc_execute()

I am taking my first tentative steps into prepared statements (and falling flat on my face).
Previously, I built the following from $_GET and echoed it back - the code was working fine and it returned what I expected from my simple test database.
SELECT * FROM edit_box WHERE (tag="9") AND (text="mango") ORDER BY time_stamp DESC
and when I try to code it using a prepared statement, even if I don't use $_GET but just hard-code the values from the previous, my code looks like this
$odbc_query = OdbcPrepare('SELECT * FROM edit_box WHERE (tag="?")' .
' AND (text ? "?") ORDER BY time_stamp DESC');
$odbcResult = odbc_exec($odbc_query, array('9', '=', 'mango'));
var_dump($odbcResult);
I get NULL.
Obviously a beginner mistake, but I stare at it and still don't say d'oh!
What am I doing wrong?
You cannot do this --
AND (text ? "?")
Parameters, like this, can usually only be passed for actual values - and in some cases identifiers...
To do what you want you need to interpolate the '=' inline into the SQL statement...
Kind of, like this --
$logical_operator = '=';
$sql = SELECT * FROM edit_box WHERE (tag=\"?\") AND (text $logical_operator \"?\") ORDER BY time_stamp DESC');
$odbc_query = OdbcPrepare($sql);
$odbcResult = odbc_exec($odbc_query, array('9', 'mango'));

How to do a SELECT LIKE with PDO Prepare Statement - are value objects of any use here?

The point is to make a query that will grab values introduced by the user on a input box, and retrieve the database records found trough that keyword comparison.
On a innodb engine, so no MATCH AGAINST available correct ?
I will use LIKE on a indexed column table, hope it's ok.
traditionally we will do:
SELECT our_column FROM our_db_table WHERE our_column LIKE '%$queryString%';
So if our query string is AB we will retrieve both: "lab" and "abnormal" precise?
1)
How can we achieve this but, by using PDO ?
Thinking:
Something like,
$stmt = $this->_dbh->prepare("SELECT d.our_column FROM our_db_table d WHERE d.our_column LIKE ?");
But what's next?
Normally I would do:
$stmt->bindParam(1, $ourTableVo->getOurColumn(), PDO::PARAM_STR, 255);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_OBJ);
2)
Could a VO be of any use on this case?
Thanks a lot in advance!
$stmt->bindValue(1, '%' . $ourTableVo->getOurColumn() . '%', PDO::PARAM_STRING);
Wouldn't this work?

Categories