Yii2: how to build query properly using Query Builder - yii2

I'm using Yii2 and I have such query now:
$data = $modelClass::find()->select([
'task_group.*',
'count' => 'COUNT(task.id)',
'processed_count' => 'SUM(CASE WHEN task.status=\''.Task::STATUS_PROCESSED.'\' THEN 1 ELSE 0 END)',
'unprocessed_count' => 'SUM(CASE WHEN task.status=\''.Task::STATUS_UNPROCESSED.'\' THEN 1 ELSE 0 END)',
'problem_count' => 'SUM(CASE WHEN task.status=\''.Task::STATUS_PROBLEM.'\' THEN 1 ELSE 0 END)'])
->joinWith('tasks')
->groupBy('task_group.id')->all();
But I think there must be some better way in Yii2 to represent it. How can I rewrite it properly? For example, without inline parameters.

Now I see, it can be done in such way:
task.status=:status_processed
...
->addParams([':status_processed' => Task::STATUS_PROCESSED])

Related

How to calculate subtraction of 2 Sql Query

I want to do a subtraction operator between entotalitem and extotalitem.query that I use retrieve data from the same table that is tbl_orders_data.
I have tried by creating 2 queries, the first is the query to retrieve entotalitem and the second query to retrieve extotalitem
$encheck = DB::table('tbl_orders_data')
->select('slot_id', DB::raw('sum(total_item) as entotalitem'))
->where('id_order_data', 'like', 'PBM' . '%')
->groupBy('slot_id')
->pluck('entotalitem');
$excheck = DB::table('tbl_orders_data')
->select('slot_id', DB::raw('sum(total_item) as extotalitem'))
->where('id_order_data', 'like', 'PBK' . '%')
->groupBy('slot_id')
->pluck('extotalitem');
$en = $encheck;
$ex = $excheck;
dd($en - $ex);
Should I only need to use one query? or should I make 2 queries as I have tried?
please help me, thanks
You may use conditional aggregation here:
$check = DB::table('tbl_orders_data')
->select('slot_id', DB::raw("sum(case when id_order_data like 'PBM%' then total_item else 0 end) -
sum(case when id_order_data like 'PBK%' then total_item else 0 end) as totalitem"))
->groupBy('slot_id')
->pluck('totalitem');

How to select multiple where conditions using codeigniter?

my table
This is my table i have to display the data using multiple where conditions. First i select using dms_expire_date,for this i got answer and i am using dms_doc_name in where conditions but i didn't got correct result.
I have tired so far.
]
See this picture if i use "or" in "where" i got testie not within this date.but if i use "and" in "where" i didn't got result for dms_expiry_date
See this picture
i don't know how to do this.please help to solve this problem.
SELECT * FROM `dms_document` WHERE dms_expire_date BETWEEN '2020-05-01' AND '2020-07-16' AND dms_doc_name = '' OR dms_category_id = '' OR dms_subcategory_id=''
I have tired in array also but i didn't result.
$this->db->select('*');
$this->db->join('dms_category as C', 'C.dms_category_id = D.dms_category_id', 'left outer');
$this->db->join('dms_sub_category as S', 'S.dms_subcategory_id = D.dms_subcategory_id', 'left outer');
$this->db->where('dms_expire_date >=', $newstart);
$this->db->where('dms_expire_date <=', $newend);
$this->db->where(array('dms_doc_name' =>$docname,'D.dms_category_id'=>$category,'D.dms_subcategory_id'=>$subcategory));
$data['documents']= $this->db->get('dms_document as D')->result_array();
try
SELECT *
FROM dms_document
WHERE dms_expire_date BETWEEN $newstart AND $newend
AND CASE WHEN $docname IS NULL THEN 1 = 1 ELSE dms_doc_name = $docname END
AND CASE WHEN $category IS NULL THEN 1 = 1 ELSE dms_category_id = $category END
AND CASE WHEN $subcategory IS NULL THEN 1 = 1 ELSE dms_subcategory_id = $subcategory END
--
edited. waiting for clearance.
if I get what you mean correctly, it is problem with understanding AND, OR, and the priority with brackets ()
--
if anyone want to use the fiddle, I have created the sample data for this issue
You can write OR using CodeIgniter's or_where() (v3 docs) or orWhere() (v4 docs).
//your initial query
$sql = "SELECT
*
FROM
`dms_document`
WHERE
dms_expire_date BETWEEN '2020-05-01' AND '2020-07-16'
AND dms_doc_name = ''
OR dms_category_id = ''
OR dms_subcategory_id=''";
//building the same query using CI
$this->db->select('*');
$this->db->from('dms_document');
$this->db->where('dms_expire_date >=', '2020-05-01');
$this->db->where('dms_expire_date <=', '2020-07-16');
$this->db->where('dms_doc_name', '');
$this->db->or_where('dms_category_id', '');
$this->db->or_where('dms_subcategory_id', '');
You can confirm the query this generates by running $this->db->_compile_select(); or $this->db->last_query(); (https://stackoverflow.com/a/6145021/1499877 for reference).
echo '<pre>';
var_dump($sql);
var_dump($this->db->_compile_select());
echo '</pre>';
die();

Symfony3-Doctrine : Order by Case When

I'm working on a Symfony 3.4 project.
I want to order a table by updated_at if exists (not null), by created_at if not.
In SQL, this works :
SELECT * FROM `contract`
ORDER BY
(CASE WHEN ISNULL(`updated_at`) THEN `created_at` ELSE `updated_at` END)
DESC
I tried a lot of things but I don't manage to make it work with Doctrine Query Builder.
First, I tried this (syntax error) :
$contracts = $em->createQuery(
'SELECT c
FROM AppBundle:Contract c
ORDER BY (CASE WHEN c.updatedAt = :update THEN c.createdAt ELSE c.updatedAt END) DESC')
->setParameter('update', NULL)
->getResult();
Then, I tried this according to this topic, but I have no result (no error) :
$contracts = $rp->createQueryBuilder('c')
->select('(CASE WHEN c.updatedAt != :update THEN 1 ELSE 0 END) AS HIDDEN orderDate')
->orderBy('orderDate', 'DESC')
->addOrderBy('c.createdAt', 'DESC')
->setParameter('update', NULL)
->getQuery()->getResult();
How can I sort my contracts by their updated date if they have been updated, or by their created date if they haven't been modified ?
If it helps, I use the DoctrineExtensions bundle for other queries, I saw IfNull and IfElse classes but I don't how to use them with my case.
After several attempts, I finally found the solution.
Use COALESCE : returns the first value not null in the list, so if A is null and B not null, then COALESCE(A,B) will return B.
$contracts = $rp->createQueryBuilder('c')
->select('c')
->addSelect('COALESCE(c.updatedAt,c.createdAt) AS HIDDEN orderDate')
->orderBy('orderDate', 'DESC')
->getQuery()->getResult();
No need to use the DoctrineExtensions bundle.

Using something similar to a CASE WHEN in Phalcon query builder

$fileQueryBuilder->columns(
[
"id" => "d.discovered_file_id",
"company_name" => "d.company_name"
]
);
This is the part of my query builder where I mention the column names to be selected/displayed. Can I handle the 'company_name' field to show it's value if it has one,
and something like 'Not available' if it's empty, in this part of the query builder itself? Is there a way of doing that, like using a CASE WHEN same as in SQL?
What I tried- CASE WHEN d.company_name IS NOT NULL THEN d.company_name ELSE 'Not available' END => d.company_name
, but this doesn't work.
Isn't IF better in this case?
IF(d.company_name IS NOT NULL, d.company_name, 'Not available') as company_name
Also PHQL only supports case syntac like this:
CASE column WHEN value THEN some expression ELSE some expression END

How to use the table columns instead of variables in QueryExpression::addCase()

In CakePHPs new ORM, you can use the QueryBuilder to build (in theory) any query.
I want to select the value of one of two columns, depending on another value. In a regular query, that can be done as follows:
SELECT IF(from_id = 1, to_id, from_id) AS other_id FROM messages;
I am trying to archive the same query using the QueryBuilder and QueryExpression::addCase()
$messagesQuery = $this->Messages->find('all');
$messagesQuery->select([
'other_id' => $messagesQuery->newExpr()->addCase(
$messagesQuery->newExpr()->add(['from_id' => $this->authUser->id]),
['to_id', 'from_id'],
['integer', 'integer']
)
]);
This does not work, as the passed values are not integers, but rather table columns containing integers.
Through trial and error (using the method add() again), I got the following:
$messagesQuery = $this->Messages->find('all');
$messagesQuery->select([
'other_id' => $messagesQuery->newExpr()->addCase(
$messagesQuery->newExpr()->add(['from_id' => $this->authUser->id]),
[
$messagesQuery->newExpr()->add(['to_id']),
$messagesQuery->newExpr()->add(['from_id'])
],
['integer', 'integer']
)
]);
This results in the following query:
SELECT (CASE WHEN from_id = 1 THEN to_id END) AS `other_id` FROM messages Messages
Now, the ELSE part is missing, although the CakePHP book states:
Any time there are fewer case conditions than values, addCase will automatically produce an if .. then .. else statement.
The examples in the CakePHP book are not very helpful in this case, as they only use static integers or strings as values, for example:
#SELECT SUM(CASE published = 'Y' THEN 1 ELSE 0) AS number_published, SUM(CASE published = 'N' THEN 1 ELSE 0) AS number_unpublished FROM articles GROUP BY published
$query = $articles->find();
$publishedCase = $query->newExpr()->addCase($query->newExpr()->add(['published' => 'Y']), 1, 'integer');
$notPublishedCase = $query->newExpr()->addCase($query->newExpr()->add(['published' => 'N']), 1, 'integer');
$query->select([
'number_published' => $query->func()->sum($publishedCase),
'number_unpublished' => $query->func()->sum($unpublishedCase)
])
->group('published');
Is there a way to get the method addCase to use the two table columns as values instead of just static values?
As it turns out, I was just one logical step short of the solution in my previous edit.
As the CakePHP book correctly states:
Any time there are fewer case conditions than values, addCase will automatically produce an if .. then .. else statement.
For that to work though, both the conditions and values have to be an array, even if there is only one condition. (This the CakePHP book does not state.)
This code:
$messagesQuery = $this->Messages->find('all');
$messagesQuery->select([
'other_id' => $messagesQuery->newExpr()->addCase(
[
$messagesQuery->newExpr()->add(['from_id' => $this->authUser->id])
],
[
$messagesQuery->newExpr()->add(['to_id']),
$messagesQuery->newExpr()->add(['from_id'])
],
['integer', 'integer']
)
]);
results in this query:
SELECT (CASE WHEN from_id = 1 THEN to_id ELSE from_id END) AS `other_id` FROM messages Messages
Eureka