Build SQL Select a better way ? (Oracle) - php

i have the following code part in one of my classes:
$l = new Location();
$result = $l->getLocIdsbyCity($city); // returns csv
$ids = explode(',', $result);
$where = 'LOC_ID = ' . $ids[0];
unset($ids[0]);
foreach ($ids as $id) {
$where .= ' OR LOC_ID = ' . $id;
}
$select->where($where);
Is there an more "elegant" way to build the select stmt? I need all records with one of the provided ids..

Assuming your csv is injection safe (contains trusted values and no user-provided input):
$l = new Location();
$result = $l->getLocIdsbyCity($city); // returns csv
$where = "LOC_ID IN ($result)";
$select->where($where);
If it's not, you should explode it, mysql_real_escape_string each value and implode back.

You can use the in operator to form a condition like:
where LOC_ID in (1,2,3,4,5)
If you are sure that $result can't contain anything harmful, you should be able to use it directly without having to split it and loop.

Related

Codeigniter Query Builder having clause with IN statement

I am using CodeIgniter query builder. I want to add the having clause in there.
My code looks as follows:
(i omitted the other parts of the query):
$this->db->having($filterarray);
And i build the filterarray beforehand like this:
$filterarray = array();
if (!empty($filters['interests'])) {
$interestids = $this->interests_model->getAllIdsByDescription($filters['interests']);
$filterarray['interests IN'] = $interestids;
}
My function getAllIdsByDescription looks like this:
function getAllIdsByDescription($names){
$res = "(";
$query = $this->db->where_in('description', $names)
->select('interest_id')
->get($this->tableName);
foreach ($query->result() as $interestid) {
$res .= $interestid->interest_id;
$res .= ", ";
}
$res = substr($res, 0, -2);
$res .= ")";
return $res;
}
It translates my query to the following, hence why I have an error:
HAVING interests IN '(7)'
How do i remove the quotes around the (7) ?
You can disable the escaping.
If you are using a database that CodeIgniter escapes queries for, you
can prevent escaping content by passing an optional third argument,
and setting it to FALSE.
$this->db->having('user_id', 45); // Produces: HAVING `user_id` = 45
$this->db->having('user_id', 45, FALSE); // Produces: HAVING user_id = 45
Not sure how to implement this when just passing a single array as one argument, but you need the additional FALSE parameter.
From http://www.codeigniter.com/user_guide/database/query_builder.html

Get ID for each array element inside second array

I need to get unique ID for each array element inside second array. That ID already exist in table but I cant get them separately. URL that am getting now looks like this: http://page.com/index.php?p=view&m=area&id=173id=552id=768id=36id=217id=
I need just one ID and if first is used set second and so on.
I know that I should use mysqli or PDO and normalized tables but that later, now I need help with this.
This is the code:
$res= mysql_query("SELECT * FROM area WHERE user='$user' ORDER BY date") or die("Error: " . mysql_error());
while($row = mysql_fetch_assoc($res))
{
$id = $row['id'];
$x = array();
$parent = array();
foreach($row as $value)
{
if ($value == $id) continue;
else if ($value == $user) continue;
$result = explode(",", $value);
foreach($result as $newvalue)
{
$query = "SELECT x,firm FROM list where list.x='$newvalue'";
$result = mysql_query($query);
$r = mysql_fetch_assoc($result);
$x[] = $r['x'];
$xx = implode("id=",$x);
$parent[] = $r['firm'];
$list = implode("<a href='index.php?p=view&m=area&$xx'>", $parent)."</a>";
}
}
echo "<td><span>" . $list . "</span>/td>";
}
Thank you
first of all
$list = implode("<a h
should be
$list .= implode("<a h
That URL syntax is not the correct way to pass an array of values to a PHP script. It should use PHP array syntax for the parameter names. Also, you need separate your parameters with an ampersand (&):
http://page.com/index.php?p=view&m=area&id[]=173&id[]=552&id[]=76&8id[]=36&id[]=217
Then you can get the second one by using
$second_id = $_GET['id'][1]; // 552
etc.
FYI, you shouldn't use mysql_* functions in new code. They are no longer maintained and are officially deprecated. See the red box? Learn about prepared statements instead, and use PDO, or MySQLi - this article will help you decide which. If you choose PDO, here is a good tutorial.

creating a flexible update query with Php and pdo - problems with bindparam

I'm updating my mysql functions to use PDO. I've got the hang of most of it but struggling with an update function to update multiple fields in a records.
The function is in a class and I'm trying to keep it flexible to reuse with other tables etc.
Here's my function so far:
public function dbUpdateRecord($table, $values, $where)
{
$this->conn();
$fieldNames = array_keys($values);
var_dump($fieldNames);
$set="";
foreach ($fieldNames as $field) {
$set .= " $field = :$field,";
}
//strip last comma
$set = substr($set, 0, strlen($set) - 1);
$wherefields = array_keys($where);
$whereCondition="";
foreach ($wherefields as $field) {
$whereCondition .= " $field = :$field AND";
}
//strip last AND
$whereCondition = substr($whereCondition, 0, strlen($whereCondition) - 3);
$sql = "UPDATE $table SET $set WHERE $whereCondition";
var_dump($sql);
$stmt = $this->db->prepare($sql);
foreach ($values as $field => $value) {
$stmt->bindParam(':$field', $value);
}
foreach ($where as $field => $value) {
$stmt->bindParam(':$field', $value);
}
return $stmt->execute();
}
The problem is all the fields in the record are being updated by the id of the record which is contained in the $where variable.
$values contains an array of (fieldname=>value).
I think the problem lies around the bindparam and trying to make the fieldnames/placeholders dynamic
I thought I needed to use bindparam as best practice - is this correct or can I just go to execute()?
ANy help appreciated
You are lifting this log from the wrong end.
Your approach is potentially insecure yet inflexible at the same time.
What if you need a JOIN based update? What if you need OR in the WHERE (or IN)?
What you really need is a conventional query where only SET statement values have to be generated.
So, you need a helper function to produce such a statement out of data array, returning both correctly formatted SET statement and array with variables to be bound:
$fields = array("name","email");
$sql = "UPDATE users SET ".pdoSet($fields,$values,$data)." WHERE id = :id"
// now we have $values array to be passed into query
$stmt = $dbh->prepare();
$values["id"] = $_POST['id'];
$stmt->execute($values);
With this code you'll be able to make updates for the arbitrary query. And make it safe.
As a further step you will need to start using type-hinted placeholders, to make whole code like this:
$db->query("UPDATE ?n SET ?u WHERE id IN(?a)",$table,$data,$ids);
Getting back to your problem, ONe is right - you need to use bindValue instead of bindParam (as it mentioned in the tag wiki)
I believe the problem is that you are using a foreach to bind the params to the query. Why is this a problem? Because when you bind a variable, you bind a reference to that variable, so if that variable changes, the value in the query will change too. Since you are using a foreach loop, the value for all the parameters will be the latest value that the variable $value referenced to.
You can read more about this foreach behavior here and here. So basically, you have 2 options:
Use a reference to the actual value, instead of using a reference to $value (which can change its value in the next iteration)
Use an auxiliar variable that references another memory position that won't change during the loop
I came here because I was having the same problems, and YCS's solution was what I needed. For anyone else in this situation, here's the helper function I ended up using:
function commit_table($record_id, $changed_values)
{
$db = open_database();
$query = 'UPDATE table SET ';
$query_arguments = array();
$is_first = TRUE;
foreach(array_keys($changed_values) as $key)
{
if($is_first)
{
$is_first = FALSE;
}
else
{
$query .= ', ';
}
$value_var = ':' . $key;
$query .= $key;
$query .= ' = ';
$query .= $value_var;
$query_arguments[$value_var] = $changed_values[$key];
}
$query .= ' WHERE record_id = :record_id';
$query_arguments[':record_id'] = $record_id;
$stmt = $db->prepare($query);
$stmt->execute($query_arguments);
close_database($db);
}

Array to string conversion error

I have a query that's pulling a list of IDs. Those IDs are in an array and I need to search another table with those IDs. I tried using implode to make those IDs a string that I could use in a where clause but I keep getting this error.
My current code is:
$query = $this->db->query('
SELECT *
FROM system_scoperights
WHERE user = '. $this->session->userdata('username') .'
');
foreach ($query->result() as $row) {
$scope = $row->site;
$data[] = $scope;
}
$dataScope[] = $data;
$idList = implode(',', $dataScope); <---- Error Line
$where = 'WHERE scope_scopes.sc_ID IN '. $idList .'';
I've tried different things I found on forums like:
$idList = implode(',', array_values($dataScope));
and
$idList = implode(',', join($dataScope));
but none of those work. (I've never even heard of the join function)
Thanks in advance for the help.
$dataScope[] = $data;
but
$data[] = $scope;
therefore $dataScope has an array inside it's array. implode only work on one level, so that why you're getting this error.
You should note that this is actually possible in SQL:
SELECT * FROM some_table WHERE id IN (SELECT site FROM another_table WHERE ... )
which would eliminate the entire need for this code.
That is:
$where = 'WHERE scope_scopes.sc_ID IN (SELECT site
FROM system_scoperights
WHERE user = '. $this->session->userdata('username') . ')';
I partially agree with Jay's answer...just remove the line:
$dataScope[] = $data
and use the $data variable directly since it's already an array:
$idList = implode(',', $data);
However you also should use ( and ) in your where clause:
$where = 'WHERE scope_scopes.sc_ID IN (' . $idList . ')';
Using sub-queries in your where clauses, although they do have their place at times, can cost a lot of overhead, especially using 'SELECT *'. Never ask for more than you need from your db tables :)

Passing a Non-Value in a mySQL WHERE clause

The following is the query that I'm trying to get to work.
$array = array();
SELECT * FROM ENTRIES
WHERE
entry_id = '12'
OR
entry_id_extra IN ('$array')
This is of course simplified. The problem is that it works great if the array has items and it returns everything fine. But if the array has no item it fails to work.
What is the correct way to construct this statement that doesn't break if there are no items in the array? I tried IN ('NULL','$array') but that didnt work.
Any help is appreciated.
You can make the OR portion of the where clause go through a conditional check:
$sql = "SELECT * FROM entries WHERE entry_id = 12"
if (count($array) > 0) {
$sql .= ' OR entry_id_extra IN ($array)';
}
$array = array(...);
$array = array_map('mysql_escape_string', $array); // make sure it's safe
$query = "SELECT *
FROM entries
WHERE entry_id = '12'"
. (count($array) > 0
? " OR entry_id_extra IN ('" . implode("','", $array) . "')"
: "");
// echo the query to see what it looks like (optional)
echo "<pre>{$query}</pre>";
You can use implode, but also make sure you escape the values so quotes don't set the query off.

Categories