I have a table that I want to select product_name with lowest/min price the following:
product_name | price
Cat | 12
Dog | 21
Cat | 14
Dog | 20
Fish | 10
Fish | 3
THE DESIRE Output should be
Cat | 12
Dog | 20
Fish | 3
BELOW IS MY SQL QUERY
$products = DB::table('products')
->orderBy('products.products_price', 'asc')
->groupBy('products.products_name')
->get();
When I used this script, it only shows the highest/max price, not the lowest price
You need an aggregate instead of ordering. For Laravel, that means passing in the columns along with a DB::raw:
$products = DB::table('products')
->orderBy('products.products_price', 'asc')
->groupBy('products.products_name')
->select(['product_name', DB::raw('min(price) as price')])
->get();
Edit for ID
Taking off the answer here : SQL: Group by minimum value in one field while selecting distinct rows the mysql query would be
SELECT p1.*
FROM products p1 INNER JOIN
(
SELECT product_name, MIN(price) AS as min_price
FROM products
GROUP BY product_name
) p2 ON p1.product_name = p2.product_name AND p1.price = p2.min_price
Now we have to convert it to Query Builder
$products = DB::table('products AS p1')
->join(DB::raw('(
SELECT product_name, MIN(price) AS as min_price
FROM products
GROUP BY product_name
) AS p2'),
function($join)
{
$join->on('p1.product_name', '=', 'p2.product_name');
$join->on('p1.price', '=', 'p2.min_price');
})
->get(['p1.id', 'p1.product_name', 'p1.price']);
This has not been tested, so I hope it works
Problem:
You are not defining that the price attribute should be aggregated to a minimum.
Solution:
If you want the max price you need to select the MIN() aggregation.
You can do this by using ->selectRaw('MIN(price) as max_price').
Please Note:
If you want other attributes also selected simply add them comma separated.
->selectRaw('name, MAX(price) as max_price')
#edit
Do you still use the oderBy? If not, try it with orderBy('products.products_price', 'ASC')
Related
Table Data:
sales_products table data:
product_id
quantity
sales price
1
4
300
1
5
300
2
3
400
2
2
400
3
3
100
products table
id
product_name
1
product_x
2
product_y
3
product_z
Expected Query Output
product_name
Quantity
Total_Price
product_x
9
2700
product_y
5
2000
product_z
3
300
I was trying with following Query and didn't get expected output
$invoiceDetails = DB::table('products')
->join('sales_products', 'sales_products.product_id', '=', 'products.id')
->select(
'products.product_name',
'sales_products.quantity',
'sales_products.sales_price',
DB::raw('(sales_products.quantity * sales_products.sales_price) as total')
)
->where('sales_products.invoice_id', '=', $id)
->get();
SELECT p.product_name, sp.Quantity, sp.Total_price
FROM `products` p
INNER JOIN (
SELECT SUM(quantity) AS Quantity,
product_id,
SUM(sales_price*quantity) AS Total_price
FROM `sales_products`
GROUP BY product_id
) AS sp
ON p.id = sp.product_id
This query above should help. How you should merge queries is by using GROUP BY. Using GROUP BY on the first table wrt to the product_id helps us sum up the columns accordingly.
I am using Laravel 5.4's Query Builder to perform a series of leftJoins on three tables. Here are my tables:
items
id type title visibility status created_at
-- ---- ----- ---------- ------ ----------
1 1 This is a Title 1 1 2017-06-20 06:39:20
2 1 Here's Another Item 1 1 2017-06-24 18:12:13
3 1 A Third Item 1 1 2017-06-26 10:10:34
count_loves
id items_id user_id
-- ------- -------
1 1 2
2 1 57
3 1 18
count_downloads
id items_id user_id
-- ------- -------
1 1 879
2 1 323
And here is the code I am running in Laravel:
$items_output = DB::table('items')
->leftJoin('count_loves', 'items.id', '=', 'count_loves.items_id')
->leftJoin('count_downloads', 'items.id', '=', 'count_downloads.items_id')
->where('items.visibility', '=', '1')
->where('items.status', '=', '1')
->orderBy('items.created_at', 'desc')
->select('items.*', DB::raw('count(count_loves.id) as loveCount'), DB::raw('count(count_downloads.id) as downloadCount'))
->groupBy('items.id')
->get();
When I return the results for this query, I am getting the following counts:
count_loves: 6
count_downloads: 6
As you can see, the actual count values should be:
count_loves: 3
count_downloads: 2
If I add another entry to the count_loves table, as an example, the totals move to 8. If I add another entry to the count_downloads table after that, the totals jump to 12. So, the two counts are multiplying together.
If I die and dump the query, here's what I get:
"query" => "select 'items'.*, count(count_loves.id) as loveCount,
count(count_downloads.id) as downloadCount from 'items' left join
'count_loves' on 'items'.'id' = 'count_loves'.'items_id' left join
'count_downloads' on 'items'.'id' = 'count_downloads'.'items_id'
where 'items'.'visibility' = ? and 'items'.'status' = ? group by
'items'.'id' order by 'items'.'created_at' desc"
How do I perform multiple leftJoins using Query Builder and count on several tables to return the proper sums?
NOTE:
This is intended as a HELP answer not the total absolute answer but I could not write the code in a comment. I am not asking for votes (for those who just can't wait to downvote me). I have created your tables and tried a UNION query on raw sql. I got correct results. I dont have laravel installed, but maybe you could try a UNION query in Laravel.
https://laravel.com/docs/5.4/queries#unions
select count(count_downloads.user_id)
from count_downloads
join items
on items.id = count_downloads.items_id
UNION
select count(count_loves.user_id)
from count_loves
join items
on items.id = count_loves.items_id
I am new to laravel.
I have two tables.
1) products
2) prices
-----------------------------
- products -
-----------------------------
- id_product | int (p_key) -
- name | varchar -
-----------------------------
-------------------------
- prices -
-----------------------------
- id_price | int (p_key) -
- id_product | int -
- price | int -
-----------------------------
the products table holds data about products like id, name,...
the price changes are stored in prices table where the last record is the newest price that should be displayed to users.
now I want to search through products and get the last price of each product from prices table. this is my query:
$result = DB::table('products')->leftJoin('prices', function($join) {
$join->on('products.id_product', '=', 'prices.id_product');
})->whereRaw(MY_SEARCH_FILTERS);
the above code is wrong because if a product has 4 records in prices table, then it will be repeated 4 times in $result, but only 1 record with the last price should be displayed.
Here we have 2 tables users and answers where users is left table and answers is right table which has user answers.
We wanted to left join users with answers but the join should be with the latest record or answers table.
$query = Users::select('users.id', 'users.user_name','answers.created_at as last_activity_date')
->leftJoin('answers', function($query)
{
$query->on('users.id','=','answers.user_id')
->whereRaw('answers.id IN (select MAX(a2.id) from answers as a2 join users as u2 on u2.id = a2.user_id group by u2.id)');
})
->where('users.role_type_id', Users::STUDENT_ROLE_TYPE)->get();
you can make make it easy by using Laravel Elquent:
class Product extends Model
{
public function lastPrice()
{
// optional: change id_price to created_at by add created_at to prices table
return $this->hasOne(Price::class)->orderBy('id_price', 'DESC');
}
}
now in
public function getProducts(){
$MY_SEARCH_FILTERS=....;
// get all products with last price
$products=Product::with('lastPrice')->whereRaw(MY_SEARCH_FILTERS)->get()
return $products
}
Here we have 2 tables 'articles' and 'comments' where articles is left table and comments is right table which has article's comments.
We wanted to left join articles with comments but the join should be with the latest record from comments table.
$query = Article::select('articles.*', 'comments.comment as article_comment')
->leftJoin('comments', function($query) {
$query->on('comments.article_id','=','articles.id')
->whereRaw('comments.id IN (select MAX(a2.id) from comments as a2 join articles as u2 on u2.id = a2.article_id group by u2.id)');
})
->get();
i found this solution from here https://laravelcode.com/post/how-to-get-last-record-from-leftjoin-table-in-laravel
You need to add two things in here,
1) orderBy descending on prices table.
2) first clause in the DB::table function (It will fetch only 1
record, that will be the latest price).
The solution :
$result = DB::table('products')
->leftJoin('prices',function($join)
{
$join->on('products.id_product', '=', 'prices.id_product')
})->whereRaw(MY_SEARCH_FILTERS)
->orderBy('prices.id_price','desc')
->first();
You can also use (Laravel 5.1) :
$result = DB::table('products')
->leftJoin('products.id','=','prices.id_product')
->whereRaw(MY_SEARCH_FILTERS)
->orderBy('prices.id_price','desc')
->first();
So Ive got the following Query to work just fine, searching for 'test*' in 'products_desc' column and fetching all its prices in the 'prices' table.
SELECT products.id, prices.price, products.product_desc FROM products
INNER JOIN prices
ON prices.product_id = products.id
WHERE
MATCH (products.product_desc) AGAINST ('test*' IN BOOLEAN MODE)
Although the 'prices' table consists of multiple prices per product and I only want to fetch the lowest one to each product. I've previously filtered the prices using
INNER JOIN (
SELECT min(price) as price, prices.product_number as product_number FROM prices
WHERE prices.product_number LIKE'".$q."%'
GROUP BY prices.product_number
) min_prices
on prices.price = min_prices.price
and prices.product_number = min_prices.product_number
but this was when I used products_numbers within the prices table (now there is just a product_id-column.
Products
id | product_desc
----------------------------------------
1 | product1
2 | product2
Prices
id | product_id | price
------------------------------------------------
1 | 1 | 312
2 | 1 | 219
3 | 2 | 312
4 | 2 | 111
Also, the table consists of 10+ million rows so, efficiency matters a lot :)
EDIT
What if I need to access value of columns on the min(prices.price) row?
SELECT products.id, MIN(prices.price) as prices_price, prices.id as prices_id, products.product_desc, products.product_number, prices.supplier_id, suppliers.name FROM products
INNER JOIN prices
ON prices.product_id = products.id
INNER JOIN suppliers
ON prices.supplier_id = suppliers.id
WHERE
MATCH (products.product_desc) AGAINST ('test*' IN BOOLEAN MODE)
GROUP BY prices.product_id
The above returns the lowest price per product but also the wrong value in the other columns?
You can use GROUP BY for this, with MIN() group by function.
GROUP BY is used to group values from a column, and perform calculations on column.
In our case we want to group the result by product_id as it's repeating in second table and perform calculation (min()) on price column of second table.
This is how your Query would look like:
SELECT products.id, MIN(prices.price), products.product_desc FROM products
INNER JOIN prices
ON prices.product_id = products.id
WHERE
MATCH (products.product_desc) AGAINST ('test*' IN BOOLEAN MODE)
GROUP BY prices.product_id
I'm using this query to collate two sets of results but I now need to use JOIN instead of UNION to get the second part of the data from another table.
However I need quite a lot of fields and can't seem to find a way to maintain the use of SELECT * when using JOIN.
mysql_query("SELECT * FROM table.products WHERE category='$cat' GROUP BY product_id ORDER BY id UNION ALL SELECT * FROM table.products WHERE type='red' GROUP BY product_id ");
Table - products
product_id | title | category | id
0 one home 10
1 two home 11
1 two - a home 12
2 three work 13
Table - product_details
product_id | type | size |
0 blue S
1 blue M
1 red L
Ultimately I need to list every product in the first table for a given category e.g home,
as there is sometimes two entries or more for a single product id, I need to only select one row for each product id value. I also need to join the second table so I can get the size info, however I must be able to get the size info by preferring a type e.g red.
So for this example I would get a list like:
product_id | title | category | type | size
0 one home blue S
1 two home red L
This excludes product_id 2 as it's not in the home category, the first entry for product_id equaling 1 is selected because of the GROUP BY and ORDER BY and the information on size for product_id 1 is L because it is of type red not blue.
Assuming you are using MySQL, you want a join with an aggregation or aggressive filtering. Here is an example using join and aggregation:
select p.product_id, p.title, p.category,
substring_index(group_concat(pd.type order by pd.type = 'red' desc, pd.type), ',', 1) as type,
substring_index(group_concat(pd.size order by pd.type = 'red' desc, pd.type), ',', 1) as size
from products p join
product_details pd
on p.product_id = qpd.product_id
where p.category = 'home'
group by p.product_id;
The expression substring_index(group_concat(. . .)) is choosing one type (and one size) with precedence given to the red type.
Your query can be simplified like below since you are using the same table table.products. Not sure why you need to UNION them.
SELECT * FROM table.products
WHERE category='$cat'
and type='red'
GROUP BY product_id
EDIT:
With your edited post, the query should look like
select p.product_id,p.title,p.category,q.type,q.size
from products p join product_details q
on p.product_id = q.product_id
where p.category = 'home'
and q.type = 'red'