rank query in laravel from one table data - php

At the moment it gets the races times in order but i want to combined the user_id column together, but to make sure when it groups it together it, the fastest recorded_time is kept.
Example of what i have so far from the query
User_id | race_id | recorded_time | stroke |
__________________________________________
2 | 2 | 10.03 | fly |
____________________________________________
1 | 3 | 12.98 | fly |
____________________________________________
2 3 13.58 | fly |
Here is the code that gives me that result
$query->where('race_history.stroke', '=', $stroke)
->orderBy('recorded_time', 'ASC')->get();
This is what i want it to return
User_id | race_id | recorded_time | stroke |
__________________________________________
2 | 2 | 10.03 | fly |
____________________________________________
1 | 3 | 12.98 | fly |
____________________________________________
I have tried the code bellow but it doesn't take the fastest record time
$query->where('stroke', '=', $stroke)
->orderBy('recorded_time', 'ASC')
->groupBy('user_id')->get();
But Instead produces this
User_id | race_id | recorded_time | stroke |
__________________________________________
1 | 3 | 12.98 | fly |
____________________________________________
2 | 3 | 13.58 | fly |
____________________________________________
So its grouping it by user_id BUT not taking it fastest recorded time
I have tried so many ways anyone can help this is a raw query to get what i want i would use this but the AND distance doesnt like this..
$total = DB::select(DB::raw('
SELECT *, min(recorded_time) as time
FROM race_history
WHERE stroke = "' . $stroke . '" AND distance="' . $distance . '"
GROUP BY user_id
ORDER BY min(recorded_time) ASC
'));

Here's raw query as I don't know laravel syntax
SELECT `user_id`, `race_id`, min(`recorded_time`) AS `recorded_time`, `stroke`
FROM `race_history`
WHERE `stroke` = 'fly'
GROUP BY `user_id`
ORDER BY `recorded_time` ASC

Related

Laravel 5.7 - multiply two columns and add to final sum

I have two tables - a students table and a products table.
When i make a list of the students in a table, i need to see the total amount (sum) of payments that has been made, unfortunately it seems like the result is the correct sum but multiplied by the amount of rows.
Students table:
+----+----------+
| id | name |
+----+----------+
| 1 | Jonathan |
| 2 | Bob |
+----+----------+
Products table:
+----+------------+-------+----------+
| id | student_id | money | quantity |
+----+------------+-------+----------+
| 1 | 1 | 1000 | 2 |
| 2 | 1 | 2000 | 1 |
| 3 | 2 | 500 | 5 |
| 4 | 2 | 3000 | 1 |
+----+------------+-------+----------+
Payments table:
+----+-------+------------+
| id | money | student_id |
+----+-------+------------+
| 1 | 5000 | 1 |
| 2 | 2000 | 1 |
| 3 | 2500 | 2 |
| 4 | 2500 | 2 |
+----+-------+------------+
In theory, the output of my query should be:
+-------------+----------+----------------+----------------+
| id | name | payments_total | products_total |
+-------------+----------+----------------+----------------+
| 1 | Jonathan | 4000 | 7000 |
| 2 | Bob | 5500 | 10000 |
+-------------+----------+----------------+----------------+
What i have tried:
$teamStudents = DB::table('students')->where('students.team', $team)->orderBy('first_name', 'ASC')
->join('products', 'students.id', '=', 'products.student_id')
->join('payments', 'students.id', '=', 'payments.student_id')
->select('students.first_name AS first_name', 'students.last_name AS last_name', 'students.created_at AS created_at', DB::raw('SUM(products.money * products.amount) AS products_total'), DB::raw('SUM(payments.money) AS payments_total'), 'students.id AS id')
->groupBy('students.id')
->get();
It returns no error except for the fact that the returned "payments_total" is inaccurate and multiplied by the amount of rows for some reason.
So my question is:
How do i get around this and what have i done wrong? I've been googling for an hour with no result.
Is my query an issue or the way i've set it up, if so, what would the correct solution be?
With your edit I was able to solve the problem that you have, but in your edit you use couple of things for which I don't have data, such as the $team, first_name and last_name of the students. But anyway, here is a solution for your problem, you have to use subqueries in order to solve this:
$teamStudents = DB::table('students')->orderBy('name', 'ASC')
->leftJoin(DB::raw('(select SUM(products.money * products.quantity) AS products_total, student_id from products group by student_id) products'), 'students.id', '=', 'products.student_id')
->leftJoin(DB::raw('(select sum(payments.money) as payments_total, student_id from payments group by student_id) payments'), 'students.id', '=',
'payments.student_id')
->select('students.name', 'payments.payments_total', 'products.products_total', 'students.id AS id')
->groupBy('students.id')
->get();
I am not sure if technically I will be correct, but the problem is because you use multiple joins, so that's why the results are doubled, if you don't use subqueries.
There's no need to join in this case, you don't use it anyways.
$teamStudents = DB::table('students')
->select('id, name')
->selectRaw('select sum(money) from payments where student_id = students.id as payments_total')
->selectRaw('select sum(money) from products where student_id = students.id as products_total')
->orderBy('name')
->get();

Laravel Query Builder - Sum Where and Sum Where

I have a table called stock_movements:
| product_id | type | qty |
|------------|------|------|
| 1 | A | 2 |
| 1 | A | 1 |
| 1 | A | 7 |
| 1 | B | -2 |
| 1 | B | -4 |
| 1 | B | -1 |
| 2 | A | 2 |
| 2 | A | 1 |
| 2 | A | 7 |
| 2 | B | -3 |
| 2 | B | -3 |
| 2 | B | -1 |
I am trying to create a collection of the products where which have the values of the sum of A and sum of B.
i.e.
Group by products, type
Sum qty for each type
The key thing is that it is a collection against the product, which is the bit I am struggling with. Does anyone have any advice?
So far I have got...
return $this->stockMovements()->groupBy('product_id')
->selectRaw('sum(qty), product_id')
->where('type', $this->type)
->get();
But this does not split out for types A and B.
You need a basic pivot query, which would look something like this in Laravel:
return $this->stockMovements()
->selectRaw("SUM(CASE WHEN type = 'A' THEN qty ELSE 0 END) AS sum_a, ".
"SUM(CASE WHEN type = 'B' THEN qty ELSE 0 END) AS sum_b, product_id")
->groupBy('product_id')
->get();
I removed the WHERE clause, because your data only seems to have two types, and there isn't much sense in restricting that. If you really want the sum of quantity for just A or B, then we can write a much simpler Laravel/MySQL query.
Using A Raw Expression
$result = DB::table('stock_movements')
->select(DB::raw('sum(qty) as sum_qty, type'))
->groupBy('type')
->get();
Rule of thumb for the use of aggregation functions in the SELECT clause.
If a select block does have a GROUP BY clause, any column
specification specified in the SELECT clause must exclusively occur as
a parameter of an aggregated function or in the list of columns given
in the GROUP BY clause, or in both.
For more details :
http://www.informit.com/articles/article.aspx?p=664143&seqNum=6

JOIN vs multiple queries when there is LIMIT 5

I have two tables like below:
// posts
+----+---------+------------------+-----------+
| id | title | content | author_id |
+----+---------+------------------+-----------+
| 1 | title1 | content1 | 123 |
| 2 | title2 | content2 | 456 |
| . | . | . | . |
| . | . | . | . |
| . | . | . | . |
+----+---------+------------------+-----------+
// users
+----+-------+
| id | name |
+----+-------+
| 1 | Jack |
| 2 | Peter |
| . | . |
| . | . |
| . | . |
+----+-------+
I want to select 5 posts like below:
SELECT * FROM posts WHERE 1 ORDER BY id LIMIT 5
I also need to get the name of author for each post. So I can do that by using a JOIN like this:
SELECT p.*, u.name
FROM posts p
JOIN users ON p.author_id = u.id
WHERE 1
ORDER BY id
LIMIT 5
I can do that the other way like this:
SELECT * FROM posts WHERE 1 ORDER BY id LIMIT 5
// storing results in a PHP array named $results
foreach( $results as $result ) {
SELECT name FROM users WHERE id = $result['author_id']
// storing results in a PHP array named $names
}
// combining $names and $results to make expecting result
So, which approach is more efficient for huge dataset? In other word, Does JOIN happen before LIMIT? if yes, then I guess doing that by PHP would be faster, am I wrong?
An engine can specify its own implementation, but MySQL will not read through the entire table: it will do LIMIT at the same time as the JOIN, fetching line by line until it has found 5 lines.
MySQL Engine: InnoDB uses the row level locking in table then LIMIT will work at the same time as the JOIN, fetching line by line until it has found 5 lines.

Optimization of SQL with subquery and Having

Currently we are using a custom CI library to generate PDF files from documents which exist as database records in our database.
Each document is related to the contents (== rows) with a one-has-many relation. Each row has a number (field: row_span) to indicate how many lines it will use once it gets printed in the PDF.
Per PDF page that gets build, Rows needed for that page only are selected using a subquery:
$where = $this->docType."_id = ".$this->data['doc']->id." AND visible = 1";
$sql = "SELECT *,
(SELECT
sum(row_span) FROM app_".$this->docType."_rows X
WHERE X.position <= O.position
AND ".$where."
ORDER BY position ASC) 'span_total'
FROM app_".$this->docType."_rows O
WHERE ".$where."
HAVING span_total > ".(($i-1)*$this->maxRows)." AND span_total <= ".($i*$this->maxRows)." ORDER BY O.position ASC ";
$rows = $rows->query($sql);
In the code $i is the page number and $this->maxRows is loaded from the document template record which indicates how many available lines the PDF template has.
So when the SQL renders it might look like this for page 1 of an order with ID 834:
SELECT `app_order_rows`.*,
(SELECT SUM(`app_order_rows_subquery`.`row_span`) AS row_span
FROM `app_order_rows` `app_order_rows_subquery`
WHERE `app_order_rows_subquery`.`position` <= 'app_order_rows.position'
AND `app_order_rows_subquery`.`order_id` = 834
AND `app_order_rows_subquery`.`visible` = 1
ORDER BY `app_order_rows_subquery`.`position` asc) AS span_total
FROM (`app_order_rows`)
WHERE `app_order_rows`.`order_id` = 834
AND `app_order_rows`.`visible` = 1
HAVING span_total > 0
AND span_total <= 45
ORDER BY `app_order_rows`.`position` asc
And running this with EXPLAIN gives this as output:
+====+=============+=========================+======+===============+======+=========+======+======+=============================+===+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |
+====+=============+=========================+======+===============+======+=========+======+======+=============================+===+
| 1 | PRIMARY | app_order_rows | ALL | NULL | NULL | NULL | NULL | 1809 | Using where; Using filesort | 1 |
+----+-------------+-------------------------+------+---------------+------+---------+------+------+-----------------------------+---+
| 2 | SUBQUERY | app_order_rows_subquery | ALL | NULL | NULL | NULL | NULL | 1809 | Using where | 2 |
+====+=============+=========================+======+===============+======+=========+======+======+=============================+===+
This is working great, but... When we have large orders or invoices it renders the documents very slow. This might be due to the subquery.
Does anyone have an idea on how to do the same select without subquery? Maybe we will have to go for a whole new approach to select rows and build the PDF. We are open for suggestions ^^
Thanks in advance
------------------------------- edit ------------------------------
The EXPLAIN after index creation:
+====+=============+=========================+=======+===============+============+=========+=======+======+=============+===+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |
+====+=============+=========================+=======+===============+============+=========+=======+======+=============+===+
| 1 | PRIMARY | app_order_rows | ref | index_main | index_main | 5 | const | 9 | Using where | 1 |
+----+-------------+-------------------------+-------+---------------+------------+---------+-------+------+-------------+---+
| 2 | SUBQUERY | app_order_rows_subquery | range | index_main | index_main | 10 | NULL | 1 | Using where | 2 |
+====+=============+=========================+=======+===============+============+=========+=======+======+=============+===+
As you confirmed in the comments, the tables have no indexes.
The immediate solution would be:
create index index_main on app_order_rows (order_id, position);

Sum multiple fields in 1 table

Hi I would like to sum my 4 columns in my table.
but I get error he SUM function requires 1 argument(s)
itemcost table
+------+------+------+------+------+
| id | col1 | col2 | col3 | col4 |
+======+======+======+======+======+
| 0002 | 5 | 5 | 5 | 5 |
+------+------+------+------+------+
| | | | | |
+------+------+------+------+------+
| | | | | |
+------+------+------+------+------+
$cost= DB::table('itemcost')
->select(
DB::raw('SUM(col1,col2,col3,col4) as unitprice')
);
Thank you in advance.
To sum just column of every single row, use:
(col1+col2+col3+col4) as unitprice
Or, to sum columns with rows, use:
(SUM(col1)+SUM(col2)+SUM(col3)+SUM(col4)) as unitprice
By the way, here is an article with examples
You could add columns with + sign,
Try like bellow:
$cost= DB::table('itemcost')
->select(
DB::raw('SUM(col1+col2+col3+col4) as unitprice')
);

Categories