[zend][db] fetchAll with multiple variables - zend-framework

I'm trying to use fetchAll on a query that has 2 variables. I can't figure out the syntax.
I can manage with only 1 variable:
$sql = "SELECT * FROM mytable WHERE field1 = ?";
$this->_db->fetchAll($sql,$value1); # that works
However I'm having some issues when query has multiple variables
$sql = "SELECT * FROM mytable WHERE field1 = ? AND field2 = ?";
$this->_db->fetchAll($sql,$value1,$value2); # doesn't work
$this->_db->fetchAll($sql,array("field1"=>$value1,"field2"=>$value2)); # doesn't work either
The reason why I want to use ? instead of placing the variables directly into the query is that I've learned that using ? allows for the query to be compiled generically by the db engine and improves performances.

There are two types of parameter, named parameters and positional parameters. You're mixing the two types and that won't work.
Named parameters match a placeholder by name. Names are started with the : symbol. The parameter names are not the same as the names of the columns you happen to use them for. You supply parameter values in an associative array, using the parameter name (not the column name) as the array keys. For example:
$sql = "SELECT * FROM mytable WHERE field1 = :param1 AND field2 = :param2";
$this->_db->fetchAll($sql,array("param1"=>$value1,"param2"=>$value2));
Positional parameters use the ? symbol for the placeholder. You supply parameter values using a simple (non-associative) array, and the order of values in the array must match the order of parameter placeholders in your query. For example:
$sql = "SELECT * FROM mytable WHERE field1 = ? AND field2 = ?";
$this->_db->fetchAll($sql,array($value1,$value2));
Most brands of SQL database natively support only one style or the other, but PDO attempts to support both, by rewriting the SQL if necessary before preparing the query. Since Zend_Db is modeled after PDO, Zend_Db also supports both parameter styles.

This question is a bit old, but I thought I'd just add to it for reference sake.
I would recommend starting to use Zend_Db_Select with Zend_Db. I've been doing a lot with Zend_Db lately. More from Zend_Db_Select reference guide.
Lets assume you have a Zend_Db adapter: $this->_db
# this will get the Zend_Db_Select object
$select = $this->_db->select();
# now you build up your query with Zend_Db_Select functions
$select->from('mytable');
$select->where('field1 = ?', $field1);
$select->where('field2 = ?', $field2);
[...]
# echo to see the SQL (helps in debugging)
# SELECT * FROM mytable WHERE field1 = ? AND field2 = ? [...]
echo '<p>My SQL: ' . $select . '</p>';
# Execute the SQL / Fetch results
$results = $select->query()->fetchAll();
That's the basics from your given example, but the Zend Framework reference guide on the select object has a lot of good information on how to build even more complex queries with JOINS, UNIONS, GROUP BY, LIMIT, HAVING, etc.
If you wanted to use an alias name for a table or parameters, you use an associative array with the alias name being the index value:
# SELECT p.* FROM products AS p
$select->from('p' => 'products');
If you want to return only selected fields, you add an array of field names as a second parameter:
# SELECT model FROM products
$select->from(products, array(model));
Actually, the above could should produce fully qualified SQL as:
SELECT 'products'.model FROM 'products'
but I wrote the above for brevity and clarity in the example.
One thing I just came across is using AND and OR in the WHERE condition.
# WHERE a = $a
$select->where('a = ?', $a);
# WHERE a = $a AND b = $b
$select->where('a = ?', $a);
$select->where('b = ?', $b);
# WHERE a = $a OR b = $b
$select->where('a = ?', $a);
$select->orWhere('b = ?', $b);
# WHERE a = $a AND b = $b
$select->orWhere('a = ?', $a);
$select->where('b = ?', $b);
Notice, that whatever the following "where" function you use, will combine with the previous statement as that operand. Ok, that sounded confusing.
If the second "where" is an "OR" it will be an "OR" conditional. If the second "where" is a "AND" the statement will be "AND".
In other words, the first WHERE function is ignored in terms of what condition it will use.
In fact, I just asked a question on Stack Overflow yesterday regarding doing a complex WHERE using select.
Hope that helps!
Cheers!

Try this:
$sql = "SELECT * FROM mytable WHERE field1 = ? AND field2 = ?";
$statement = $this->_db->query($sql,array("field1"=>$value1,"field2"=>$value2));
$data = $statement->fetchAll();
$this->_db must be an instance of Db adapter.

Heres the actual Zend way to code for this.
$sql = "SELECT * FROM mytable WHERE field1 = :param1 AND field2 = :param2";
$this->_db->fetchAll($sql,array("param1"=>$value1,"param2"=>$value2));
$where = $this->_db->select()
->from('mytable')
->where('field1 = ?',$value1)
->where('field2 = ?',$value2);
$rowSet = $this->_db->fetchAll($where);
This works great for me

Related

SELECT with substring in ON clause?

I have the following select statement in ABAP:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
INTO corresponding fields of table GT_INSTMUNIC_F
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN EVER AS EV on
MUNIC~POD = EV~VREFER(9).
"where EV~BSTATUS = '14' or EV~BSTATUS = '32'.
My problem with the above statement is that does not recognize the substring/offset operation on the 'ON' clause. If i remove the '(9) then
it recognizes the field, otherwise it gives error:
Field ev~refer is unknown. It is neither in one of the specified tables
nor defined by a "DATA" statement. I have also tried doing something similar in the 'Where' clause, receiving a similar error:
LOOP AT gt_instmunic.
clear wa_gt_instmunic_f.
wa_gt_instmunic_f-mandt = gt_instmunic-mandt.
wa_gt_instmunic_f-bis = gt_instmunic-bis.
wa_gt_instmunic_f-ab = gt_instmunic-ab.
wa_gt_instmunic_f-zzelecdate = gt_instmunic-zzelecdate.
wa_gt_instmunic_f-ZZCERTDATE = gt_instmunic-ZZCERTDATE.
wa_gt_instmunic_f-CONSYEAR = gt_instmunic-CONSYEAR.
wa_gt_instmunic_f-ZDIMO = gt_instmunic-ZDIMO.
wa_gt_instmunic_f-ZZONE_M = gt_instmunic-ZZONE_M.
wa_gt_instmunic_f-ZZONE_T = gt_instmunic-ZZONE_T.
wa_gt_instmunic_f-USAGE_M = gt_instmunic-USAGE_M.
wa_gt_instmunic_f-USAGE_T = gt_instmunic-USAGE_T.
temp_pod = gt_instmunic-pod.
SELECT vrefer
FROM ever
INTO wa_gt_instmunic_f-vrefer
WHERE ( vrefer(9) LIKE temp_pod ). " PROBLEM WITH SUBSTRING
"AND ( BSTATUS = '14' OR BSTATUS = '32' ).
ENDSELECT.
WRITE: / sy-dbcnt.
WRITE: / 'wa is: ', wa_gt_instmunic_f.
WRITE: / 'wa-ever is: ', wa_gt_instmunic_f-vrefer.
APPEND wa_gt_instmunic_f TO gt_instmunic_f.
WRITE: / wa_gt_instmunic_f-vrefer.
ENDLOOP.
itab_size = lines( gt_instmunic_f ).
WRITE: / 'Internal table populated with', itab_size, ' lines'.
The basic task i want to implement is to modify a specific field on one table,
pulling values from another. They have a common field ( pod = vrefer(9) ). Thanks in advance for your time.
If you are on a late enough NetWeaver version, it works on 7.51, you can use the OpenSQL function LEFT or SUBSTRING. Your query would look something like:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN ever AS ev
ON MUNIC~POD EQ LEFT( EV~VREFER, 9 )
INTO corresponding fields of table GT_INSTMUNIC_F.
Note that the INTO clause needs to move to the end of the command as well.
field(9) is a subset operation that is processed by the ABAP environment and can not be translated into a database-level SQL statement (at least not at the moment, but I'd be surprised if it ever will be). Your best bet is either to select the datasets separately and merge them manually (if both are approximately equally large) or pre-select one and use a FAE/IN clause.
They have a common field ( pod = vrefer(9) )
This is a wrong assumption, because they both are not fields, but a field an other thing.
If you really need to do that task through SQL, I'll suggest you to check native SQL sentences like SUBSTRING and check if you can manage to use them within an EXEC_SQL or (better) the CL_SQL* classes.

Column is not updating in postgresql

I tried to update my table like below:
$query = "select *
FROM sites s, companies c, tests t
WHERE t.test_siteid = s.site_id
AND c.company_id = s.site_companyid
AND t.test_typeid = '20' AND s.site_id = '1337'";
$queryrow = $db->query($query);
$results = $queryrow->as_array();
foreach($results as $key=>$val){
$update = "update tests set test_typeid = ? , test_testtype = ? where test_siteid = ?";
$queryrow = $db->query($update,array('10','Meter Calibration Semi Annual',$val->site_id));
}
The above code is working good. But in update query , The column test_typeid is not updated with '10'. Column test_typeid is updating with empty value. Other columns are updating good. I dont know why this column test_typeid is not updating? And the column test_typeid type is integer only. I am using postgreSql
And My table definition is:
What i did wrong with the code. Kindly advice me on this.
Thanks in advance.
First, learn to use proper JOIN syntax. Never use commas in the FROM clause; always use proper explicit JOIN syntax.
You can write the query in one statement:
update tests t
set test_typeid = '10',
test_testtype = 'Meter Calibration Semi Annual'
from sites s join
companies c
on c.company_id = s.site_companyid
where t.test_siteid = s.site_id and
t.test_typeid = 20 and s.site_id = 1337;
I assume the ids are numbers, so there is no need to use single quotes for the comparisons.

How do I use Placeholders in Perl to Inject a Variable into Mysql

I keep getting errors when attempting to use placeholders in my perl script for a Mysql routine.
Code :
use DBI;
my $driver = "mysql";
my $database = "database";
my $user = "exxxxxx";
my $password = "xxxxx";
my $dsn = "DBI:mysql:$database;mysql_local_infile=ON";
my $dbh = DBI->connect($dsn,$user,$password);
$dbh->do("SET \#tempc5 = (SELECT temp FROM day5 WHERE time = '00:00') ");
my $inter1 = i24;
$sth = $dbh->prepare( "SET \#sumadd5 = (SELECT ? FROM humid WHERE temp=\#tempc5) " );
$sth->bind_param( 1, $inter1 );
$sth->finish();
$dbh->disconnect();
This produces the following error:
Global symbol "$sth" requires explicit...
If I add a my $sth I get the following error:
Scalar found where operator expected...
Note that I am have no objection in trying this with $dbh->do("SET"
if possible.
The placeholders are not allowed for column names according to MySQL Manual for mysql_stmt_prepare() which is the function behind prepare.
The markers are legal only in certain places in SQL statements. For
example, they are permitted in the VALUES() list of an INSERT
statement (to specify column values for a row), or in a comparison
with a column in a WHERE clause to specify a comparison value.
However, they are not permitted for identifiers (such as table or
column names), or to specify both operands of a binary operator such
as the = equal sign. The latter restriction is necessary because it
would be impossible to determine the parameter type. In general,
parameters are legal only in Data Manipulation Language (DML)
statements, and not in Data Definition Language (DDL) statements.
If you think about it, it would not make sense to prepare a statement where you can change a column. Preparation of statement includes execution plan, but you can't plan execution of a statement where you don't know if given column has or doesn't have an index on it.
You can't use a placeholder there.
When you call prepare, all structural information about your tables is baked into the query, waiting for you to pass in data values to replace placeholders when you execute the query.
But you're trying to use a placeholder for a column name, which is part of the table's structure.
If you fix the Perl syntax to be:
my $inter1 = 'i24';
my $sth = $dbh->prepare( "SET \#sumadd5 = (SELECT ? FROM humid WHERE temp=\#tempc5) " );
$sth->execute($inter1);
it should run, but the ? will be treated as a data value rather than a column name (structural information). So you'll get the results of the SQL query
SET #sumadd5 = (SELECT 'i24' FROM humid WHERE temp=#tempc5)
instead of
SET #sumadd5 = (SELECT i24 FROM humid WHERE temp=#tempc5)
The subquery will return the literal value "i24" for each matching row rather than the value found in column i24.
You didn't quoted the vaule of $inter1. Change $inter1 = i24; to $inter1 = 'i24';. Just edited in your code, this will not give you syntax error.
use warnings;
use strict;
use DBI;
my $driver = "mysql";
my $database = "database";
my $user = "exxxxxx";
my $password = "xxxxx";
my $dsn = "DBI:mysql:$database;mysql_local_infile=ON";
my $dbh = DBI->connect($dsn,$user,$password);
$dbh->do("SET \#tempc5 = (SELECT temp FROM day5 WHERE time = '00:00') ");
my $inter1 = 'i24';
my $sth = $dbh->prepare( "SET \#sumadd5 = (SELECT ? FROM humid WHERE temp=\#tempc5) " );
$sth->bind_param( 1, $inter1 );
$sth->finish();
$dbh->disconnect();

Quote raw sql in ZEND to avoid sql injection

Long story short, I have an admin section where the user can choose from multiple dropdown lists the tables and fields that must be queries in order to get some values. Therefore, the query in ZEND is performed by concatenating the strings
$query = "SELECT $fieldName1, $fieldName2 from $tableName where $fieldName1 = $value";
How can I escape the above using ZEND approach to avoid sql injection? I tried adding them all as ? and calling quoteinto but it seems this does not work on some of the variables (like table names or field names)
ZF has quoteIdentifier() specifically for this purpose:
$query = "SELECT ".$db->quoteIdentifier($fieldName1).","...
In your case you might (also) want to check against a white list of valid column names.
Use quoteInto() or Zend_db_Select::where() for the values, and for the table and column names, I would simply strip any non alpha characters and then wrap them in ` quotes prior to using them in your SQL.
Example:
// Strip non alpha and quote
$fieldName1 = '`' . preg_replace('/[^A-Za-z]/', '', $fieldName1) . '`';
$tableName = '`' . preg_replace('/[^A-Za-z]/', '', $tableName) . '`';
// ....
// Build the SQL using Zend Db Select
$db->select()->from($tableName, array($fieldName1, $fieldName2))
->where($fieldName1 . ' = ?', $value);
In SafeMysql you can make it as simple, as
$sql = "SELECT ?n, ?n from ?n where ?n = ?s";
$data = $db->getAll($sql,$fieldName1,$fieldName2, $tableName, $fieldName1, $value);
though I understand that you won't change your ZF to SafeMysql.
Nevertheless, there is one essential thing that is ought to be done manually:
I doubt you want to let users to browse users table or financial table or whatever.
So, you have to verify a passed table name against an allowed tables array.
like
$allowed = ('test1','test2');
if (!in_array($tableName, $allowed)) {
throw new _403();
}

Modifying Columns In Zend Framework Select

In a Zend_Db_Select object, I am doing a join to get user information for some data records. For the join is on a userId, I would like to combine the user first and last name into a single column of name.
Basically I am looking to have something similar to this:
$table = array('u' => 'User');
$condition = 'u.id = t.id';
$columns = array('UserName' => 'u.FirstName + " " + u.LastName')
$select->joinLeft($table, $condition, $columns);
I have tried using a Zend_Db_Expr with no luck and the above does not work.
How would I go about doing this?
Zend_Db_Expr is the way to go, but you'll have better luck if you use the database's concatenation function. Assuming MySQL:
$columns = new Zend_Db_Expr("CONCAT(u.FirstName, ' ', u.LastName') AS name")