Group rows excluding column used in case - laravel

I'm building a small stocks/portfolio tracker and I'm having some trouble retrieving and counting cells of my transactions. Below you can find the dummy data in my transactions table of my database.
// Transactions table
ID | Name | Symbol | Currency | amount_bought | amount_sold | price | commission | bought | portfolio_id
_____________________________________________________________________________________________________________________
1 | Ocugen Inc | FRA.2H51 | EUR | 55 | NULL | 0.29 | 7.51 | 1 | 1
2 | Tesla, Inc | NASDAQ.TSLA | EUR | 5 | NULL | 654.87 | 4.23 | 1 | 1
3 | Ocugen Inc | FRA.2H51 | EUR | NULL | 40 | 1.31 | 7.55 | 0 | 1
I'm using a boolean named bought (final column) in order to "identify" my transaction as either being a sold or bought stock. Next, I want to retrieve all my transactions using my portfolio_id and group them on their name, symbol and currency in order to output the following:
// Desired result:
Name | Symbol | Currency | amount_current | commission_total | bought_total | sales_total
_____________________________________________________________________________________________________
Ocugen Inc | FRA.2H51 | EUR | 15 | 15.06 | 15.95 | 52.4
Tesla, Inc | NASDAQ.TSLA | EUR | 5 | 4.23 | 3274.35 | 0
Currently my code works exactly like I wanted, except for how my rows are being grouped. Because I'm using a case, in order to calculate the total amount of buys and sales of a single stock, I'm forced to include the bought column into my groupBy(). Therefore my results are also grouped on the bought in addition to the name, symbol and currency:
// Current result:
name | symbol | currency | amount_current | commission_total | bought_total | sales_total
_____________________________________________________________________________________________________
Ocugen Inc | FRA.2H51 | EUR | 15 | 7.51 | 15.95 | 0
Tesla, Inc | NASDAQ.TSLA | EUR | 5 | 4.23 | 3274.35 | 0
Ocugen Inc | FRA.2H51 | EUR | NULL | 7.55 | 0 | 52.4
Below you can find my code that generates the result above.
$transactions = Transaction::where('portfolio_id', $portfolio->id)
->groupBy('name', 'symbol', 'currency', 'bought')
->select([
'name',
'symbol',
'currency',
DB::raw('sum(amount_bought) - sum(amount_sold) as amount_current'),
DB::raw('sum(commission) AS commission_total'),
DB::raw('case when bought = 1 then sum(price) * sum(amount_bought) else 0 end as bought_total'),
DB::raw('case when bought = 0 then sum(price) * sum(amount_sold) else 0 end as sales_total')
])
->get();
How can I group my transactions on the stock name, symbol and currency and calculate their totals without grouping them on the bought column?

I don't really know Laravel, but you should be able to use:
$transactions = Transaction::where('portfolio_id', $portfolio->id)
->groupBy('name', 'symbol', 'currency')
->select([
'name',
'symbol',
'currency',
DB::raw('sum(amount_bought) - sum(amount_sold) as amount_current'),
DB::raw('sum(commission) AS commission_total'),
DB::raw('sum(case when bought = 1 then price * amount_bought else 0 end as bought_total'),
DB::raw('sum(case when bought = 0 then price * amount_sold) else 0 end) as sales_total')
])
->get();
That is, remove bought from the group by and make the case the argument to sum().

Related

How to find and group records which have same conditions subsequently?

We have a table which we record our students' attendances in. We need to find out which students missed n number of contacts in a row.
Here is the attendance table structure with example records
----------------------------------------------------
| id | student_id | class_id | checkedin_time |
--------------------------------------------------
| 1 | 1 | 1 | null |
| 2 | 1 | 2 | 2019-07-09 10:30 |
| 3 | 1 | 3 | null |
| 4 | 1 | 4 | null |
| 5 | 1 | 5 | 2019-07-12 12:00 |
----------------------------------------------------
What I'm looking for is a code that show me records with id 3 and 4 (two subsequent missed contacts) grouped by student_id
This was my starting point:
$attendances = \App\Attendance::where('checked_time' , null)->get();
$attendances->groupBy('student_id')

Laravel Count > Other Count

I have the next question:
I have the next table on mysql
ID | IDPAYMENT | CUSTOMER | PAYDATE | STATUS | PENDINGAMOUNT | PAYTO DATE
1 | 1 | JOHN | 2018-12-15 | PAYED | $0 | 2018-11-01
2 | 1 | JOHN | NO/PAY | PENDING | $10 | 2018-11-15
3 | 1 | JOHN | NO/PAY | PENDING | $10 | 2018-12-01
4 | 1 | JOHN | NO/PAY | PENDING | $10 | 2018-12-15
5 | 1 | JOHN | NO/PAY | PENDING | $10 | 2019-01-01
6 | 1 | JOHN | NO/PAY | PENDING | $10 | 2018-01-15
7 | 1 | JOHN | NO/PAY | PENDING | $10 | 2018-02-01
I want to count status with PENDING and compare with count of ID:
STATUS PENDING = 6
ID COUNT = 7
The idea is to SUM the PENDINGAMOUNT where the Status = Pending, and the date is < to today to add a commission for No Payment...
For :
SUM the PENDINGAMOUNT where the Status = Pending, and the date is < to today ...
Raw SQL :
SELECT COUNT(*) FROM table_name WHERE `status` = 'PENDING` AND `paydate` < CURDATE()
Converting to laravel builder :
$count = Model::where('status', 'PENDING')->whereDate('paydate', '<', Carbon::today())->count();
Getting total count of all rows :
$totalCount = Model::count();
Not sure about adding commission as we will need more business logic for the same.

Eloquent Joins and Sums

I have 2 tables and would like to get have a query which gets all columns from table 'projects' and several sums from table 'invoices' based on a field 'type'.
projects
+----+--------+------+--------+
| ID | Address| Date | Other |
+----+--------+------+--------+
| 1 | demo | date | other |
| 2 | demo2 | date2| other2 |
invoices
+----+---------+-----+--------+
| ID | proj_id | type| amount |
+--------------+-----+--------+
| 1 | 1 | a | 10 |
| 2 | 1 | a | 20 |
| 3 | 1 | b | 10 |
| 4 | 1 | b | 15 |
| 5 | 1 | c | 5 |
| 6 | 2 | a | 30 |
| 7 | 2 | a | 5 |
| 8 | 2 | b | 30 |
| 9 | 2 | c | 5 |
| 10 | 2 | c | 30 |
Using Laravel Eloquent I want to be able to get:
+----+---------+------+-------+---+---+---+
| ID | Address | Date | Other | a | b | c |
+----+---------+------+-------+---+---+---+
| 1 | demo | date | other |30 |25 | 5 |
| 2 | demo2 | date2| other2|35 |30 |35 |
Im getting stuck with the sum part, well actually the whole thing!
So far I have:
$projects = DB::table('projects')
->leftJoin('invoices', 'projects.id', '=', 'invoices.project_id')
->select('projects.*', 'invoices.*')
->get();
Which is obviously not very far along! Any help would be greatly appreciated!
You need a basic pivot query here. The easiest way to go here would probably be via a raw select:
$sql = "projects.*, ";
$sql .= "sum(case when invoices.type = 'a' then invoices.amount end) as a, ";
$sql .= "sum(case when invoices.type = 'b' then invoices.amount end) as b, ";
$sql .= "sum(case when invoices.type = 'c' then invoices.amount end) as c";
$projects = DB::table('projects')
->select(DB::raw($sql))
->leftJoin('invoices', 'projects.id', '=', 'invoices.project_id')
->groupBy('project.id')
->get();
This should correspond to the following raw MySQL query:
SELECT
p.*,
SUM(CASE WHEN i.type = 'a' THEN i.amount END) AS a,
SUM(CASE WHEN i.type = 'b' THEN i.amount END) AS b,
SUM(CASE WHEN i.type = 'c' THEN i.amount END) AS c
FROM project p
LEFT JOIN invoices I
ON p.id = i.project_id
GROUP BY
p.id;

laravel, group by category and select the record with the minimum price

I have models Book and BookCategory
How do I select the cheapest book in every category?
Book table:
| id | name | price | book_category_id |
| 1 | test | 10 | 1
| 2 | test | 15 | 3
| 3 | test | 75 | 1
| 4 | test | 25 | 2
| 5 | test | 19 | 1
| 6 | test | 11 | 2
| 7 | test | 10 | 1
The selection should be :
| id | name | price | book_category_id |
| 1 | test | 10 | 1
| 2 | test | 15 | 3
| 6 | test | 11 | 2
I've tried:
$books = Book::groupBy("book_category_id")->orderBy("price")->get()
But the output is not the minimum price row.
any idea?
EDIT:
I found this page:
https://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
it has 90% of the solution in SQL
SELECT *
FROM books
WHERE price = (SELECT MIN(price) FROM books AS u WHERE u.book_category_id= books.book_category_id)
GROUP BY books.book_category_id
how to convert this to laravel query builder?
You need to perform a subquery like this post. Try this:
$books = Book::from(DB::raw("SELECT * FROM books order by price asc"))
->groupBy("book_category_id")->get();
Please note that this is a mysql only solution because in mysql you're allowed to not aggregate non-group-by columns. If you need to do this for another DB, you need to perform an inner join on the subquery

laravel eloquent relation with 3 table

Hi I want query from 3 table
user:
+----+-----------+
| id | name |
+----+-----------+
| 1 | Denny |
| 2 | Agus |
| 3 | Dini |
| 4 | Angel |
+----+-----------+
History_Education
+----+-----------+-------------+
| id | userId | educationId |
+----+-----------+-------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
+----+-----------+-------------+
Education
+----+-----------+----------+
| id | level | Name |
+----+-----------+----------+
| 1 | 1 | SD |
| 2 | 2 | SMP |
| 3 | 3 | SMA |
| 4 | 4 | S1 |
+----+-----------+----------+
How to query with laravel Eloquent to get the latest user education order by Level DESC
expected:
+----+-----------+----------------------+
| id | Name | Latest_Education |
+----+-----------+----------------------+
| 1 | Denny | SMP |
| 2 | Agus | SMP |
| 3 | Dini | - |
| 4 | Angel | - |
+----+-----------+----------------------+
In normal query:
select id,name ,(select E.name from education E inner join History_eductaion HE on E.id = HE.education_id where HE.userId =U.id limit 1 order by E.level DESC )latest_education from USER U
How to translate to laravel eloquent?
The quick answer is use the query builder - your query needed to be altered slightly to match the table names and columns as listed in your question:
$result = DB::table('user')
->select([
'id',
'name',
DB::raw("(select E.name from Education E inner join History_Education HE on E.id = HE.educationId where HE.userId = user.id order by E.level DESC limit 1) as latest_education")
])
->get();

Resources