I have a simple table
Via $Poles OR $Power (or combination of both) I am selecting a list of rows
if($_GET["Power"]!="*" and $_GET["Poles"]!="*") $query = "SELECT * FROM `TABLE 2` WHERE Power=".$_GET["Power"]." AND Poles=".$_GET["Poles"]."";
elseif($_GET["Poles"]!="*") $query.= "SELECT * FROM `TABLE 2` WHERE Poles=".$_GET["Poles"]."";
elseif($_GET["Power"]!="*") $query.= "SELECT * FROM `TABLE 2` WHERE Power=".$_GET["Power"]."";
else $query= "SELECT * FROM `TABLE 2` ORDER BY Power";
I am absolute beginner but I feel that this is a wrong way to do it.
Please advise me, how should it be done?
If Power is set and Poles is * - I want all rows with this power.
If Poles is set and Power is * - I want all rows with this poles.
If both are set to any value - I want to see this exact row;
If both are set to * - I want all rows from the table
With just the information provided I think your logic generally accomplishes what you're going for. You have several scenarios that to me are distinctly different queries, so I would handle the logic for building the queries in PHP.
I'm not sure if you are using any kind of framework or not, but I'll assume you are not, though I would suggest that you do if it's practical to do so. A good framework will provide input scrubbing and database management to make life easier, and also more security options.
For security purposes, since you are constructing a query with input directly from the user, I would suggest you use the PDO class for interacting with your database (if you aren't). The PDO class will allow you to bind the user's input to the query, and then it will escape the input automatically for you. This greatly reduces your risk of SQL injection attacks (users doing malicious things to your database using what they submit to you) and is considered standard practice in PHP now.
More information on the PDO class can be found here: http://php.net/manual/en/book.pdo.php
More information on SQL injection attacks can be found here: http://www.w3schools.com/sql/sql_injection.asp
So here would be my untested code putting it all together:
$dsn = "localhost"; //Where is your DB located?
$username = "username";
$password = "password";
//Create new DB connection (http://php.net/manual/en/pdo.construct.php)
$dbh = new PDO($dsn, $username, $password);
//Craft and execute the appropriate query
//Each query will include placeholders for data (like :power)
//Placeholders will be replaced with the data passed to execute()
//The PDO class will escape the user input to prevent SQL injection
if ($_GET["Power"] != "*" && $_GET["Poles"] != "*") {
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Power` = :power AND `Poles` = :poles;");
$stmt->execute([
':power' => $_GET["Power"],
':poles' => $_GET["Poles"]
]);
} elseif ($_GET["Power"] != "*") {
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Power` = :power");
$stmt->execute([':power' => $_GET["Power"]]);
} elseif ($_GET["Poles"]) {
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Poles` = :poles;");
$stmt->execute([':power' => $_GET["Poles"]]);
} else {
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` ORDER BY `Power` ASC");
$stmt->execute();
}
if ($stmt->rowCount()) {
//The query returned results, which we can fetch in a variety of ways
//http://php.net/manual/en/pdostatement.fetch.php
//http://php.net/manual/en/pdostatement.fetchall.php
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
} else {
//No results, handle appropriately
}
If that if/elseif/else block looks gnarly, you can actually accomplish the same thing with a switch statement. Pass true as the value to match, and make your case statements the logic that defines each of your cases. The first case that matches will run, while default will run if no cases match.
Here's what that looks like:
switch (true) {
case $_GET["Power"] != "*" && $_GET["Poles"] != "*":
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Power` = :power AND `Poles` = :poles;");
$stmt->execute([
':power' => $_GET["Power"],
':poles' => $_GET["Poles"]
]);
break;
case $_GET["Power"] != "*":
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Power` = :power");
$stmt->execute([':power' => $_GET["Power"]]);
break;
case $_GET["Poles"] != "*":
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Poles` = :poles;");
$stmt->execute([':power' => $_GET["Poles"]]);
break;
default:
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` ORDER BY `Power` ASC");
$stmt->execute();
break;
Edit 1:
For what it's worth, I tend to avoid exposing any of my underlying technologies as much as possible, so using default SQL keywords/operators that a user can see and potentially manipulate is problematic (to me). As a result, I don't use * or % as values that a user can pass to me. Instead I go with some other placeholder I can use in my logic, like an empty string, 0, or -1, depending on what the field accepts.
The use of PDO and bound parameters negates the risk of users doing malicious things with their input, so just doing that alone takes away a lot of the risk of using * or %. In that case it just comes down to whether or not you want the user to be able to guess your underlying storage engine.
From a performance standpoint, I believe the equality operator (=) will be faster than the LIKE operator in a WHERE clause, but you would likely only notice the performance hit in a large production environment, so no real worries there.
$query = "SELECT * FROM `TABLE 2` WHERE Power LIKE ".$_GET["Power"]." AND Poles LIKE ".$_GET["Poles"]."";
Using this query will be able to get you the result you want. Notice that I have change the = operator to LIKE.
Then in your userform or the way you pass values to $_GET you need to change * to %. % is MySQL's wilcard symbol.
Additionally
you are using input from the user in database queries and as such you should ALWAYS use a prepared statement to avoid SQL injection.
Please refer to This SO Q&A which two very easy to follow samples of how to perform a proper prepared statement
Related
This is a request for confirmation/clarification based on a very easily missed comment from 6 years ago in the PHP.net manual for PDO::prepare, which I haven't seen discussed elsewhere (even in the great phpdelusions blog). It's such a powerful possibility if it's true that I feel it deserves a little wider coverage and search highlighting for others (or needs debunking if not).
Here's the comment (by Hayley Watson):
It is possible to prepare in advance several statements against a single connection.
As long as that connection remains open the statements can be executed and fetched from
as often as you like in any order; their "prepare-execute-fetch" steps can be interleaved
in whichever way is best.
So if you're likely to be using several statements often (perhaps within a loop of
transactions), you may like to consider preparing all the statements you'll be using up front.
I have some code that must run like (pseudocode):
foreach (fetchAll row with PDO) {
process row results
if (condition)
update table with processed results
else
delete row no longer needed
}
According to that comment, I could create TWO prepared statements BEFORE the loop, one for the update query and one for the delete query, then execute (only) within the loop. As long as the handles are different and preserved, the connection should cache both, I can use them interchangeably and I'd not have to do any SQL statement parsing INSIDE the loop, which would be very inefficient:
// do statement prepare/execute/fetchAll for main loop, then...
$update_stmt = $PDO->prepare($update_query);
$delete_stmt = $PDO->prepare($delete_query);
foreach (fetchAll row) {
process row results
if (condition)
$update_stmt->execute(some_processed_values);
else
$delete_stmt->execute(some_other_values);
}
Since most questions here only discuss using one prepared statement at a time, and this has excellent implications for code efficiency if widely applied, would anyone like to confirm that this is definitely the case (at least from PHP7)? If so, I guess other neat applications for this form of code could be shared in the solutions.
There's no problem with using multiple prepared statements at the same time and executing them out of order.
You can run code such as the following with intertwined statements and it will work.
$stmt1 = $pdo->prepare('INSERT INTO addplate(Plate) VALUES(?)');
$stmt2 = $pdo->prepare('UPDATE addplate SET Plate=? WHERE Plate=?');
$stmt1->execute(['val1']);
$stmt2->execute(['val2', 'val1']);
$stmt1->execute(['val1']);
$stmt2->execute(['val2', 'val1']);
$stmt1->execute(['val1']);
This can bring you some performance benefit when for some reason you can't avoid N+1 problem. You prepare the inner query once and then execute it inside the loop multiple times.
However, this could be a problem with result-producing queries if you want to run unbuffered query (it's very rarely used). PDO by default executes buffered queries, so you need to switch them off to run into this issue.
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$stmt1 = $pdo->prepare('SELECT * FROM addplate WHERE Plate=?');
$stmt2 = $pdo->prepare('SELECT * FROM addplate WHERE Plate=?');
$stmt1->execute(['val1']);
var_dump($stmt1->fetch());
$stmt2->execute(['val2']); // <-- Error if stmt1 still has more records
var_dump($stmt2->fetch());
It will produce:
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
Nice Question! First statement ?
$id = '2';
$username = 'John';
if ($id === 2){
$array = array(':username'=> $username, ':id'=>$id);
$sql = "UPDATE users SET username = ? WHERE id = ?";
}else{
$array = array(':id'=>$id);
$sql = "DELETE FROM users WHERE id = ?";
}
$stmt = $pdo->prepare($sql);
$stmt->execute($array);
Second statement :
$sql = "UPDATE users SET username = ? WHERE id = ?";
$sql = "DELETE FROM users WHERE id = ?";
$stmt1 = $pdo->prepare($sql);
$stmt2 = $pdo->prepare($sql);
if ($id === 2){
$array = array(':username'=> $username, ':id'=>$id);
$stmt1->execute($array);
}else{
$array = array(':id'=>$id);
$stmt2->execute($array);
}
Simple statements:
$stmt = $pdo->prepare("UPDATE users SET username = ? WHERE id = ?")->execute([':username'=> $username, ':id'=>$id]);
$stmt = null;
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([':id'=>$id]);
$stmt = null;
First statement run 1 second faster than second statement.
Runing separete statements at the bottom, both update and delete at same time much faster than others.
is that because of if statements ?
I am runing php 7.4.0 and mysql 8.0
Updated if someone wants to try.
I have multiple tables in my database named teacher1, teacher2.... I am trying to access them using a variable $id.
I have written down the following code.
$query = "SELECT * FROM table.$id";
How could i access those different tables using a variable.
I'm not clear from the question text whether your $id variable contains the full table name, or some kind of number.
However, in either case you have to make a slight tweak to your $query variable.
If $id contains the full name of the table (i.e. teacher1):
$query = "SELECT * FROM " . $id;
If $id contains a number used to identify which teacher table it is (i.e. 1, 2, 3, etc):
$query = "SELECT * FROM teacher" . $id;
Learn to use parameters!
$stmt = $conn->prepare($query = "SELECT t.* FROM table t where t.id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
Don't munge query strings with parameter values! This introduces SQL injection risk, can introduce syntax errors that are hard to debug, and can degrade performance.
i am doing a mini project of social networking , i am having a doubt about table .
let see , i need to provide post in their page. to do this,should i have to create table for each user or just create one table and use it for multiple users (data can be fetched by selecting particular user name and display it in their page ).
which is the best way?
my php code:
<?php
$query="select * from table_name where user=$username order by time desc;";
?>
To answer your question
It's best to just use 1 table of users and have a separate able for your posts. Your users table should have all the information for each specific users with 1 unique value that is automatically generated by the MySQL database. (Use auto-increment) And in the posts table you should have all the data for each post with a user_id column that holds the unique value from the users table, this way you know who posted it.
Here is a mockup table structure:
Users table:
uid | name | email
Posts table:
uid | user_id | message
user_id in the posts table should always be equal to some uid in the users table.
Every single table should always have some unique value that is assigned its primary value
My real concern
I am very concerned with the security of your application. Prepared statements are WAY easier to use, and WAY more secure.
In the code snippet that you shared:
<?php
$query="select * from table_name where user=$username order by time desc;";
?>
this query is very insecure, as Bobby Tables would tell you. I'm not sure why type of database connection you are using, but I suggest PDO. I wrote a function that makes this very very easy, here is the snippet for that:
This is a file I usually call connection.php that you can import on any page you need to use your database.
<?php
$host = 'localhost';
$db = '';
$user = '';
$pass = '';
$charset = 'utf8';
$dsn = "mysql:host={$host};dbname={$db};charset={$charset}";
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, $user, $pass, $opt);
function pdoQuery($query, $values = []) {
global $pdo;
if(!empty($values)) {
$stmt = $con->prepare($query);
$stmt->execute($values);
} else {
$stmt = $con->query($query);
}
return $stmt;
}
?>
This function allows you to EASILY use prepared statements by just
including the connection.php page and writing queries in a way that is
readable, clean, and secure. As I'm sure a lot of people reading this are not used to Prepared Statements or know how they
work, the rest of this post will explain that.
One of the biggest differences here is that instead of using String
Interpolation in
your query, you will set variables as question marks ?, so your
query looks like this: UPDATE table SET user=? instead of UPDATE
table SET user='$user' - and the variables will be sent later for
safety, so this prevents SQL Injection.
This it the way your query would look now:
pdoQuery("SELECT * FROM `table_name` WHERE user=? ORDER BY `time` DESC", [$username]);
This is basically how it works:
pdoQuery(string $query, array $variables)
If you pass no variables, it automatically uses the query() function, if you do pass variables it automatically binds and executes the statements. No matter what query you do, the function always returns the query as an object, so you can act on it using any method you can normally use on a PDO query after the execute.
If you know how these work, you can stop reading here :) I put some
exmaples below of some of the ways you can manipulate the return data
to do what you need to do.
This function returns the object of the query you requested, so if you wanted to loop through all of the results of your query you use it like this:
$stmt = pdoQuery("SELECT * FROM `table_name` WHERE `user`=? ORDER BY time DESC", [$username])->fetchAll();
foreach($stmt as $row) {
$row['name']."<br>";
}
Or if you just wanted to get a single column from a specific row, you could use it like this:
$username = pdoQuery("SELECT `username` FROM `users_table` WHERE uid=? ORDER BY `time` DESC", [$uid])->fetchColumn();
Which will return the username from user where uid=$uid as a string
or if you wanted several values from 1 specific row, you could do
$user = pdoQuery("SELECT `username`,`name`,`email` FROM `users_table` WHERE uid=? ORDER BY time DESC", [$uid])->fetch();
Which will return to $user as an array that has the username, name, and email of the user.
You can also use this function to INSERT, UPDATE, or basically any type of query you can think of. This is how you insert:
pdoQuery("INSERT INTO `table_name` (`name`,`col2`, `col3`) VALUES (?,?,?)", [$name, $col1, $col2]);
My PDO Class
Since writing this post, I have created a new database wrapper class called GrumpyPDO (Hosted on Github).
This class method returns the object of the query you requested, so if you wanted to loop through all of the results of your query you use it like this:
Fetch All
GrumpyPDO Long Syntax
$stmt = $db->run("SELECT * FROM `table_name` WHERE `user`=? ORDER BY time DESC", [$username])->fetchAll();
GrumpyPDO Short Syntax
$stmt = $db->all("SELECT * FROM `table_name` WHERE `user`=? ORDER BY time DESC", [$username]);
Loop:
foreach($stmt as $row) {
$row['name']."<br>";
}
Single Column Return
Or if you just wanted to get a single column from a specific row, you could use it like this:
//Long Syntax
$username = $db->run("SELECT `username` FROM `users_table` WHERE uid=? ORDER BY `time` DESC", [$uid])->fetchColumn();
//Short
$username = $db->cell("SELECT `username` FROM `users_table` WHERE uid=? ORDER BY `time` DESC", [$uid]);
Which will return the username from user where uid=$uid as a string
Entire Row Return
or if you wanted several values from 1 specific row, you could do
//Long Syntax
$user = $db->run("SELECT `username`,`name`,`email` FROM `users_table` WHERE uid=? ORDER BY time DESC", [$uid])->fetch();
//Short Syntax
$user = $db->row("SELECT `username`,`name`,`email` FROM `users_table` WHERE uid=? ORDER BY time DESC", [$uid]);
Which will return to $user as an array that has the username, name, and email of the user.
DML Queries
You can also use this function to INSERT, UPDATE, or basically any type of query you can think of. This is how you insert (All DML's are similar):
$db->run("INSERT INTO `table_name` (`name`,`col2`, `col3`) VALUES (?,?,?)", [$name, $col1, $col2]);
I've two 3 variable and that used in $sql string
$bikeid = xxxxx
$st_char = column name
$st_tab = table name
I've coded out like this
$sql = "select $st_char
from $st_tab
where bike_id like '$bike_id'";
And like this
$sql = "select ".$st_char."
from dbo.".$st_tab."
where bike_id like ".$bike_id;
To select data from my database,the result is the same,they can get data from database
My question is which one is right and which one is wrong
if none wrong which one is better and why ?
Thanks
Both are bad because they are vulnerable to SQL injection.
There are also potential performance gains from using prepared statements. So at the very least, your query should look like:
select $st_char from $st_tab where bike_id like :bike_id
Unfortunately, you can't use parameters in certain situations, like column and table names. In this case you will need to do manual string concatenation, but whitelist allowed input. For example:
$allowed_cols = array('col1', 'col2', 'col3');
$allowed_tables = array('t1', 't2', 't3');
if(in_array($st_char, $allowed_cols, true) && in_array($st_tab, $allowed_tables, true))
{
$query = "select $st_char from $st_tab where bike_id like :bike_id";
// perform execution here
}
else
{
// invalid or malicious input
}
You may also want to wrap the table/column names in square brackets ([]) to avoid conflicts with any reserved keywords:
$query = "select [$st_char] from [dbo].[$st_tab] where bike_id like :bike_id";
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.