Laravel: get the most purchases product for current user - mysql

I have 3 tables here (order_lists, order_details, products)
Table order_lists contain the following user_id
Table order_details contain the following order_lists_id product_id
Table products contain all the products details.
the three tables are related to each others in models.
I need to select the most purchases items from table order_details based this user_id.
I made very basic Eloquent query which will select 6 products for this user but stuck on how to get the most purchases product
$mostPurchases = OrderList::where( 'user_id', Auth::id() )
->with( [
'orderDetails' => function ( $query ) {
$query->with( 'productId' );
}
] )->take( 6 )->get();
in order_details I need to count the user most purchases product.
UPDATE
As #party-ring suggest to use DB::raw query this is the final query looks like.
$usId = Auth::id();
$mostPurchases = DB::select( DB::raw( 'SELECT product_id, count(*) from order_details
LEFT JOIN order_lists ON order_details.order_lists_id = order_lists.id
WHERE order_lists.user_id = ' . $usId . '
group by product_id
order by count(*) DESC
LIMIT 6' ) );
Update
DB::table( 'products' )
->where( 'soft_delete', '!=', 1 )
->leftJoin( 'order_details', 'products.id', '=', 'order_details.product_id' )
->select( DB::raw( 'count(*) as pro_count, product_id' ) )
->leftJoin( 'order_lists', 'order_details.order_lists_id', '=', 'order_lists.id' )
->where( 'order_lists.user_id', '=', $usId )
->orderBy( 'pro_count', 'DESC' )
->groupBy( 'product_id' )
->take( 6 )
->get();
but still I got only the pro_count, and product_id
{
"pro_count": 22,
"product_id": 733
},
{
"pro_count": 15,
"product_id": 85
},
I need the product details within the response.

I would go about this by using join and DB::raw as I have found it can be more efficient than Eloquent when dealing with large datasets opposed to a function query - also I find it a little easier to build up my queries in this way.
select(DB::raw('SELECT product_id, count(*) from order_details
LEFT JOIN `order_lists` ON order_details.order_lists_id = order_lists.id
WHERE order_lists.user_id = :authUser
group by product_id
order by count(*) DESC
LIMIT 6', ['authUser => Auth::id()']);
You might need to play around with this a little as I don't know if you will need to specify the table names in your query.
This is adding together your order_lists and order_details tables, limiting it to that of the auth user, grouping it by the product id, ordering it by the count of those product purchases for the user and limiting it to 6 results.

Related

Laravel query to join different tables based on a condition

How to join multiple tables based on a condition.
I have 3 tables.
Transactions table
advertisement table
offerrequests table
Transaction table has relation with advertisement and offerrequests table by the fields - is_sell and post_id
if is_sell = 1 then
// post id is id in advertisement table
if is_sell is 0 then
// post id is id in offerrequests table
column country is only presnt in advertisemnt and offerrequests table.
so i need to join the tables to get country for each transaction
I got the result using mysql query like :
SELECT transactions.id , IF( transactions.is_sell = '1', advertisements.country, offerrequests. country ) AS country
FROM transactions
LEFT JOIN advertisements ON ( advertisements.id = transactions.post_id )
LEFT JOIN offerrequests ON ( offerrequests.id = transactions.post_id );
Can anyone help me to get the laravel query corresponding to same
You can use
$transactions = DB::table('transactions')
->select('transactions.id', DB::raw("IF(transactions.is_sell = '1', advertisements.country, offerrequests.country) as country"))
->leftJoin('advertisements', function ($join) {
$join->on('advertisements.id', '=', 'transactions.post_id');
})
->leftJoin('offerrequests', function ($join) {
$join->on('offerrequests.id', '=', 'transactions.post_id');
})
->get();

Get 50k rows faster with subqueries - Laravel 5.6

The below query is to get the candidate's details from the table which has 50k rows. Including jobs, regions, and employment types. A candidate has basic details with employment type, jobs, regions are in another table with foreign key relation.
$candidates = DB::table('candidates')
->join('role_users', function($join){
$join->on('candidates.user_id', '=', 'role_users.user_id');
$join->where('role_users.role_id', 6);
})
->join('candidate_statuses', 'candidates.candidate_status_id', '=', 'candidate_statuses.id')
->join('employment_types', 'candidates.employment_types_id', '=', 'employment_types.id')
->select(
'candidates.id',
'candidates.user_id',
'candidates.candidate_code',
'candidates.full_name as name',
'employment_types.title AS employment_type',
DB::raw("(SELECT GROUP_CONCAT(candidate_jobs.job_id SEPARATOR ',') FROM candidate_jobs WHERE candidate_jobs.candidate_id = candidates.id) as job_ids"),
DB::raw("(SELECT GROUP_CONCAT(regions.name SEPARATOR ',') FROM candidate_regions INNER JOIN regions ON regions.id=candidate_regions.region_id WHERE candidate_regions.candidate_id = candidates.id) as regions"),
'role_users.email',
'role_users.login_at',
'candidates.is_deleted')
->where('candidates.candidate_status_id', '!=' , 6)
->where('candidates.is_deleted', $request->is_deleted)
->orderBy('candidates.first_name')
->groupBy('candidates.id')
->paginate(10);
}
select `candidates`.`id`, `candidates`.`user_id`, `candidates`.`candidate_code`,
`candidates`.`first_name`,
`candidates`.`last_name`, `candidates`.`full_name` as `name`,
`candidates`.`profile_img`, `candidates`.`candidate_status_id`,
`candidates`.`employment_types_id`,
`employment_types`.`title` as `employment_type`,
(
SELECT GROUP_CONCAT(candidate_jobs.job_id SEPARATOR ',')
FROM candidate_jobs
WHERE candidate_jobs.candidate_id = candidates.id
) as job_ids,
(
SELECT GROUP_CONCAT(regions.name SEPARATOR ',')
FROM candidate_regions
INNER JOIN regions ON regions.id=candidate_regions.region_id
WHERE candidate_regions.candidate_id = candidates.id
) as regions,
`candidates`.`formatted_mobile_number`, `candidates`.`place`,
`candidates`.`post_code`, `role_users`.`email`, `role_users`.`login_at`,
`role_users`.`email`, `candidates`.`has_access`, `candidates`.`is_deleted`
from `candidates`
inner join `role_users` ON `candidates`.`user_id` = `role_users`.`user_id`
and `role_users`.`role_id` = ?
inner join `candidate_statuses`
ON `candidates`.`candidate_status_id` = `candidate_statuses`.`id`
inner join `employment_types`
ON `candidates`.`employment_types_id` = `employment_types`.`id`
where (`candidates`.`candidate_status_id` in (?))
and `candidates`.`candidate_status_id` != ?
and `candidates`.`is_deleted` = ?
group by `candidates`.`id`
order by `candidates`.`first_name` asc
It takes 2/ 3 seconds to get the result in my local machine but in production takes too long time.
Can anyone please help?
It seems like the second part is unnecessary:
`candidates`.`candidate_status_id` in (?))
and `candidates`.`candidate_status_id` != ?
Making these match avoids an extra pass over the results
group by `first_name`, `id`
order by `first_name` asc, id
Possibly helpful indexes:
candidates: INDEX(candidate_status_id, is_deleted, first_name, id, user_id)
role_users: INDEX(user_id, email, login_at, role_id)
candidate_jobs: INDEX(candidate_id, job_id)
candidate_regions: INDEX(candidate_id, region_id)

Mysql/Laravel : Using left join to fetch latest created at date of any employee

I'm trying to get all sections and their latest created_at using left join
I'm using below query
select es.*, sul.created_at as created_at from section as es
left join section_update_logs as sul on es.id = sul.section_id
where sul.emp_id = 3
group by es.id
order by es.id, sul.created_at asc;
the obvious output of the query is only show that records where emp_id is 3, but i want all the records of section table, if there is no employee record in section_update_logs there should be a null/empty created_at.
Desired output is :
all rows of table section
created_at date if sul.section_id = section.id and emp_id = of particular employee.
Laravel Query :
$result = DB::table("section as es")
->select('es.*', 'sul.created_at')
->leftJoin('section_update_logs as sul', 'es.id', '=', 'sul.section_id')
->orderBy('es.weight')
->orderBy('sul.created_at')
->orWhere('sul.emp_id', $emp_id)
->groupBy('es.id')
->get();
Please help
You can add a closure to the leftJoin function to add more conditions.
$result = DB::table("section as es")
->select('es.*', DB::raw('MAX(sul.created_at)'))
->leftJoin('section_update_logs as sul', function ($join) use ($emp_id) {
$join->on('es.id', '=', 'sul.section_id')
->where('sul.emp_id', $emp_id);
})
->orderBy('es.weight')
->orderBy('sul.created_at')
->groupBy('es.id')
->get();
This way, the filter on sul.emp_id is only validated when a relation is found.
To get the same result with your sql query, add a second condition to your join using AND:
select es.*, MAX(sul.created_at) as created_at from section as es
left join section_update_logs as sul on es.id = sul.section_id AND sul.emp_id = 3 -- HERE
group by es.id
order by es.id, sul.created_at asc;
you need to use first since you are to display the result of a particular employee
$search = $request->input('id');
$data['result'] = DB::table("section")
->where('emp_id', $search)
->leftJoin('section_update_logs', 'section.id', '=', 'section_update_logs.section_id')
->orderBy('section_update_logs.created_at','desc')
->first();
return view('viewblade', $data)
on your view
{{ $result ->name }}
...

Get the last record in a belongsToMany association

I have a belongsToMany association between Appointment and Status models. The below query returns all of the statuses and I want to alter it to pull the last status assigned to the appointment.
$query = Appointment::query();
$query->with('statuses');
$query->with("agent");
$query->with("instruction_type");
$query->with("sign_up_customer");
$table = Datatables::of($query);
I have tried altering the query with this but it doesn't work.
$query->with('statuses')->latest();
This is my raw query:
select * from `users` where `users`.`id` = '1' limit 1
select count(*) as aggregate from (select '1' as `row_count` from `appointments`) count_row_table
select * from `appointments` limit 100 offset 0
select `statuses`.*, `appointment_status`.`appointment_id` as `pivot_appointment_id`, `appointment_status`.`status_id` as `pivot_status_id` from `statuses` inner join `appointment_status` on `statuses`.`id` = `appointment_status`.`status_id` where `appointment_status`.`appointment_id` in ('2') order by `created_at` desc
select * from `agents` where `agents`.`id` in ('1')
select * from `instruction_types` where `instruction_types`.`id` in ('1')
select * from `organisations` where `organisations`.`id` in ('1')
So this works, but it runs two queries on Statuses
$query = Appointment::with(['statuses' => function ($query) {
$query->latest()->first();
}]);
$query->with("agent");
$query->with("instruction_type");
$query->with("sign_up_customer");
$table = Datatables::of($query);
In order to get the latest record from the database, you should have a created_at column to reach that. In this case, you can do something like that:
Appointment::with(['statuses' => function ($query) {
$query->orderBy('created_at', 'desc')->first();
}])->get();
try
$query->with(["statuses"=>function($q){
$q->latest();
}])->get()
not tested but guess it should work

Matching all values in IN clause

Is there a way to ensure all values in an IN clause are matched?
Example:
I can use IN as: IN (5,6,7,8).
I need it to work like an AND across multiple rows.
UPDATE:
I need this to list companies from db that fit specified parameters. Companies and taxonomy are MANY TO MANY relation. I'm using Yii framework. And this is the code of my controller:
public function actionFilters($list)
{
$companies = new CActiveDataProvider('Company', array(
'criteria' => array(
'condition'=> 'type=0',
'together' => true,
'order'=> 'rating DESC',
'with'=>array(
'taxonomy'=>array(
'condition'=>'term_id IN ('.$list.')',
)
),
),
));
$this->render('index', array(
'companies'=>$companies,
));
}
You can do something like this:
select ItemID
from ItemCategory
where CategoryID in (5,6,7,8) <-- de-dupe these before building IN clause
group by ItemID
having count(distinct CategoryID) = 4 <--this is the count of unique items in IN clause above
If you provide your schema and some sample data, I can provide a more relevant answer.
SQL Fiddle Example
If you want to find the items that have all of a specific set of CategoryIDs and no others, this is one way you can approach it:
select a.ItemID
from (
select ItemID, count(distinct CategoryID) as CategoryCount
from [dbo].[ItemCategory]
where CategoryID in (5,6,7,8)
group by ItemID
having count(distinct CategoryID) = 4
) a
inner join (
select ItemID, count(distinct CategoryID) as CategoryCount
from [dbo].[ItemCategory]
group by ItemID
) b on a.ItemID = b.ItemID and a.CategoryCount = b.CategoryCount
SQL Fiddle Example
If you prefer, you could do it with a subquery:
select ItemID
from ItemCategory
where ItemID in (
select ItemID
from ItemCategory
where CategoryID in (5,6,7,8)
group by ItemID
having count(distinct CategoryID) = 4
)
group by ItemID
having count(distinct CategoryID) = 4
SQL Fiddle Example
SELECT ItemID
FROM ItemCategory
WHERE (
(CategoryID = 5) OR
(CategoryID = 6) OR
(CategoryID = 7) OR
(CategoryID = 8)
)
GROUP BY ItemID
HAVING COUNT(DISTINCT CategoryID) = 4