Hi i have a problem in QueryByilder in Doctrine. i wrote a Query that has 2 parameter and they affect in where statement. i want to ignore where statement if the related parameter was null. for example if $play = 3 and $theater = null the query must return all tickets with play 3 and whatever theater
this is my code:
public function getAllSearchedTickets($play,$teater){
return $this->getEntityManager()->createQuery('
select s from mtadminBundle:ReserveLocation s
join s.reserve a
join a.sance b
where a.acceptCode != 0
and b.play = :play
and b.teater = :teater')
->setParameters(array('play'=>$play,'teater'=>$teater))->getResult();
}
thank you.
You should use the QueryBuilder for this, to do it more efficiently, I'll show you how you do yours and then the same with the QueryBuilder as example:
Yours:
public function getAllSearchedTickets($play,$teater){
$query = 'select s from mtadminBundle:ReserveLocation s'.
'join s.reserve a'.
'join a.sance b'.
'where a.acceptCode != 0');
$paramArray = array();
if( $play ) {
$query .= ' and b.play = :play';
$paramArray['play'] = $play;
}
if( $teater ) {
$query .= ' and b.teater = :teater';
$paramArray['teater '] = $teater;
}
return $this->getEntityManager()->createQuery($query)
->setParameters($paramArray)->getResult();
}
QueryBuilder:
public function getAllSearchedTickets($play,$teater){
$queryBuilder = $this->getEntityManager()->createQueryBuilder();
$queryBuilder->select('s')
->from('mtadminBundle:ReserveLocation', 's')
->join('s.reserve', 'a')
->join('a.sance', 'b')
->where('a.acceptCode != 0');
if( $play ) {
$queryBuilder->andWhere('b.play = :play');
$queryBuilder->setParameter('play', $play);
}
if( $teater ) {
$queryBuilder->andWhere('b.teater = :teater');
$queryBuilder->setParameter('teater', $teater);
}
return $queryBuilder->getResult();
}
Related
I´ve got the following SQL-Query
$uidArray = explode(",", $uids);
foreach ($uidArray as $uid) {
$dynamicUid[] = '`uid` LIKE \''.$uid.'\'';
}
$query = $this->createQuery();
$query->statement("SELECT * FROM `tx_myextension_domain_model_thi` WHERE ".implode($dynamicUid, " OR "));
return $query->execute();
This works fine but I want to have it like this:
$uidArray = explode(",", $uids);
$query = $this->createQuery();
foreach ($uidArray as $key => $value) {
$constraints[] = $query->equals('uid', $value);
}
return $query->matching(
$query->logicalAnd($constraints)
)->execute();
Here I get the following Output with the Query Parser :
'SELECT `tx_myextension_domain_model_thi`.* FROM `tx_myextension_domain_model_thi`
`tx_myextension_domain_model_thi`
WHERE ((`tx_myextension_domain_model_thi`.`uid` = :dcValue1) AND
(`tx_myextension_domain_model_thi`.`uid` = :dcValue2)) AND
(`tx_myextension_domain_model_thi`.`sys_language_uid` IN (0, -1)) AND
(`tx_myextension_domain_model_thi`.`pid` = 0) AND
((`tx_myextension_domain_model_thi`.`deleted` = 0) AND (
`tx_myextension_domain_model_thi`.`t3ver_state` <= 0) AND
(`tx_myextension_domain_model_thi`.`pid` <> -1) AND
(`tx_myextension_domain_model_thi`.`hidden` = 0) AND
(`tx_myextension_domain_model_thi`.`starttime` <= 1607084460) AND
((`tx_myextension_domain_model_thi`.`endtime` = 0)
OR (`tx_myextension_domain_model_thi`.`endtime` > 1607084460)))'
And the Uids as dcValue-Array.
dcValue1 => '1' (1 chars)
dcValue2 => '2' (1 chars)
Maybe someone can help me to rewrite this, because unfortunately I can't get any further!
Thanks :)
Did you try the in operator?
public function yourFunctionName($uid)
{
$query = $this->createQuery();
$query->in('uid', $uidArray);
return $query->execute();
}
Assuming that your array looks like this:
$uidArray = [
0 = '34',
1 = '15',
3 = '88'
]
EDIT
If you do not care about where your objects are stored then you can do the following.
public function yourFunctionName($uid)
{
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(FALSE);
$query->matching(
$query->in('uid', $uidArray)
);
return $query->execute()];
}
Which is going to ignore the pid in the query
I tryng to pass an array of values from my controller to the model
My controller:
$arr_list = array('O','N','H');
$data['lista'] = $getmodel->query_list($arr_list1);
My model
public function query_list($list = '', $limit = 10, $lang = 'en')
{
$query = "SELECT tab1.id, name FROM tab_name";
$a = 0;
foreach( $list as $value)
{
if ($value != ''){
if ($a == 0){
$query .= " AND (field = '".$value."' ";
}
else
{
$query .= " OR field = '".$value."' ";
}
$a = $a + 1;
}
}
$query .= " ORDER BY RAND() ";
}
I receive the error: Array to string conversion
This code works perfectly in CI 2 but does not work on CI4.
Why?
Lots of bugs in yours the code, but I'll try to help.
Docs CI4: $builder->whereIn()
Generates a WHERE field IN (‘item’, ‘item’) SQL query joined with AND if appropriate
$names = ['Frank', 'Todd', 'James'];
$builder->whereIn('username', $names);
// Produces: WHERE username IN ('Frank', 'Todd', 'James')
Controller
$this->data['return'] = $HotelModel->test(['1', '2', '3', '4']);
Model
public function test($array)
{
return $this->db->table('tab_name')->whereIn('column',$array)->get()->getResultObject();
}
Result last_query
SELECT * FROM 'tab_name' WHERE 'column' IN ('1', '2', '3', '4')
I have to perform a select on which I have more than 1000 elements via hibernate, and then I received the error "ORA-01795:maximum number of expressions in a list is 1000" when I'm using the Oracle brand.
SELECT * FROM matable WHERE column IN (?,?,...) (>1000 items)
I found many solutions :
Split the list with OR
where A in (a,b,c,d,e,f)
becomes
where (A in (a,b,c) OR a in (d,e,f)) ...
Create a table with UNION ALL
SELECT * FROM maintable
JOIN (
SELECT v1 a FROM DUAL UNION ALL
SELECT v2 a FROM DUAL UNION ALL
SELECT v3 a FROM DUAL UNION ALL
...
SELECT v2000 a FROM DUAL) tmp
on tmp.a = maintable.id
Using tuples to get rid of the limit
where (column,0) in ((1,0),(2,0),(3,0),(4,0), ... ,(1500,0))
Using a temporary table..
where A in SELECT item FROM my_temporary_table
References here and there and also there.
My question is the following : what is the best practice to deal with this issue? By best practice I mean the most performant, but not only for Oracle; if I use hibernate, I don't want to create and manage a different code for each brand of database (I'm concerned by Oracle, MS SQL and PostGre only).
My first reaction would have been to use a temporary table, but I don't know what has the most impact.
Use a temporary table and make the values primary keys on the table. This should allow very efficient optimizations for comparison. The most like is simply an index lookup, although if the table is very small, Oracle might choose some other method such as a table scan.
This method should be faster than 1,000 or conditions, in almost any database. Sometimes in is optimized in a similar way (using a binary tree to store the values). In such databases, the performance would be similar.
I fixed this issue with some changes in hibernate-core jar.
I made a helper class to split an expression in more joins like: ... t.column IN (: list_1) OR t.column IN (: list_2) ... , Then I changed AbstractQueryImpl.expandParameterList method from hibernate to call my method if the collection exceeds the limit.
My hibernate-core version is 3.6.10.Final, but it work fine and for 4.x versions - I tested it.
My code is tested for next cases:
where t.id in (:idList)
where (t.id in (:idList))
where ((t.id) in (:idList))
where 1=1 and t.id in (:idList)
where 1=1 and (t.id in (:idList))
where 1=1 and(t.id) in (:idList)
where 1=1 and((t.id) in (:idList))
where 1=1 and(t.id in (:idList))
where t.id not in (:idList)
where (t.id not in (:idList))
where ((t.id) not in (:idList))
AbstractQueryImpl.expandParameterList :
private String expandParameterList(String query, String name, TypedValue typedList, Map namedParamsCopy) {
Collection vals = (Collection) typedList.getValue();
Type type = typedList.getType();
boolean isJpaPositionalParam = parameterMetadata.getNamedParameterDescriptor( name ).isJpaStyle();
String paramPrefix = isJpaPositionalParam ? "?" : ParserHelper.HQL_VARIABLE_PREFIX;
String placeholder =
new StringBuffer( paramPrefix.length() + name.length() )
.append( paramPrefix ).append( name )
.toString();
if ( query == null ) {
return query;
}
int loc = query.indexOf( placeholder );
if ( loc < 0 ) {
return query;
}
String beforePlaceholder = query.substring( 0, loc );
String afterPlaceholder = query.substring( loc + placeholder.length() );
// check if placeholder is already immediately enclosed in parentheses
// (ignoring whitespace)
boolean isEnclosedInParens =
StringHelper.getLastNonWhitespaceCharacter( beforePlaceholder ) == '(' &&
StringHelper.getFirstNonWhitespaceCharacter( afterPlaceholder ) == ')';
if ( vals.size() == 1 && isEnclosedInParens ) {
// short-circuit for performance when only 1 value and the
// placeholder is already enclosed in parentheses...
namedParamsCopy.put( name, new TypedValue( type, vals.iterator().next(), session.getEntityMode() ) );
return query;
}
// *** changes by Vasile Bors for HHH-1123 ***
// case vals.size() > 1000
if ((vals.size() >= InExpressionExpander.MAX_ALLOWED_PER_INEXPR) && isEnclosedInParens) {
InExpressionExpander inExpressionExpander = new InExpressionExpander(beforePlaceholder, afterPlaceholder);
if(inExpressionExpander.isValidInOrNotInExpression()){
List<String> list = new ArrayList<String>( vals.size() );
Iterator iter = vals.iterator();
int i = 0;
String alias;
while ( iter.hasNext() ) {
alias = ( isJpaPositionalParam ? 'x' + name : name ) + i++ + '_';
namedParamsCopy.put( alias, new TypedValue( type, iter.next(), session.getEntityMode() ) );
list.add(ParserHelper.HQL_VARIABLE_PREFIX + alias );
}
String expandedExpression = inExpressionExpander.expandExpression(list);
if(expandedExpression != null){
return expandedExpression;
}
}
}
// *** end changes by Vasile Bors for HHH-1123 ***
StringBuffer list = new StringBuffer(16);
Iterator iter = vals.iterator();
int i = 0;
while (iter.hasNext()) {
String alias = (isJpaPositionalParam ? 'x' + name : name) + i++ + '_';
namedParamsCopy.put(alias, new TypedValue(type, iter.next(), session.getEntityMode()));
list.append(ParserHelper.HQL_VARIABLE_PREFIX).append(alias);
if (iter.hasNext()) {
list.append(", ");
}
}
return StringHelper.replace(
beforePlaceholder,
afterPlaceholder,
placeholder.toString(),
list.toString(),
true,
true
);
}
My helper class InExpressionExpander:
package org.hibernate.util;
import org.hibernate.QueryException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
/**
* Utility class for expand Hql and Sql IN expressions with a parameter with more than IN expression limit size (HHH-1123).
* <br/>
* It work for expression with formats:
* <pre>
*
* where t.id in (:idList)
* where (t.id in (:idList))
* where ((t.id) in (:idList))
* where 1=1 and t.id in (:idList)
* where 1=1 and (t.id in (:idList))
* where 1=1 and(t.id) in (:idList)
* where 1=1 and((t.id) in (:idList))
* where 1=1 and(t.id in (:idList))
*
* where t.id not in (:idList)
* where (t.id not in (:idList))
* where ((t.id) not in (:idList))
* </pre>
* <p/>
* Example:
* <pre>
* select t.id from tableOrEntity t where t.id IN (:idList)
* </pre
*
* #author Vasile Bors
* #since 13/12/2015.
*/
public class InExpressionExpander {
private static final Logger log = LoggerFactory.getLogger(InExpressionExpander.class);
public static final int MAX_ALLOWED_PER_INEXPR = 1000;
private static final int MAX_PARAMS_PER_INEXPR = 500;
private Stack<String> stackExpr = new Stack<String>();
private StringBuilder toWalkQuery;
private final String beforePlaceholder;
private final String afterPlaceholder;
private boolean wasChecked = false;
private boolean isEnclosedInParens = false;
private boolean isInExpr = false;
private boolean isNotInExpr = false;
public InExpressionExpander(String beforePlaceholder, String afterPlaceholder) {
this.toWalkQuery = new StringBuilder(beforePlaceholder);
this.beforePlaceholder = beforePlaceholder;
this.afterPlaceholder = afterPlaceholder;
}
public boolean isValidInOrNotInExpression() {
if (!wasChecked) {
String lastExpr = extractLastExpression();
if ("(".equals(lastExpr)) {
isEnclosedInParens = true;
lastExpr = extractLastExpression();
}
isInExpr = "in".equalsIgnoreCase(lastExpr);
}
wasChecked = true;
return isInExpr;
}
public String expandExpression(List paramList) {
if (isValidInOrNotInExpression()) {
final String lastExpr = extractLastExpression(false);
if ("not".equalsIgnoreCase(lastExpr)) {
isNotInExpr = true;
extractLastExpression(); //extract "not" and consume it
}
extractColumnForInExpression();
StringBuilder exprPrefixBuilder = new StringBuilder();
for (int i = stackExpr.size() - 1; i > -1; i--) {
exprPrefixBuilder.append(stackExpr.get(i)).append(' ');
}
if (!isEnclosedInParens) {
exprPrefixBuilder.append('(');
}
String expandedExpression = expandInExpression(exprPrefixBuilder, paramList);
String beforeExpression = getBeforeExpression();
String afterExpression = getAfterExpression();
String expandedQuery = new StringBuilder(beforeExpression).append(expandedExpression)
.append(afterExpression)
.toString();
if (log.isDebugEnabled()) {
log.debug(
"Query was changed to prevent exception for maximum number of expression in a list. Expanded IN expression query:\n {}",
expandedExpression);
log.debug("Expanded query:\n {}", expandedQuery);
}
return expandedQuery;
}
log.error("Illegal call of InExpressionExpander.expandExpression() without IN expression.");
return null;
}
private String expandInExpression(StringBuilder exprPrefixBuilder, List values) {
String joinExpr = isNotInExpr ? ") and " : ") or ";
StringBuilder expr = new StringBuilder(16);
Iterator iter = values.iterator();
int i = 0;
boolean firstExpr = true;
while (iter.hasNext()) {
if (firstExpr || i % MAX_PARAMS_PER_INEXPR == 0) {
//close previous expression and start new expression
if (!firstExpr) {
expr.append(joinExpr);
} else {
firstExpr = false;
}
expr.append(exprPrefixBuilder);
} else {
expr.append(", ");
}
expr.append(iter.next());
i++;
}
expr.append(')');// close for last in expression
return expr.toString();
}
/**
* Method extract last expression parsed by space from toWalkQuery and remove it from toWalkQuery;<br/>
* If expression has brackets it will return al content between brackets and it will add additional space to adjust splitting by space.
*
* #return last expression from toWalkQuery
*/
private String extractLastExpression() {
return extractLastExpression(true);
}
/**
* Method extract last expression parsed by space from toWalkQuery, remove it from toWalkQuery if is consume = true;<br/>
* If expression has brackets it will return al content between brackets and it will add additional space to adjust splitting by space.
*
* #param consum if true the method will extract and remove last expression from toWalkQuery
* #return last expression from toWalkQuery
*/
private String extractLastExpression(final boolean consum) {
int lastIndex = this.toWalkQuery.length() - 1;
String lastExpr;
int exprSeparatorIndex = this.toWalkQuery.lastIndexOf(" ");
if (lastIndex == exprSeparatorIndex) { //remove last space from the end
this.toWalkQuery.delete(exprSeparatorIndex, this.toWalkQuery.length());
return extractLastExpression(consum);
} else {
lastExpr = this.toWalkQuery.substring(exprSeparatorIndex + 1, this.toWalkQuery.length());
if (lastExpr.length() > 1) {
if (lastExpr.endsWith(")")) {
//if parens are closed at the end we need to find where it is open
int opensParens = 0;
int closedParens = 0;
int startExprIndex = -1;
char c;
for (int i = lastExpr.length() - 1; i > -1; i--) {
c = lastExpr.charAt(i);
if (c == ')') {
closedParens++;
} else if (c == '(') {
opensParens++;
}
if (closedParens == opensParens) {
startExprIndex = i;
break;
}
}
if (startExprIndex > -1) {
lastExpr = lastExpr.substring(startExprIndex, lastExpr.length());
exprSeparatorIndex = exprSeparatorIndex + startExprIndex
+ 1; // +1 because separator is not space and don't must be deleted
}
} else if (lastExpr.contains("(")) {
int parentsIndex = exprSeparatorIndex + lastExpr.indexOf('(') + 1;
this.toWalkQuery.replace(parentsIndex, parentsIndex + 1, " ( ");
return extractLastExpression(consum);
}
}
if (consum) {
this.toWalkQuery.delete(exprSeparatorIndex, this.toWalkQuery.length());
}
}
if (consum) {
stackExpr.push(lastExpr);
}
return lastExpr;
}
private String extractColumnForInExpression() {
String column = extractLastExpression();
String beforeColumn = extractLastExpression(false);
long pointIndx = beforeColumn.lastIndexOf('.');
if (pointIndx > -1) {
if (pointIndx == (beforeColumn.length() - 1)) {
throw new QueryException(
"Invalid column format: " + beforeColumn + ' ' + column
+ " . Remove space from column!");
}
}
return column;
}
private String getBeforeExpression() {
return this.toWalkQuery + " (";
}
private String getAfterExpression() {
if (StringHelper.getFirstNonWhitespaceCharacter(afterPlaceholder) == ')') {
return afterPlaceholder;
}
return afterPlaceholder + ") ";
}
}
I am happy to receive any suggestions for improving this solution.
I have catalog price rules for some categories. In frontend, in category.tpl, I have to notify if there are special prices bound to that specific category or some of its parents.
For now, I'm building a function on the controller that finds it with a query. I was wandering if there was some shortcut for doing this.
I wrote a function (a CategoryController method) to solve my problem and I share:
public function get_category_promo(){
//get the active country, since promo are also country-based
if($this->context->customer->isLogged()){
$current_country = Customer::getCurrentCountry($this->context->customer->id);
} else {
$current_country = $this->context->country->id;
}
$db = Db::getInstance();
$sql = '
SELECT
truncate(`reduction`,0) as reduction,
`reduction_type`,
`from`,
`to`,
`type` as category_type,
`value` as id_category,
`id_parent` as category_parent,
`level_depth` as depth
FROM
`'._DB_PREFIX_.'specific_price_rule_condition` rule_condition
INNER JOIN
`'._DB_PREFIX_.'specific_price_rule_condition_group` rule_group
on rule_condition.`id_specific_price_rule_condition_group` = rule_group.`id_specific_price_rule_condition_group`
INNER JOIN
`'._DB_PREFIX_.'specific_price_rule` price_rule
on price_rule.`id_specific_price_rule` = rule_group.`id_specific_price_rule`
INNER JOIN
`'._DB_PREFIX_.'category` category
on rule_condition.`value` = category.`id_category`
WHERE rule_condition.`type` = "category"';
$parents = $this->category->getParentsCategories();
array_shift($parents);//first is == this->category so I shift it out
$sql_aux = ' and (rule_condition.`value` = ' . $this->category->id_category;
foreach($parents as $parent){
$sql_aux .= ' or rule_condition.`value` = ' . $parent["id_category"];
}
$sql_aux .= ')';
$sql_aux .= ' and price_rule.`id_country` = ' . $current_country;
$sql_aux .= ' order by level_depth desc';
$sql .= $sql_aux;
$promo_data = $db->executeS($sql);
$promo_data = count($promo_data) > 0 ? $promo_data : null;
if(!$promo_data) return false;//stop
function validate_date($promo){
//if there are no dates
if((int)$promo['to'] == 0 && (int)$promo['from'] == 0) return true;
//if there is end promo date
if((int)$promo['to'] != 0){
$to = new DateTime($promo['to']);
//...and is still valid
if($to >= new DateTime('NOW')) return true;
}
}//end validate
//refine query results
$filtered = array_values(array_filter($promo_data,'validate_date'));
$promo_data = $filtered[0];//if there are more than one promo on the same category, the promo on the higher depth is used form the system, so I need only that promo
//promo without dates. Only refine to/from to better use in smarty
if((int)$promo_data['to'] == 0 && (int)$promo_data['from'] == 0){
$promo_data['to'] = 0;
$promo_data['from'] = 0;
$this->context->smarty->assign('promo_data', $promo_data);
return true;
}
if((int)$promo_data['to'] != 0){//promo ha send date
$to = new DateTime($promo_data['to']);
if($to >= new DateTime('NOW')){
$promo_data['to'] = $to->format('d-m-Y');
} else {
return false;//not a valid date
}
}
if((int)$promo_data['from'] != 0){
$from = new DateTime($promo_data['from']);
$promo_data['from'] = $from->format('d-m-Y');
} else {
$promo_data['from'] = 0;
}
$this->context->smarty->assign('promo_data', $promo_data);
}//end get_category_promo
This is a question as might be focused on working in the best way, if there are other alternatives or is the only way:
Using Codeigniter ... I have the typical 2 functions of list records and show total number of records (using the page as an alternative). The problem is that they are rather large. Sample 2 functions in my model:
count Rows:
function get_all_count()
{
$this->db->select('u.id_user');
$this->db->from('user u');
if($this->session->userdata('detail') != '1')
{
$this->db->join('management m', 'm.id_user = u.id_user', 'inner');
$this->db->where('id_detail', $this->session->userdata('detail'));
if($this->session->userdata('management') === '1')
{
$this->db->or_where('detail', 1);
}
else
{
$this->db->where("id_profile IN (
SELECT
e2.id_profile
FROM profile e, profile e2, profile_path p, profile_path p2
WHERE e.id_profile = " . $this->session->userdata('profile') . "
AND p2.id_profile = e.id_profile
AND p.path LIKE(CONCAT(p2.path,'%'))
AND e2.id_profile = p.id_profile
)", NULL, FALSE);
$this->db->where('MD5(u.id_user) <>', $this->session->userdata('id_user'));
}
}
$this->db->where('u.id_user <>', 1);
$this->db->where('flag <>', 3);
$query = $this->db->get();
return $query->num_rows();
}
results per page
function get_all($limit, $offset, $sort = '')
{
$this->db->select('u.id_user, user, email, flag');
$this->db->from('user u');
if($this->session->userdata('detail') != '1')
{
$this->db->join('management m', 'm.id_user = u.id_user', 'inner');
$this->db->where('id_detail', $this->session->userdata('detail'));
if($this->session->userdata('management') === '1')
{
$this->db->or_where('detail', 1);
}
else
{
$this->db->where("id_profile IN (
SELECT
e2.id_profile
FROM profile e, profile e2, profile_path p, profile_path p2
WHERE e.id_profile = " . $this->session->userdata('profile') . "
AND p2.id_profile = e.id_profile
AND p.path LIKE(CONCAT(p2.path,'%'))
AND e2.id_profile = p.id_profile
)", NULL, FALSE);
$this->db->where('MD5(u.id_user) <>', $this->session->userdata('id_user'));
}
}
$this->db->where('u.id_user <>', 1);
$this->db->where('flag <>', 3);
if($sort) $this->db->order_by($sort);
$this->db->limit($limit, $offset);
$query = $this->db->get();
return $query->result();
}
You see, I repeat the most of the functions, the difference is that only the number of fields and management pages.
I wonder if there is any alternative to get as much results as the query in a single function. I have seen many tutorials, and all create 2 functions: one to count and another to show results ... Will there be more optimal?
you have to use SQL_CALC_FOUND_ROWS in your query, something like this:
SELECT SQL_CALC_FOUND_ROWS first_name, age FROM table
to return the result you can simply create a new array:
$result['total'] = $this->db->query("SELECT FOUND_ROWS() as total")->row_array();
$result['result_array'] = $query->result();
return $result;