Let's say the query looks like this:
$query = 'select * from some_table LIMIT :limit'
My db->selects are the following:
a) $orders = $db->select($db->raw($query), array("limit" => '0,10'));
b) $orders = $db->select($db->raw($query), array("limit" => '10'));
a) doesn't work, but b) does. Why?
Also this doesn't work:
$query2 = 'select :col from some_table LIMIT :limit';
$orders = $db->select($db->raw($query2), array("col" => "some_col","limit" => '10'));
Am I using it the wrong way?
You need to realize that prepared statements are not just formatted strings. The idea of prepared statements is that syntax and arguments are sent separately, so you can safely send user data without risking mysql injection. In query a) you are putting syntax in the parameter. The same can be said about the columns. Column names are part of the syntax.
Related
Sorry my english. I'm trying to search mysql database that contains hashtags, but it returns all.
eg Search #NBA returns: #NBA, #NBA2021, #NBAscoreBoard, etc
I've tried every preg_replace on here. eg #(^|\s)#(\w*[a-zA-Z_]+\w*)# But how do I break after the specific search is met?
$_GET["tag"]='#nba'; // $_GET is from a query string
$fulltag = preg_replace("/[^a-zA-Z0-9_]/", '', $_GET["tag"]); //trying to isolate this problem
$sql='SELECT * FROM status WHERE data LIKE "%#'.$fulltag.'%" LIMIT 12';
//
//
//
echo //the result containing the "# $fulltag";
As I said in my comment--just doing two queries with one to test if data = fullTag, and then if that returns nothing, then doing the wildcard search--is probably going to be simpler.
However, if you really want this to be a single query, you could do something like this, wherein you test to see if it is an exact match within a sub-query, then order by whether it's an exact match so that if there is an exact match, it will be the first result.
SELECT *
FROM (
SELECT
data,
CASE
WHEN data = "#NBA"
THEN 1
ELSE 0
END AS is_exact
FROM status
WHERE data LIKE "%#NBA%"
LIMIT 12
) AS matches
ORDER BY is_exact DESC
A separate note: You code right now is very vulnerable to SQL Injection. You should try using parameterized prepared statements instead of manually building your queries. See PDO or MySQLi prepared statements.
Here is an example of the above query using PDO, and this method of using CONCAT to safely add wildcards to your query:
$_GET["tag"]='#nba'; // $_GET is from a query string
$fulltag = preg_replace("/[^a-zA-Z0-9_]/", '', $_GET["tag"]); //trying to isolate this problem
$pdo = new PDO(/* your connection details */);
$sql =
'SELECT *
FROM (
SELECT
data,
CASE
WHEN data = CONCAT("#", :tag1)
THEN 1
ELSE 0
END AS is_exact
FROM status
WHERE data LIKE CONCAT("%#", :tag2, "%")
LIMIT 12
) AS matches
ORDER BY is_exact DESC
';
$stmt = $pdo->prepare($sql);
$stmt->execute([
':tag1' => $fulltag,
':tag2' => $fulltag,
]);
Here's your simpler query using the same, safer approach:
$_GET["tag"]='#nba'; // $_GET is from a query string
$fulltag = preg_replace("/[^a-zA-Z0-9_]/", '', $_GET["tag"]); //trying to isolate this problem
$pdo = new PDO(/* your connection details */);
$sql = 'SELECT * FROM status WHERE data LIKE CONCAT("%#", :fullTag, "%") LIMIT 12';
$stmt = $pdo->prepare($sql);
$stmt->execute([':fullTag' => $fulltag]);
I am using Laravel 5.
I need to save some sql statements in a field of a table of the database (these statements are used to get some results).
I want to get these statements in my controller and execute them using Laravel, but the statements are only strings,
Let's suppose the following
$statement = Table::where('ID', 1);
$statement = $statement->STATEMENT;
In $statement I have a string like this
$statement = 'SELECT SUM(VAL) FROM TABLE';
What I need to know is how to execute in the database the statement saved in my string var $statement
I finally want to have something like
$result = 10 (the result of executing 'SELECT SUM(VAL) FROM TABLE', which was in $statement)
Thanks!
This is called raw queries in laravel. For example:
DB::select(DB::raw('select * from users''));
So in your case(if You've already got $statement):
DB::select(DB::raw($statement));
After years of reading it's time to ask first question :)
My problem is that after migrating the code from mySQLi to PDO we have got a problem as it seems PDO adds the apostrophes to the query.
PHP code goes like that:
$sort = $_GET['sort']; << table column name (mySQL VARCHAR only columns)
....
$query = 'SELECT * FROM table WHERE xxx > 0';
$query .= ' ORDER BY :sort ASC ;';
$qry_result= $db->prepare($query);
$qry_result->execute(array(':sort'=>$sort));
mysqli version went smoothly but now queries (mysql log file) looks like this:
SELECT * FROM table where xxx > 0 ORDER BY 'SORT_VAR_VALUE' ASC;
^ 2 problems ^
So the table is NOT sorted, as sort order (from mySQL point of view) is wrong.
phpinfo() does not get any results for search on "magic" nor "quotes" btw.
Any idea ??
The placeholders in PDO statements are for values only. If you want to add actual SQL to the query you need to do it another way.
First, you should sanitize $sort and surround it with backticks in the query.
$sort = preg_replace('/^[a-zA-Z0-9_]/', '', $sort);
Then you could double quote the query string and PHP will replace $sort with it's value for you:
$query = "SELECT * FROM table WHERE xxx > 0 ORDER BY `$sort` ASC";
Or you could replace it with preg_replace like so:
$query = 'SELECT * FROM table WHERE xxx > 0 ORDER BY `:sort` ASC';
$query = preg_replace('/:sort/', $sort, $query, 1);
I would use the preg_replace method because it allows you to reuse the query if you assign the results from preg_replace to another variable instead of overwriting the original variable.
by default pdo binds values as strings.
To fix this you will want to check that the column is actually a valid name and then add it to the query, you can do it the following way:
function validName($string){
return !preg_match("/[^a-zA-Z0-9\$_\.]/i", $string);
}
if(validName($sort)){
$db->prepare("SELECT * FROM table where xxx > 0 ORDER BY $sort ASC");
}
With PDO it's not possible to bind other things that variables in the WHERE statement. So you have to hard code the names of the columns you order by.
See How do I set ORDER BY params using prepared PDO statement?
or Can PHP PDO Statements accept the table or column name as parameter? for further explanations.
Is it possible with PHP PDO to use named placeholders that effectively match everything?
Take as example: $sql = "SELECT * FROM users WHERE targetGroup = :targetGroup AND userId = :userId"
And take $statement = $this->dbh->prepare($sql);
Can I then somehow bind values such that I get the following behaviours:
Original query (this can be done obviously).
Parameters: :targetGroup = 1, :userId = 5.
Resulting query: $sql = "SELECT * FROM users WHERE targetGroup = 1 AND userId = 5
A query to show all users
Parameters: Somehow set :targetGroup and :userId to some special value ALL.
Resulting query: $sql = "SELECT * FROM users
I would like to use this such that I can define queries once somewhere in my program, as static strings, and then reuse them to my likings.
You can't do that. You'll have to prepare different statements for different queries.
$findOneStmt = $pdo->prepare("SELECT * FROM user WHERE target_group = :tg AND user_id = :uid");
$findAllStmt = $pdo->prepare("SELECT * FROM users");
Later, when you'll need one of this queries just call:
$findOneStmt->execute(array(':tg' => 123, ':uid' => 321));
$user = $findOneStmt->fetch();
// ...
$findAllStmt->execute();
$users = $findAllStmt->fetchAll();
You could put a conditional in your SQL that lets you show everything. For example:
SELECT *
FROM users
WHERE
1 = :showAll OR (
targetGroup = :targetGroup
AND userId = :userId
)
Then you can set the :showAll to 0 when you want to filter by :targetGroup and :userId or to 1 when you want to ignore :targetGroup and :userId.
I believe best practice however would be to have two separate queries.
The problem is that you're essentially performing two entirely different queries. They may look very similar, but a SELECT * with no where clause is quite different to one with a where clause.
Rather than define SQL queries in one place and then reuse, abstract the functionality out and use that method call as and when needed, it'll provide you with the ability to have the query defined once, but you will have two queries there now. This way is much safer and much better terms of readability and usage.
I recently heard that soon PHP will be deprecating all of the traditional mysql functions (ex: mysql_query(), mysql_num_rows(), etc...). That being said, I'm trying to convert all of my queries into PDO format. This one particular case is giving me some difficulty,
$team = $_GET['team'];
$ncaa = array("Duke", "Harvard", "Yale", "Stanford");
$limit = idate('z')+14;
$list = join("','", $ncaa);
$query = "SELECT game_id
FROM ncaa_current_season_games
WHERE game_date_int >= :game_date_int
AND (home_team = :team OR away_team = :team)
AND home_team IN(:list)
AND away_team IN(:list)
ORDER BY game_date_int ASC
LIMIT 1";
$stmt = $db->prepare($query);
$stmt->execute(array(':game_date_int' => $limit, ':team' => $team, ':list' => $list));
$num = $stmt->rowCount();
$num is returned as 0. When I had the query in my old format (not PDO), it worked just fine.
Any suggestions?
Thanks,
Lance
You can't use the :list as one placeholder for the in clause, you need one placeholder for one value.
PDO, as any other raw API, is insufficient for the any real-life task.
And prepared statements are insufficient too, as they accept only scalar data, not whatever complex data types or arbitrary query parts.
So, a developer have to help himself with adopting a database abstraction library.
And use this library methods in their application code instead of raw API calls.
With raw PDO you need to create ? mark for the every IN statement value dynamically, using str_repeat() function or something of the kind, add every value to the data array, and then run all that train.
You can find plenty of examples on this site. Some of them are quite smart though.
But with good database access library, you can do it the way you tried (in vain) with raw PDO
$team = $_GET['team'];
$ncaa = array("Duke", "Harvard", "Yale", "Stanford");
$limit = idate('z')+14;
$query = "SELECT game_id
FROM ncaa_current_season_games
WHERE game_date_int >= ?s
AND (home_team = ?s OR away_team = ?s)
AND home_team IN(?a)
AND away_team IN(?a)
ORDER BY game_date_int ASC
LIMIT 1";
$data = $db->getRow($query, $limit, $team, $team, $list, $list);