How to join sub-query in laravel elequent - mysql

How do this query in laravel query builder ?
select * from products p join
(
select product_id,sum(qty) total_sales
from orders where qty !=0 group by product_id
)
s on p.id = s.product_id
order by s.total_sales desc

There are different ways are there to do the same thing. But you can do with Raw Expressions is very similar to your above code.

if you want to use Eloquent, Do this provided the orders table has product_id that is a foreign key from products
DB::table('products')->join('orders','products.id','orders.product_id')->where('orders.qty','!=',0)->get()

The first part for sub-query for joined part. It is joined with toSql() method inside the raw statement.
$subQuery = DB::table('orders')
->where('qty', '!=', DB::raw(0))
->groupBy('product_id')
->select('product_id', DB::raw('sum(qty) as total_sales'));
return DB::table('products as p')
->join(DB::raw('(' . $subQuery->toSql() . ') s'), 'p.id', '=', 's.product_id')
->orderByDesc('s.total_sales')
->get();
It prints the following sql;
SELECT *
FROM `products` AS `p`
INNER JOIN (
SELECT `product_id`, SUM(qty) AS total_sales
FROM `orders`
WHERE `qty` != 0
GROUP BY `product_id`
) s ON `p`.`id` = `s`.`product_id`
ORDER BY `s`.`total_sales` DESC

Related

Trying to convert a query to eloquent

I have the following SQL:
SELECT arv.*
FROM article_reference_versions arv
INNER JOIN (SELECT `order`,
Max(`revision`) AS max_revision
FROM article_reference_versions
WHERE `file` = '12338-230180-1-CE.doc'
GROUP BY `file`,
`order`) AS b
ON arv.order = b.order
AND arv.revision = b.max_revision
WHERE arv.file = '12338-230180-1-CE.doc'
I need to convert this to Eloquent, so that I can properly access the data in object form. I tried doing it as such,
$s = Models\EloArticleReferenceVersion::select(
'SELECT arv.*
FROM article_reference_versions arv
INNER JOIN (
SELECT `order`, max(`revision`) as max_revision
FROM article_reference_versions
WHERE file = ? group by `file`, `order`) AS b
ON
arv.order = b.order AND arv.revision = b.max_revision
WHERE arv.file = ?',
[
'12338-230180-1-CE.doc',
'12338-230180-1-CE.doc'
])->get();
dd($s);
But I'm running into a plethora of issues, one after another. I figured it'd be easier to just convert this into an eloquent query, looking for some help with this.
DB Query to Query using Eloquent.
$query = EloArticleReferenceVersion::query()
->join(DB::raw('( SELECT `order`,Max(`revision`) AS max_revision FROM article_reference_versions WHERE `file` = '12338-230180-1-CE.doc' GROUP BY `file`, `order`) as sub_table'), function($join) {
$join->on('sub_table.order', '=', 'article_reference_versions.order');
$join->on('sub_table.max_revision ', '=', 'article_reference_versions.revision');
})
->where('article_reference_versions.file', '=', '12338-230180-1-CE.doc' )
->get();
Not Tested

Add complex mysql query to seach model in Yii2

I want to use the default search and pagination in yii2. But the query is complex and I don't know how can I add it to the search model! This is the query:
SELECT p.*,po_sum,rpo_sum,so_sum
FROMproduct p
LEFT JOIN (
SELECT id,product_id , IF(sum(quantity) IS NULL, 0, sum(quantity)) AS po_sum
FROM purchase_order_products inner join purchase_order on purchase_order.id = purchase_order_products.purchase_order_id
Where purchase_order.status = 'Approved'
GROUP BY product_id )
subcount ON p.id = subcount.product_id
LEFT JOIN (
SELECT id,product_id , sum(quantity) AS rpo_sum
FROM return_purchase_order_products inner join return_purchase_order on return_purchase_order.id = return_purchase_order_products.purchase_order_id Where return_purchase_order.status = 'Approved'
GROUP BY product_id )
subcount2 ON p.id = subcount2.product_id
LEFT JOIN (
SELECT product_id , sum(quantity_ordered) AS so_sum
FROM sales_order_item inner join sales_order on sales_order.id = sales_order_item.sales_order_id Where sales_order.order_status = 'complete'
GROUP BY product_id )
subcount3 ON p.id = subcount3.product_id
order by po_sum DESC,rpo_sum DESC
Any help?
If you use MySql >= 5.7.7 the easiest way is to create a view with that query and use it in the tableName method.
You need that version of MySql because you cant use subquery in from clause during view creation in previous versions.

How do I select columns from results of union of tables using laravel query builder?

I am trying to convert my mysql statement into laravel query builder. In the following query I am trying to do GROUP BY and ORDER BY on the columns which are fetched from the result of UNION-ing three tables
staff_task_history_calibrate
staff_task_history_samples
staff_task_history_measures
Have you guys come across anything similar to it? If yes, could you please shed some light on my issue?
SELECT staff_name, task_id, task_type, task_desc, SUM(task_multiplier)
FROM
(
(
select CONCAT(S.first_name," ",S.last_name) as staff_name,
`STHC`.`task_id`, IFNULL(T.type, "-") as task_type,
`T`.`description` as `task_desc`,
STHC.task_multiplier
from `staff_task_history_calibrate` as `STHC`
inner join `staffs` as `S` on `STHC`.`staff_id` = `S`.`staff_id` inner join `tasks` as `T` on `STHC`.`task_id` = `T`.`id`
)
union
(
select CONCAT(S.first_name," ",S.last_name) as staff_name,
`STHS`.`task_id`, IFNULL(T.type, "-") as task_type,
`T`.`description` as `task_desc`,
STHS.task_multiplier
from `staff_task_history_samples` as `STHS`
inner join `staffs` as `S` on `STHS`.`staff_id` = `S`.`staff_id` inner join `tasks` as `T` on `STHS`.`task_id` = `T`.`id`
)
union
(
select CONCAT(S.first_name," ",S.last_name) as staff_name,
`STHM`.`task_id`, IFNULL(T.type, "-") as task_type,
`T`.`description` as `task_desc`,
STHM.task_multiplier
from `staff_task_history_measures` as `STHM`
inner join `staffs` as `S` on `STHM`.`staff_id` = `S`.`staff_id` inner join `tasks` as `T` on `STHM`.`task_id` = `T`.`id`
)
) combined_tables
GROUP BY staff_name, task_type, task_desc
ORDER BY staff_name, task_type, task_desc;
Difficult for me to test, but try:
// Build up the sub-queries
$sub1 = DB::table('staff_task_history_calibrate AS STHC')
->select(DB::raw(
'CONCAT(S.first_name," ",S.last_name) as staff_name',
'STHC'.'task_id', 'IFNULL(T.type, "-") as task_type',
'T'.'description' as 'task_desc', 'STHC.task_multiplier')
)->join('staffs AS S', 'STHC.staff_id', '=', 'S.staff_id')
->join('tasks AS T', 'STHC.task_id', '=', 'T.id');
$sub2 = DB::table('staff_task_history_samples AS STHS')
->select(DB::raw(
'CONCAT(S.first_name," ",S.last_name) as staff_name',
'STHS'.'task_id', 'IFNULL(T.type, "-") as task_type',
'T'.'description' as 'task_desc', 'STHS.task_multiplier')
)->join('staffs AS S', 'STHS.staff_id', '=', 'S.staff_id')
->join('tasks AS T', 'STHS.task_id', '=', 'T.id');
$sub3 = DB::table('staff_task_history_measures AS STHM')
->select(DB::raw(
'CONCAT(S.first_name," ",S.last_name) as staff_name',
'STHM'.'task_id', 'IFNULL(T.type, "-") as task_type',
'T'.'description' as 'task_desc', 'STHM.task_multiplier')
)->join('staffs AS S', 'STHM.staff_id', '=', 'S.staff_id')
->join('tasks AS T', 'STHM.task_id', '=', 'T.id');
// Add the unions
$allUnions = $sub1->union($sub2)->union($sub3); // (Check the documentation for Unions in Query Builder)
// Get the results
$results = DB::table(DB::raw("({$allUnions->toSql()}) as combined_tables"))
->select('staff_name, task_id, task_type, task_desc')
->sum('task_multiplier')
->mergeBindings($allUnions) // We need to retrieve the underlying SQL
->groupBy('staff_name')
->groupBy('task_type')
->groupBy('task_desc')
->orderBy('staff_name')
->orderBy('task_type')
->orderBy('task_desc')
->get();
(If what you have works, I'd keep it.)

How to write this (left join, subquery ) in Laravel 5.1?

How to write this query in Laravel 5.1:
SELECT p.id, p.title, p.created_at, p.updated_at, u.name, COALESCE(c.comments_count, 0) AS comments_count, COALESCE(pl.status_sum, 0) AS status_sum
FROM posts p
LEFT OUTER JOIN users u ON u.id = p.user_id
LEFT OUTER JOIN (
SELECT pl.post_id, SUM(pl.status) AS status_sum
FROM postslikes pl
GROUP BY pl.post_id
) pl ON pl.post_id = p.id
LEFT OUTER JOIN (
SELECT c.post_id, COUNT(*) as comments_count
FROM comments c
GROUP BY c.post_id
) c ON c.post_id = p.id ORDER BY comments_count DESC
I need it for Pagination. I can perform this query raw without any problems but the manually paginator gives always the same results:
http://laravel.com/docs/5.1/pagination#manually-creating-a-paginator
The same problem as here: http://laravel.io/forum/07-22-2015-laravel-51-manual-pagination-not-working-as-expected
My attempt without success:
DB::table( 'posts' )
->select( 'posts.id', 'posts.title', 'posts.created_at', 'posts.updated_at', 'users.name', DB::raw( 'COALESCE( comments.body, 0 ), COALESCE( postslikes.status, 0 )' ) )
->leftJoin( 'users', 'users.id', '=', 'posts.user_id' )
->leftJoin( DB::raw( 'SELECT postslikes.post_id, SUM( postslikes.status ) FROM postslikes GROUP BY postslikes.post_id' ), function( $join )
{
$join->on( 'postslikes.post_id', '=', 'post.id' );
})
->leftJoin( DB::raw( 'SELECT comments.post_id, COUNT(*) FROM comments GROUP BY comments.post_id' ), function( $join )
{
$join->on( 'comments.post_id', '=', 'post.id' );
})
->get();
I think the problem is comments_count and status_sum?
Thanks!
To use subqueries with Laravel's query builder, you should add it to the join as follows:
->leftJoin(DB::raw("(SELECT [...]) AS p"), 'p.post_id', '=', 'posts.id')
It's also better to create an alias for calculated fields, as you did in your raw query:
COUNT(*) AS count
Despite this changes, unless I'm wrong, you can start by making your query simpler. Drop the subqueries, this way:
SELECT
p.id,
p.title,
p.created_at,
p.updated_at,
u.name,
COUNT(c.id) AS comments_count,
COALESCE(SUM(pl.status), 0) AS status_sum
FROM
posts p
LEFT OUTER JOIN
users u
ON
u.id = p.user_id
LEFT OUTER JOIN
postslikes pl
ON
pl.post_id = p.id
LEFT OUTER JOIN
comments c
ON
c.post_id = p.id
ORDER BY
comments_count DESC
GROUP BY
p.id
Then, with this new query, you can use Laravel to build it:
DB::table('posts')
->select([
'posts.id',
'posts.title',
'posts.created_at',
'posts.updated_at',
'users.name',
DB::raw('COUNT(comments.id) AS comments_count'),
DB::raw('COALESCE(SUM(postslikes.status), 0) AS status_sum'),
])
->leftJoin('users', 'users.id', '=', 'posts.user_id')
->leftJoin('comments', 'comments.post_id', '=', 'posts.id')
->leftJoin('postslikes', 'postslikes.post_id', '=', 'posts.id')
->orderBy('comments_count', 'DESC')
->groupBy('posts.id')
->get();
Note that I'm assuming you have a column named id in your comments table that is the primary key.

Filter MySQL result set by result of inner select

I have an MySQL statement that performs an inner SELECT and returns the result as a pseudo column. I’d like to use the result of this pseudo column in my WHERE clause. My current SQL statement looks like this:
SELECT
product.product_id,
product.range_id,
product.title,
product.image,
product.image_text,
product.friendly_url,
attribute.comfort_grade_id,
category.category_id,
category.category AS category_name,
category.friendly_url AS category_friendly_url,
(SELECT price_now FROM product_bedding_sizes AS size
WHERE size.product_id = product.product_id
ORDER BY size.price_now ASC LIMIT 1) AS price
FROM
products AS product
LEFT JOIN
categories AS category ON product.category_id = category.category_id
LEFT JOIN
product_bedding_attributes AS attribute
ON product.product_id = attribute.product_id
$where
$order
LIMIT
?,?
However, I get the following error message when running the query:
#1054 - Unknown column 'price' in 'where clause'
How can I get around this and actually use the value of price in my WHERE clause?
The WHERE clause is evaluated before the SELECT clause, so it doesn't say the alias name. You have to do the filter by the WHERE clause in an outer query like this:
SELECT *
FROM
(
SELECT
product.product_id,
product.range_id,
product.title,
product.image,
product.image_text,
product.friendly_url,
attribute.comfort_grade_id,
category.category_id,
category.category AS category_name,
category.friendly_url AS category_friendly_url,
(SELECT price_now
FROM product_bedding_sizes AS size
WHERE size.product_id = product.product_id
ORDER BY size.price_now ASC
LIMIT 1) AS price
FROM
...
) AS sub
WHERE price = ... <--- here it can see the price alias.
See this for more details:
My SQL Query Order of Operations.
Or: You can join that table, instead of a correlated subquery like this:
SELECT
product.product_id,
product.range_id,
product.title,
product.image,
product.image_text,
product.friendly_url,
attribute.comfort_grade_id,
category.category_id,
category.category AS category_name,
category.friendly_url AS category_friendly_url,
size.price_now
FROM
products AS product
LEFT JOIN
(
SELECT product_id, MIN(price_now) AS price
FROM product_bedding_sizes
GROUP BY product_id
) AS size ON size.product_id = product.product_id
LEFT JOIN
categories AS category ON product.category_id = category.category_id
LEFT JOIN
product_bedding_attributes AS attribute ON product.product_id = attribute.product_id
$where price = ----;
Try using a variable:
#price:= (SELECT price_now
FROM product_bedding_sizes AS size
WHERE size.product_id = product.product_id
ORDER BY size.price_now ASC LIMIT 1) AS price;
Then reference it as
WHERE #price > 9000;
if you have
WHERE price > 0 AND price` <= 199`
in your where clause
then try do this with HAVING clause
like that
$HAVING
//-- where $having = HAVING price > 0 AND price <= 199