Write union query in codeigniter style - codeigniter

How can I write the following query in Codeigniter style.
SELECT COUNT(`id`) AS reccount
FROM
(SELECT `id` FROM table1
WHERE tid= '101' AND `status` = 1
UNION ALL
SELECT `id` FROM table2
WHERE tid= '101' AND `status` = 1
UNION ALL
SELECT `id` FROM table3
WHERE tid= '101' AND `status` = 1) t
I have used the following way to execute it.
Is it the only correct way or do you have any suggestion to improve it?
$q = $this->db->query(SELECT COUNT(`id`) AS reccount
FROM
(SELECT `id` FROM table1
WHERE tid= '101' AND `status` = 1
UNION ALL
SELECT `id` FROM table2
WHERE tid= '101' AND `status` = 1
UNION ALL
SELECT `id` FROM table3
WHERE tid= '101' AND `status` = 1) t ");

Since CodeIgniter 3 it's been introduced in Active Record the function get_compiled_select() that gives the query string without actually executing the query.
This allows #MDeSilva method to use less resources, being adapted as follows:
function get_merged_result($ids){
$this->db->select("column");
$this->db->distinct();
$this->db->from("table_name");
$this->db->where_in("id",$model_ids);
$query1 = $this->db->get_compiled_select(); // It resets the query just like a get()
$this->db->select("column2 as column");
$this->db->distinct();
$this->db->from("table_name");
$this->db->where_in("id",$model_ids);
$query2 = $this->db->get_compiled_select();
$query = $this->db->query($query1." UNION ".$query2);
return $query->result();
}

You can use CI to generate a union query. However, latest versions made this much harder than before.
DB has a method called _compile_select, in previous versions of CI it was public, however now it is protected so you can't just call $this->db->_compile_select() from your controller. In order to do this properly one could:
Create custom loader class to be able to extend core/database classes (i.e. load MY_DB_active_record instead of CI_DB_active_record).
Create custom activerecord class, with just one method:
public function compile_select() {
return $this->_compile_select();
}
In your controller, create all necessary queries, compile them into a string array using our public method compile_select()
Join the array into single query: '(' . implode(') UNION (', $queries) . ')'. You can also wrap this into a separate method inside your custom AR class.

function get_merged_result($ids){
$this->db->select("column");
$this->db->distinct();
$this->db->from("table_name");
$this->db->where_in("id",$model_ids);
$this->db->get();
$query1 = $this->db->last_query();
$this->db->select("column2 as column");
$this->db->distinct();
$this->db->from("table_name");
$this->db->where_in("id",$model_ids);
$this->db->get();
$query2 = $this->db->last_query();
$query = $this->db->query($query1." UNION ".$query2);
return $query->result();
}

Related

Which is the best efficient way to get many-to-many relation count in laravel?

I have students and subjects table in many-to-many relation (pivot table is student_subject).
Student Model
public function subjects()
{
return $this->belongsToMany(Subject::class, 'student_subject');
}
Subject Model
public function students()
{
return $this->belongsToMany(Student::class, 'student_subject');
}
Here I want the particular student subjects counts. I tried the below methods it's working fine but I want the best efficient way for this purpose.
1.
$student = Student::find($id);
$subject_count = $student->subjects()->count();
I checked the SQL query through laravel debuger it shows as below
select * from `students` where `students`.`id` = '10' limit 1
select count(*) as aggregate from `subjects` inner join `student_subject` on `subjects`.`id` = `student_subject`.`subject_id` where `student_subject`.`student_id` = 10 and `subjects`.`deleted_at` is null
$student = Student::withCount('subjects')->find($id);
$subject_count = $student->subjects_count;
I checked the SQL query through laravel debuger it shows as below
select `students`.*, (select count(*) from `subjects` inner join `student_subject` on `subjects`.`id` = `student_subject`.`subject_id` where `students`.`id` = `student_subject`.`student_id` and `subjects`.`deleted_at` is null) as `subjects_count` from `students` where `students`.`id` = '10' limit 1
$student = Student::find($id);
$subject_count = $student->loadCount('subjects')->subjects_count;
I checked the SQL query through laravel debuger it shows as below
select * from `students` where `students`.`id` = '10' limit 1
select `id`, (select count(*) from `subjects` inner join `student_subject` on `subjects`.`id` = `student_subject`.`subject_id` where `students`.`id` = `student_subject`.`student_id` and `subjects`.`deleted_at` is null) as `subjects_count` from `students` where `students`.`id` in (10)
$student = Student::find($id);
$subject_count = DB::table('student_subject')->where('student_id', $student->id)->count();
I checked the SQL query through laravel debuger it shows as below
select * from `students` where `students`.`id` = '10' limit 1
select count(*) as aggregate from `student_subject` where `student_id` = 10
According to the above ways which one is best and why? or if any different best way also there?
Doing relation()->count() is probably faster.
But if all you need is the count, withCount() should be better in terms of memory consumption.

Why eloquent separates queries in eager loading?

For example I have two queries:
return $this->model->where('closed_at', null)
->whereHas('users', function ($query) {
$query->where('id', '=', auth()->user()->id);
})
->with(['product:id,title', 'in_works' => function ($query) {
$query->where('user_id', '=', auth()->user()->id);
}])
->get();
its work good and return 1 query with join(eager loading):
select * from `order` where `closed_at` is null and exists (select * from `user` inner join `user_order` on `user`.`id` = `user_order`.`user_id` where `order`.`id` = `user_order`.`order_id` and `id` = ?)
but similar query
return $this->model->where('closed_at', null)
->with(['product:id,title', 'in_works' => function ($query) {
$query->where('closed_at', '=', null);
}])->get();
return 3 queries, why?
select * from `order` where `closed_at` is null
select `id`, `title` from `product` where `product`.`id` in (1, 2)
select * from `in_work` where `in_work`.`order_id` in (1, 2) and `closed_at` is null
Each relationship added to a model via eager loading (i.e. using with) is queried via an additional query.
Here's what you have:
Model query
select * from `order` where `closed_at` is null -- Model
with product:id,title query
select `id`, `title` from `product` where `product`.`id` in (1, 2)
with in_works query
select * from `in_work` where `in_work`.`order_id` in (1, 2) and `closed_at` is null
Note that in cases (2) and (3) (1,2) corresponds to the identifiers of order that were obtained from the first query.
whereHas will further refine the original model query and therefore affect the total results returned. In your case the following is the result of the whereHas('users',...) query:
select * from `order` where `closed_at` is null and exists (select * from `user` inner join `user_order` on `user`.`id` = `user_order`.`user_id` where `order`.`id` = `user_order`.`order_id` and `id` = ?)
When a model query does not actually yield any results then no relationship queries are actually performed which is what is happening in this case.

Using same table in subquery

I am failing to convert next SQL code into laravel eloquent:
SELECT t1.template, t1.created_at
FROM sent_emails t1
where created_at = (
select max(created_at) from sent_emails t2 where t2.template = t1.template
)
group by t1.created_at, t1.template
or:
SELECT t1.template, t1.created_at
FROM sent_emails t1
JOIN
(
SELECT Max(created_at) date, template
FROM sent_emails
GROUP BY template
) AS t2
ON t1.template = t2.template
AND t1.created_at = t2.date
group by t1.created_at, t1.template
Both queries return same data set. Creating subquery in separate variable is not an option as I need multiple values to be returned from it.
I also don't know how can I set alias name if I create table using models (and not using DB::), so this is my unsuccessful try:
$sent_emails = SentEmail::where('created_at', function($query) {
SentEmail::where('template', 't1.template')->orderBy('created_at', 'desc');
})->groupBy('template', 'created_at')->get(['template', 'created_at']);
You query should be something like this (I'm not at a computer to test this, so it may require further editing)
$sent_emails = SentEmail::where('created_at', function($query) {
$query->where('created_at', SentEmail::->whereColumn('column_name', 'table.column_name')->orderBy('created_at', 'desc')->max('created_at'));
})->groupBy('template', 'created_at')->get(['template', 'created_at']);

Raw Laravel query as a collection with conditions

I've got a fairly complex query that I'd rather not write in Eloquent so I've written it raw. The only problem is that I need to be able to search/filter through the data using the front-end this query is connected to.
This what I've tried but I'm getting an "Call to a member function get() on null" error.
Here's my code:
$report = collect(DB::connection('mysql2')->select("SELECT
t2.holder,
t2.merchantTransactionId,
t2.bin,
t2.last4Digits,
t3.expDate,
(CASE WHEN t3.expDate < CURDATE() THEN 'Expired'
WHEN t3.expDate > CURDATE() THEN 'Due to expire' END) AS expInfo,
t2.uuid
FROM transactions AS t2
INNER JOIN (
SELECT t1.uuid, t1.holder, t1.bin, t1.last4Digits, LAST_DAY(CONCAT(t1.expiryYear, t1.expiryMonth, '01')) AS expDate
FROM transactions t1
JOIN (SELECT t1.merchant_access
FROM total_control.users,
JSON_TABLE(merchant_access, '$[*]' COLUMNS (
merchant_access VARCHAR(32) PATH '$')
) t1
WHERE users.id = :userId
) AS t2
ON t1.merchantUuid = t2.merchant_access
WHERE t1.paymentType = 'RG'
AND t1.status = 1
) t3
ON t2.uuid = t3.uuid
WHERE t3.expDate BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)
GROUP BY t2.holder, t2.bin, t2.last4Digits
ORDER BY t2.holder ASC", ['userId' => $request->userId]))
->when($request->search['holder'], function($q) use ($request) {
$q->where('t2.holder', 'LIKE', '%'.$request->search['holder'].'%');
})->get();
return $report;

Using Mysql WHERE IN clause in codeigniter

I have the following mysql query. Could you please tell me how to write the same query in Codeigniter's way ?
SELECT * FROM myTable
WHERE trans_id IN ( SELECT trans_id FROM myTable WHERE code='B')
AND code!='B'
You can use sub query way of codeigniter to do this for this purpose you will have to hack codeigniter. like this
Go to system/database/DB_active_rec.php
Remove public or protected keyword from these functions
public function _compile_select($select_override = FALSE)
public function _reset_select()
Now subquery writing in available
And now here is your query with active record
$this->db->select('trans_id');
$this->db->from('myTable');
$this->db->where('code','B');
$subQuery = $this->db->_compile_select();
$this->db->_reset_select();
// And now your main query
$this->db->select("*");
$this->db->where_in("$subQuery");
$this->db->where('code !=', 'B');
$this->db->get('myTable');
And the thing is done. Cheers!!!
Note : While using sub queries you must use
$this->db->from('myTable')
instead of
$this->db->get('myTable')
which runs the query.
Watch this too
How can I rewrite this SQL into CodeIgniter's Active Records?
Note : In Codeigntier 3 these functions are already public so you do not need to hack them.
$data = $this->db->get_where('columnname',array('code' => 'B'));
$this->db->where_in('columnname',$data);
$this->db->where('code !=','B');
$query = $this->db->get();
return $query->result_array();
Try this one:
$this->db->select("*");
$this->db->where_in("(SELECT trans_id FROM myTable WHERE code = 'B')");
$this->db->where('code !=', 'B');
$this->db->get('myTable');
Note: $this->db->select("*"); is optional when you are selecting all columns from table
try this:
return $this->db->query("
SELECT * FROM myTable
WHERE trans_id IN ( SELECT trans_id FROM myTable WHERE code='B')
AND code!='B'
")->result_array();
Is not active record but is codeigniter's way http://codeigniter.com/user_guide/database/examples.html
see Standard Query With Multiple Results (Array Version) section
you can use a simpler approach, while still using active record
$this->db->select("a.trans_id ,a.number, a.name, a.phone")
$this->db->from("Name_Of_Your_Table a");
$subQueryIn = "SELECT trans_id FROM Another_Table";
$this->db->where("a.trans_id in ($subQueryIn)",NULL);

Resources