How do you embedded your sql queries in php scripts (coding-style)? - php

how do you embed your sql scripts in php? Do you just write them in a string or a heredoc or do you outsource them to a sql file? Are there any best practices when to outsource them ? Is there an elegant way to organize this?

Use a framework with an ORM (Object-Relational Mapping) layer. That way you don't have to put straight SQL anywhere. Embedded SQL sucks for readability, maintenance and everything.

Always remember to escape input. Don't do it manually, use prepared statements. Here is an example method from my reporting class.
public function getTasksReport($rmId, $stage, $mmcName) {
$rmCondition = $rmId ? 'mud.manager_id = :rmId' : 'TRUE';
$stageCondition = $stage ? 't.stage_id = :stageId' : 'TRUE';
$mmcCondition = $mmcName ? 'mmcs.username = :mmcName' : 'TRUE';
$sql = "
SELECT
mmcs.id AS mmc_id,
mmcs.username AS mmcname,
mud.band_name AS mmc_name,
t.id AS task_id,
t.name AS task,
t.stage_id AS stage,
t.role_id,
tl.id AS task_log_id,
mr.role,
u.id AS user_id,
u.username AS username,
COALESCE(cud.full_name, bud.band_name) AS user_name,
DATE_FORMAT(tl.completed_on, '%d-%m-%Y %T') AS completed_on,
tl.url AS url,
mud.manager_id AS rm_id
FROM users AS mmcs
INNER JOIN banduserdetails AS mud ON mud.user_id = mmcs.id
LEFT JOIN tasks AS t ON 1
LEFT JOIN task_log AS tl ON tl.task_id = t.id AND tl.mmc_id = mmcs.id
LEFT JOIN mmc_roles AS mr ON mr.id = t.role_id
LEFT JOIN users AS u ON u.id = tl.user_id
LEFT JOIN communityuserdetails AS cud ON cud.user_id = u.id
LEFT JOIN banduserdetails AS bud ON bud.user_id = u.id
WHERE mmcs.user_type = 'mmc'
AND $rmCondition
AND $stageCondition
AND $mmcCondition
ORDER BY mmcs.id, t.stage_id, t.role_id, t.task_order
";
$pdo = new PDO(.....);
$stmt = $pdo->prepare($sql);
$rmId and $stmt->bindValue('rmId', $rmId); // (1)
$stage and $stmt->bindValue('stageId', $stage); // (2)
$mmcName and $stmt->bindValue('mmcName', $mmcName); // (3)
$stmt->execute();
return $stmt->fetchAll();
}
In lines marked (1), (2), and (3) you will see a way for conditional binding.
For simple queries I use ORM framework to reduce the need for building SQL manually.

It depends on a query size and difficulty.
I personally like heredocs. But I don't use it for a simple queries.
That is not important. The main thing is "Never forget to escape values"

You should always really really ALWAYS use prepare statements with place holders for your variables.
Its slightly more code, but it runs more efficiently on most DBs and protects you against SQL injection attacks.

I prefer as such:
$sql = "SELECT tbl1.col1, tbl1.col2, tbl2.col1, tbl2.col2"
. " FROM Table1 tbl1"
. " INNER JOIN Table2 tbl2 ON tbl1.id = tbl2.other_id"
. " WHERE tbl2.id = ?"
. " ORDER BY tbl2.col1, tbl2.col2"
. " LIMIT 10, 0";
It might take PHP a tiny bit longer to concatenate all the strings but I think it looks a lot nicer and is easier to edit.
Certainly for extremely long and specialized queries it would make sense to read a .sql file or use a stored procedure. Depending on your framework this could be as simple as:
$sql = (string) View::factory('sql/myfile');
(giving you the option to assign variables in the view/template if necessary). Without help from a templating engine or framework, you'd use:
$sql = file_get_contents("myfile.sql");
Hope this helps.

I normally write them as function argument:
db_exec ("SELECT ...");
Except cases when sql gonna be very large, I pass it as variable:
$SQL = "SELECT ...";
$result = db_exec ($SQL);
(I use wrapper-functions or objects for database operations)

$sql = sprintf("SELECT * FROM users WHERE id = %d", mysql_real_escape_string($_GET["id"]));
Safe from MySQL injection

You could use an ORM or an sql string builder, but some complex queries necessitate writing sql. When writing sql, as Michał Słaby illustrates, use query bindings. Query bindings prevent sql injection and maintain readability. As for where to put your queries: use model classes.

Once you get to a certain level, you realise that 99% of the SQL you write could be automated. If you write so much queries that you think of a properties file, you're probably doing something that could be simpler:
Most of the stuff we programmers do is CRUD: Create Read Update Delete
As a tool for myself, I built Pork.dbObject. Object Relation Mapper + Active Record in 2 simple classes (Database Abstraction + dbObject class)
A couple of examples from my site:
Create a weblog:
$weblog = new Weblog(); // create an empty object to work with.
$weblog->Author = 'SchizoDuckie'; // mapped internally to strAuthor.
$weblog->Title = 'A test weblog';
$weblog->Story = 'This is a test weblog!';
$weblog->Posted = date("Y-m-d H:i:s");
$weblog->Save(); // Checks for any changed values and inserts or updates into DB.
echo ($weblog->ID) // outputs: 1
And one reply to it:
$reply = new Reply();
$reply->Author = 'Some random guy';
$reply->Reply = 'w000t';
$reply->Posted = date("Y-m-d H:i:s");
$reply->IP = '127.0.0.1';
$reply->Connect($weblog); // auto-saves $reply and connects it to $weblog->ID
And, fetch and display the weblog + all replies:
$weblog = new Weblog(1); //Fetches the row with primary key 1 from table weblogs and hooks it's values into $weblog;
echo("<h1>{$weblog->Title}</h1>
<h3>Posted by {$weblog->Author} # {$weblog->Posted}</h3>
<div class='weblogpost'>{$weblog->Story}</div>");
// now fetch the connected posts. this is the real magic:
$replies = $weblog->Find("Reply"); // fetches a pre-filled array of Reply objects.
if ($replies != false)
{
foreach($replies as $reply)
{
echo("<div class='weblogreply'><h4>By {$reply->Author} # {$reply->Posted}</h4> {$reply->Reply}</div>");
}
}
The weblog object would look like this:
class Weblog extends dbObject
{
function __construct($ID=false)
{
$this->__setupDatabase('blogs', // database table
array('ID_Blog' => 'ID', // database field => mapped object property
'strPost' => 'Story', // as you can see, database field strPost is mapped to $this->Story
'datPosted' => 'Posted',
'strPoster' => 'Author',
'strTitle' => 'Title',
'ipAddress' => 'IpAddress',
'ID_Blog', // primary table key
$ID); // value of primary key to init with (can be false for new empty object / row)
$this->addRelation('Reaction'); // define a 1:many relation to Reaction
}
}
See, no manual SQL writing :)
Link + more examples: Pork.dbObject
Oh yeah i also created a rudimentary GUI for my scaffolding tool: Pork.Generator

I like this format. It was mentioned in a previous comment, but the alignment seemed off to me.
$query = "SELECT "
. " foo, "
. " bar "
. "FROM "
. " mytable "
. "WHERE "
. " id = $userid";
Easy enough to read and understand. The dots line up with the equals sign keeping everything in a clean line.
I like the idea of keeping your SQL in a separate file too, although I'm not sure how that would work with variables like $userid in my example above.

Related

SQL query result to string

I'm using SQL in Yii framework.
I need to show the person's latest active week (it's number and date).So I wrote following code:
public function latestWeek()
{
$datalogin=//the login is working fine
$sql ="SELECT w.number,MAX(w.start_date)
FROM tbl_person_week t, tbl_week w
WHERE t.person_id=$this->id AND t.week_id=w.id";
$query = mysqli_query($datalogin, $sql);
return $query;
}
Now , I checked this query on the server and it works fine (almost) but first thing: I need to convert it into string , because yii's CgridView can't read it , and I couldn't find a working solution for this.
Second: on the server , it gave me the max date indeed , but not it's correct number , but the first number available. How can I fix this as well?
Queries like that should never be used in objective framework. If yu want to execute your own query, you should do it this way:
$sql = "your sql code";
$array = Yii::app()->db->createCommand($sql)->queryAll();
As result you will get multidimensional array with selected columns and rows
If you want to use it in grid view, you should do it this way:
$count = Yii::app()->db->createCommand($sql)->queryScalar();
$dataProvider = new CSqlDataProvider($sql, array('totalItemCount'=>$count));
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'grid-id',
'dataProvider'=> $dataProvider,
));
You can also use connection other than Yii::app()->db. Check CDbConnection class in docs.
edit: if you wanna use queries like mysql_fetch_assoc, check out also queryRow() method instead of queryAll()
Use Mysql_fetch _array
public function latestWeek()
{
$datalogin=//the login is working fine
$sql ="SELECT w.number,MAX(w.start_date)
FROM tbl_person_week t, tbl_week w
WHERE t.person_id=$this->id AND t.week_id=w.id";
$query = mysqli_query($datalogin, $sql);
while($row = mysqli_fetch_array($query)){
echo $row;
}
}
Assuming from your qu. that you want the week number and start date as one string, you have to concatenate the two columns in the sql.
You also need to specify that the week number is from the row with the maximum start date, which isn't as simple as you might first think.
I don't like injecting the person_id straight into SQL, it isn't awful in this case but is a bad habit to get into security-wise. There are binding methods available in the framework and I agree with Arek, that you should lean on the yii framework as much as possible.
To get the scalar string value, if you are insisting on using your own SQL.. I suggest the following:
$sql='
SELECT CONCAT('Week ',tw.number,' starting ',tw.start_date)
FROM tbl_week tw
JOIN (
SELECT MAX(twi.start_date) max_start_date
FROM tbl_week twi
JOIN tbl_person_week tpwi
ON tpwi.week_id = twi.id
AND tpwi.person_id = :person_id
) i
ON tw.start_date = i.max_start_date;
';
$command=Yii::app()->db->createCommand($sql);
$command->bindParam(":person_id", $this->id);
return $command->queryScalar();

How to filter function parameters

What is a proper way to filter parameters passed in functions? The goal is to make the function secure, especially when working with a database.
Example:
function user_profile($user_id)
{
//get user's profile data
$query = "SELECT * FROM `users` WHERE `user_id` = $user_id";
}
$user_id is a URI segment.
Other general examples are welcomed.
To escape strings, use the same method you'd use outside the function:
$user_id= mysql_real_escape_string($user_id);
If you're expecting the value to be, for example, an integer and would like to return error from the function if it isn't, you can do something like:
if (!is_int($user_id)) {
return FALSE;
}
else // do you query
Or if you expect it to match some specific pattern, do so with preg_match():
// For example, $user_id should be 4 letters and 4 numbers
if (!preg_match("/^[A-Z]{4}[0-9]{4}$/", $user_id)) {
return FALSE;
}
else // do you query
There's a couple of ways. The OLD way is to use mysql_real_escape_string(). However, many people nowadays complain bitterly about this, and say the proper way is to use prepared statements.
Create a filter class to handle all your filtering. Before you pass the variable into the function as a parameter, pass it through the filter class first. Or run the parameter through the filter class in the first line of your function.
So essentially, you're creating an abstract layer that 'filters'.
So the kind of filtering you're wanting to do in your scenario is to filter against sql injection/code injections.
So create a wrapper with this filter class around the mysql_real_escape_string() function.
The idea is to create an extensible filter class that can be used anywhere else in your application that is conceptually high level enough to handle all future needs.
final class Filter
{
static public function sqlInjections($some_parameter)
{
// my code to prevent injections by filtering $some_parameter
return mysql_real_escape_string($some_paramters);
}
static public function badWords()
{
// code in the future that can be added to filter bad words
}
}
call it like so $filtered_parameter = Filter::sqlInjections($some_paramter);
If your user_id field is a string in your database, then, you'll use mysql_real_escape_string(), or mysqli_real_escape_string(), or PDO::quote() -- depending on the API you're working with :
$query = "SELECT * FROM `users` WHERE `user_id` = '"
. mysql_real_escape_string($user_id) . "'";
or
$query = "SELECT * FROM `users` WHERE `user_id` = '"
. mysqli_real_escape_string($user_id) . "'";
or, with PDO -- provided that $db is a PDO object :
$query = "SELECT * FROM `users` WHERE `user_id` = '"
. $db->quote($user_id) . "'";
But, if it's an integer, you should make sure that the value passed to it is indeed an integer -- which is generally done using intval() :
$query = "SELECT * FROM `users` WHERE `user_id` = "
. intval($user_id);
Edit: I just realized you said it's an URL segment -- so, not an integer. I don't delete this idea, though: it might help someone else who would read this answer.
Another solution would be to not build a query containing that value -- and use prepared statements.
See :
For mysqli : mysqli::prepare()
And with PDO : PDO::prepare()
Use mysql_real_escape_string()
$query = "SELECT * FROM users WHERE user_id = '" . mysql_real_escape_string($user_id) . "'";
You can do two or Three complementary ways to prevent SQL injection:
The escape functions commented above.
Query the other way around:
function user_profile($user_id)
{
//get user's profile data
$query = "SELECT * FROM users WHERE {$user_id} = user_id";
}
User prepare and execute functions/methods if your database engine allow it
http://php.net/manual/en/pdo.prepare.php
http://www.php.net/manual/en/pdostatement.execute.php

Problem: Writing a MySQL parser to split JOIN's and run them as individual queries (denormalizing the query dynamically)

I am trying to figure out a script to take a MySQL query and turn it into individual queries, i.e. denormalizing the query dynamically.
As a test I have built a simple article system that has 4 tables:
articles
article_id
article_format_id
article_title
article_body
article_date
article_categories
article_id
category_id
categories
category_id
category_title
formats
format_id
format_title
An article can be in more than one category but only have one format. I feel this is a good example of a real-life situation.
On the category page which lists all of the articles (pulling in the format_title as well) this could be easily achieved with the following query:
SELECT articles.*, formats.format_title
FROM articles
INNER JOIN formats ON articles.article_format_id = formats.format_id
INNER JOIN article_categories ON articles.article_id = article_categories.article_id
WHERE article_categories.category_id = 2
ORDER BY articles.article_date DESC
However the script I am trying to build would receive this query, parse it and run the queries individually.
So in this category page example the script would effectively run this (worked out dynamically):
// Select article_categories
$sql = "SELECT * FROM article_categories WHERE category_id = 2";
$query = mysql_query($sql);
while ($row_article_categories = mysql_fetch_array($query, MYSQL_ASSOC)) {
// Select articles
$sql2 = "SELECT * FROM articles WHERE article_id = " . $row_article_categories['article_id'];
$query2 = mysql_query($sql2);
while ($row_articles = mysql_fetch_array($query2, MYSQL_ASSOC)) {
// Select formats
$sql3 = "SELECT * FROM formats WHERE format_id = " . $row_articles['article_format_id'];
$query3 = mysql_query($sql3);
$row_formats = mysql_fetch_array($query3, MYSQL_ASSOC);
// Merge articles and formats
$row_articles = array_merge($row_articles, $row_formats);
// Add to array
$out[] = $row_articles;
}
}
// Sort articles by date
foreach ($out as $key => $row) {
$arr[$key] = $row['article_date'];
}
array_multisort($arr, SORT_DESC, $out);
// Output articles - this would not be part of the script obviously it should just return the $out array
foreach ($out as $row) {
echo '<p>'.$row['article_title'].' <i>('.$row['format_title'].')</i><br />'.$row['article_body'].'<br /><span class="date">'.date("F jS Y", strtotime($row['article_date'])).'</span></p>';
}
The challenges of this are working out the correct queries in the right order, as you can put column names for SELECT and JOIN's in any order in the query (this is what MySQL and other SQL databases translate so well) and working out the information logic in PHP.
I am currently parsing the query using SQL_Parser which works well in splitting up the query into a multi-dimensional array, but working out the stuff mentioned above is the headache.
Any help or suggestions would be much appreciated.
From what I gather you're trying to put a layer between a 3rd-party forum application that you can't modify (obfuscated code perhaps?) and MySQL. This layer will intercept queries, re-write them to be executable individually, and generate PHP code to execute them against the database and return the aggregate result. This is a very bad idea.
It seems strange that you imply the impossibility of adding code and simultaneously suggest generating code to be added. Hopefully you're not planning on using something like funcall to inject code. This is a very bad idea.
The calls from others to avoid your initial approach and focus on the database is very sound advice. I'll add my voice to that hopefully growing chorus.
We'll assume some constraints:
You're running MySQL 5.0 or greater.
The queries cannot change.
The database tables cannot be changed.
You already have appropriate indexes in place for the tables the troublesome queries are referencing.
You have triple-checked the slow queries (and run EXPLAIN) hitting your DB and have attempted to setup indexes that would help them run faster.
The load the inner joins are placing on your MySQL install is unacceptable.
Three possible solutions:
You could deal with this problem easily by investing money into your current database by upgrading the hardware it runs on to something with more cores, more (as much as you can afford) RAM, and faster disks. If you've got the money Fusion-io's products come highly recommended for this sort of thing. This is probably the simpler of the three options I'll offer
Setup a second master MySQL database and pair it with the first. Make sure you have the ability to force AUTO_INCREMENT id alternation (one DB uses even id's, the other odd). This doesn't scale forever, but it does offer you some breathing room for the price of the hardware and rack space. Again, beef up the hardware. You may have already done this, but if not it's worth consideration.
Use something like dbShards. You still need to throw more hardware at this, but you have the added benefit of being able to scale beyond two machines and you can buy lower cost hardware over time.
To improve database performance you typically look for ways to:
Reduce the number of database calls
Making each database call as efficient as possible (via good design)
Reduce the amount of data to be transfered
...and you are doing the exact opposite? Deliberately?
On what grounds?
I'm sorry, you are doing this entirely wrong, and every single problem you encounter down this road will all be consequences of that first decision to implement a database engine outside of the database engine. You will be forced to work around work-arounds all the way to delivery date. (if you get there).
Also, we are talking about a forum? I mean, come on! Even on the most "web-scale-awesome-sauce" forums we're talking about less than what, 100 tps on average? You could do that on your laptop!
My advice is to forget about all this and implement things the most simple possible way. Then cache the aggregates (most recent, popular, statistics, whatever) in the application layer. Everything else in a forum is already primary key lookups.
I agree it sounds like a bad choice, but I can think of some situations where splitting a query could be useful.
I would try something similar to this, relying heavily on regular expressions for parsing the query. It would work in a very limited of cases, but it's support could be expanded progressively when needed.
<?php
/**
* That's a weird problem, but an interesting challenge!
* #link http://stackoverflow.com/questions/5019467/problem-writing-a-mysql-parser-to-split-joins-and-run-them-as-individual-query
*/
// Taken from the given example:
$sql = "SELECT articles.*, formats.format_title
FROM articles
INNER JOIN formats ON articles.article_format_id = formats.format_id
INNER JOIN article_categories ON articles.article_id = article_categories.article_id
WHERE article_categories.category_id = 2
ORDER BY articles.article_date DESC";
// Parse query
// (Limited to the clauses that are present in the example...)
// Edit: Made WHERE optional
if(!preg_match('/^\s*'.
'SELECT\s+(?P<select_rows>.*[^\s])'.
'\s+FROM\s+(?P<from>.*[^\s])'.
'(?:\s+WHERE\s+(?P<where>.*[^\s]))?'.
'(?:\s+ORDER\s+BY\s+(?P<order_by>.*[^\s]))?'.
'(?:\s+(?P<desc>DESC))?'.
'(.*)$/is',$sql,$query)
) {
trigger_error('Error parsing SQL!',E_USER_ERROR);
return false;
}
## Dump matches
#foreach($query as $key => $value) if(!is_int($key)) echo "\"$key\" => \"$value\"<br/>\n";
/* We get the following matches:
"select_rows" => "articles.*, formats.format_title"
"from" => "articles INNER JOIN formats ON articles.article_format_id = formats.format_id INNER JOIN article_categories ON articles.article_id = article_categories.article_id"
"where" => "article_categories.category_id = 2"
"order_by" => "articles.article_date"
"desc" => "DESC"
/**/
// Will only support WHERE conditions separated by AND that are to be
// tested on a single individual table.
if(#$query['where']) // Edit: Made WHERE optional
$where_conditions = preg_split('/\s+AND\s+/is',$query['where']);
// Retrieve individual table information & data
$tables = array();
$from_conditions = array();
$from_tables = preg_split('/\s+INNER\s+JOIN\s+/is',$query['from']);
foreach($from_tables as $from_table) {
if(!preg_match('/^(?P<table_name>[^\s]*)'.
'(?P<on_clause>\s+ON\s+(?P<table_a>.*)\.(?P<column_a>.*)\s*'.
'=\s*(?P<table_b>.*)\.(?P<column_b>.*))?$/im',$from_table,$matches)
) {
trigger_error("Error parsing SQL! Unexpected format in FROM clause: $from_table", E_USER_ERROR);
return false;
}
## Dump matches
#foreach($matches as $key => $value) if(!is_int($key)) echo "\"$key\" => \"$value\"<br/>\n";
// Remember on_clause for later jointure
// We do assume each INNER JOIN's ON clause compares left table to
// right table. Forget about parsing more complex conditions in the
// ON clause...
if(#$matches['on_clause'])
$from_conditions[$matches['table_name']] = array(
'column_a' => $matches['column_a'],
'column_b' => $matches['column_b']
);
// Match applicable WHERE conditions
$where = array();
if(#$query['where']) // Edit: Made WHERE optional
foreach($where_conditions as $where_condition)
if(preg_match("/^$matches[table_name]\.(.*)$/",$where_condition,$matched))
$where[] = $matched[1];
$where_clause = empty($where) ? null : implode(' AND ',$where);
// We simply ignore $query[select_rows] and use '*' everywhere...
$query = "SELECT * FROM $matches[table_name]".($where_clause? " WHERE $where_clause" : '');
echo "$query<br/>\n";
// Retrieve table's data
// Fetching the entire table data right away avoids multiplying MySQL
// queries exponentially...
$table = array();
if($results = mysql_query($table))
while($row = mysql_fetch_array($results, MYSQL_ASSOC))
$table[] = $row;
// Sort table if applicable
if(preg_match("/^$matches[table_name]\.(.*)$/",$query['order_by'],$matched)) {
$sort_key = $matched[1];
// #todo Do your bubble sort here!
if(#$query['desc']) array_reverse($table);
}
$tables[$matches['table_name']] = $table;
}
// From here, all data is fetched.
// All left to do is the actual jointure.
/**
* Equijoin/Theta-join.
* Joins relation $R and $S where $a from $R compares to $b from $S.
* #param array $R A relation (set of tuples).
* #param array $S A relation (set of tuples).
* #param string $a Attribute from $R to compare.
* #param string $b Attribute from $S to compare.
* #return array A relation resulting from the equijoin/theta-join.
*/
function equijoin($R,$S,$a,$b) {
$T = array();
if(empty($R) or empty($S)) return $T;
foreach($R as $tupleR) foreach($S as $tupleS)
if($tupleR[$a] == #$tupleS[$b])
$T[] = array_merge($tupleR,$tupleS);
return $T;
}
$jointure = array_shift($tables);
if(!empty($tables)) foreach($tables as $table_name => $table)
$jointure = equijoin($jointure, $table,
$from_conditions[$table_name]['column_a'],
$from_conditions[$table_name]['column_b']);
return $jointure;
?>
Good night, and Good luck!
In instead of the sql rewriting I think you should create a denormalized articles table and change it at each article insert/delete/update. It will be MUCH simpler and cheaper.
Do the create and populate it:
create table articles_denormalized
...
insert into articles_denormalized
SELECT articles.*, formats.format_title
FROM articles
INNER JOIN formats ON articles.article_format_id = formats.format_id
INNER JOIN article_categories ON articles.article_id = article_categories.article_id
Now issue the appropriate article insert/update/delete against it and you will have a denormalized table always ready to be queried.

Parameterized Queries

I am currently learning parametrized queries as there are advantages to using them.
Could someone give some pointers by converting this block of code to a parametrized version?
Thanks.
if(isset($_GET['news_art_id']) && (!empty($_GET['news_art_id'])))
{
$news_art_id = htmlentities(strip_tags($_GET['news_art_id']));
$news_art_id = validate_intval($news_art_id);
//echo $news_art_id;
$_SESSION['news_art_id'] = $news_art_id;
// Assign value to status.
$onstatus = 1;
settype($onstatus, 'integer');
$query = 'SELECT M.id, M.j_surname, M.j_points_count, M.j_level, A.j_user_id,A.id, A.jart_title, A.jart_tags, A.jart_description, A.jart_createddate FROM jt_articles A, jt_members M WHERE M.id = A.j_user_id AND A.id = ' . check_db_query_id($news_art_id) . " AND A.jart_status = $onstatus;";
$result = mysql_query($query) or die('Something went wrong. ' . mysql_error());
$artrows = mysql_num_rows($result);
}
The general rule is: Every variable should be binded, no inline variables at all.
Technical details: http://php.net/manual/en/pdo.prepare.php
in your case there is no advantage, remember a parameterised query requires 2 calls to the db : one to setup the query template and parse, the other to populate the query template params and is typically used when looping. So in this instance you're better off calling a stored procedure (always the best choice) or using inline sql and making sure you use http://php.net/manual/en/function.mysql-real-escape-string.php when applicable.

SQL query building practices

I am building a SQL query that dynamically changes based on passed in $_GET parameters. I simply want to know if putting in a 'dummy' constraint is an 'acceptable' practice, so I do not have to check the SQL string for the existence of 'where' before I add new constraints (each time).
For example, the 'dummy' constraint':
$sql = "select * from users u where u.id != 0";
Now, in each block where I determine if I have to add more constraints, I can just do:
if (!empty($uid))
$sql .= " and (u.id = {$uid})";
Instead of doing:
if (!empty($uid)) {
$sql .= strpos($sql, "where") === false ? " where " : " and ";
$sql .= " (u.id = {$uid})";
}
I've used that convention (although I usually use WHERE 1=1 AND.) Depending on your RDBMS, using a column there could affect performance. For example, if you had an index that would otherwise be a covering index except for that column.
Please make sure that you understand the potential pitfalls of this kind of dynamic SQL, but if it's what you ultimately end up doing, adding that extra bit seems to make sense to me.
Instead of appending to the SQL string for each check, you could collect the conditions:
if ($_GET["uid"]) {
$where[] = array("u.id =", $_GET->sql["uid"]);
if ($_GET["photo"]) {
$where[] = array("u.has_photo =", 1);
And complete the SQL string when you're through:
foreach ($where as $add) {
$sql .= ...;
}
Otherwise, it's an acceptable approach. I wouldn't turn out the big weapons and use a full blown ORM query builder for that.
from the manual
SELECT * FROM table WHERE 1
so yes, you can do that

Categories