I have two three tables:
// invoices
+----+----------------+
| id | invoice_code |
+----+----------------+
| 1 | d09823r230r4 |
| 2 | 34tf354f45tf |
+----+----------------+
// commodities
+----+-------------+------------+--------+
| id | invoice_id | product_id | number |
+----+-------------+------------+--------+
| 1 | 1 | 1 | 2 |
| 2 | 1 | 3 | 4 |
+----+-------------+------------+--------+
-- number columns is the number of each ordered product in the invoice
// products
// invoices
+----+-----------+---------+
| id | name | price |
+----+-----------+---------+
| 1 | SFX-300 | 15000 |
| 2 | GB32-b10 | 2000 |
| 3 | SVX-m30 | 1200 |
+----+-----------+---------+
All I need to do is calculating the total price of an invoice. Here is the formula to calculate the total price for invoice x:
$total_invoice_price = 0;
foreach( $invoice_x->commodities as $commodity){
$total_invoice_price += ( $commodity.number * <products.price> )
}
echo $total_invoice_price;
The problem is about getting <products.price>. It needs one more join to products table. Any idea how can I do that using Laravel relationships ?
If you just need the total price, this could be done in pure sql with aggregate statements and joins over the three tables.
SELECT invoice.invoice_code, SUM(product.price * commodities.number)
FROM invoice
JOIN commodities ON invoice.id = commodities.invoice_id
JOIN product ON product.id = commodities.product_id
GROUP BY invoice.id
To save the query, you should do eager loading using "with()" the model you wish to join.
I'm not sure how you named your model and how well it linked to each other.
Let's assume that it've been done in the conventional way.
Here is the script.
$total_invoice_price = $invoice_x->commodities
->with('products')
->get()
->map(function ($commodity) {
return $commodity->number * $commodity->product->price;
})
->sum();
What I've done is after getting the products joined with each commodity. I do the get() to have the commodities collection. The from the collection, we do map on each commodity and return the number and price of product. Then we multiply and return as the sum of each commodity record. After that we sum all the totals to the grand total and get your result.
I wrote the code without testing, so try to adjust it to your code.
Please check below Query
$invoices_total = DB::table('invoices')
->join('products', 'invoices.id', '=', 'commodities.invoice_id')
->join('commodities', 'products.id', '=', 'commodities.product_id')
->sum('products.price * commodities.number')
->groupBy('invoices.id')
->get();
Related
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();
I have two mySQL database table, I'm using one to store to product information and the other to store the sales information of product sold, where it receive my product_id as the product it sold
product Table example
product_id | prod_name |
------------------------
1 | toyota |
2 | lexus |
3 | wagon |
sale Table example
sale_id | prod_id | qty
------------------------
1 | 1 | 5
2 | 1 | 1
3 | 3 | 2
4 | 1 | 4
5 | 2 | 5
6 | 2 | 1
Now i want the mysqli datebase to tabulate the sum of the most sold product name using php and html table
you can use this query
SELECT
product.product_id,
product.prod_name,
sum(qty) as total
FROM sale
INNER JOIN product
ON
sale.prod_id=product.product_id
GROUP BY
product.product_id
ORDER BY
total DESC
this fiddle if you want to see the results
and you just need to echo the column name if you want to show it to your table on php
the case is i have this table called journal_type
id_type | account_no | description |
---------------------------------------------
1 |
1.1 |
bank
2 |
1.2 |
bank & cash
3 |
1.3 |
sale
4 |
1.4 |
stock
5 |
1.5 |
office tools
6 |
1.6 | payroll
and the second table called journal is something like this
id_transaction | id_type | debit | date_of_transaction
---------------------------------------------
1 |
1 |
50.00 | 08-22-2018
2 |
3 |
90.00 | 08-21-2018
3 |
1 |
80.00 | 08-17-2018
what i do so far is sum the data from second table's debit column based on month(in this case is august) and i want to group it by the id_type. so it would be something like id_type1 = 130.00 and id_type3 = 90.00
but i want the output is printing all the record from the id_type column in first table and let the other column null when it don't used. maybe the result i want is something like this
id_type | account_no | description | debit
---------------------------------------------
1 |
1.1 |
bank | 130
2 |
1.2 |
bank & cash| 0
3 |
1.3 |
sale | 90
4 |
1.4 |
stock | 0
5 |
1.5 |
office tools |0
6 |
1.6 | payroll | 0
public function get_finance_debitkredit($periode1, $periode2){
$query = $this->db->select('sum(debit) as debit, journal_type.account_no, journal_type.description, journal.id_type')
->from('journal_type')
->join('journal', 'journal.id_type = journal_type.id_type', 'left outer')
->group_by('journal.id_type')
->where('date_of_transaction >=', $periode1)
->where('date_of_transaction <=', $periode2)
->order_by('journal_type.account_no')
->get();
return $query;
}
but so far what I get is that it's only printed the record data that have the transaction (in this case is the id_type 1 and id_type_3) so only 2 row that appear. can i make all the 6 row of journal_type appear like what i want with some change of the query.
that code is from my model in codeigniter
You first need to create seperate table for that records and then need to left join with that table. Use following query it will give the result you want
public function get_finance_debitkredit($periode1, $periode2){
$query = $this->db->select('finl_tbl.debit, journal_type.account_no, journal_type.description, journal_type.id_type')
->from('journal_type')
->join('(SELECT sum(debit) as debit,id_type FROM journal WHERE date_of_transaction >='.$periode1.' AND date_of_transaction <='.$periode2.' group by id_type) as finl_tbl', 'finl_tbl.id_type = journal_type.id_type', 'left')
->order_by('journal_type.account_no')
->get();
return $query;
}
Products :
--------------------------------------------
| ID | Group | Name | Sold |
--------------------------------------------
| 1 | A | Dell | 0 |
--------------------------------------------
| 2 | A | Dell | 0 |
--------------------------------------------
| 3 | B | Dell | 1 |
--------------------------------------------
| 4 | B | Dell | 1 |
--------------------------------------------
| 5 | C | Dell | 0 |
--------------------------------------------
| 6 | C | Dell | 1 |
--------------------------------------------
Hi everyone, i have a table (products) stored in MySql with many records, for now i'm using this query SELECT * FROM products WHERE sold = 0, in results i get :
--------------------------------------------
| ID | Group | Name | Sold |
--------------------------------------------
| 1 | A | Dell | 0 |
--------------------------------------------
| 2 | A | Dell | 0 |
--------------------------------------------
| 5 | C | Dell | 0 |
--------------------------------------------
i want to get only one record from each group, so the results will be like :
--------------------------------------------
| ID | Group | Name | Sold |
--------------------------------------------
| 1 | A | Dell | 0 |
--------------------------------------------
| 5 | C | Dell | 0 |
--------------------------------------------
You could easily do this by using a distinct clause and removing the id column. If you want to keep the id column you need to specify how one would chose which id to keep.
select distinct
`group`
, name
, sold
from
products
where
sold = 0;
To keep the row with the smallest id (as your example shows) something along the lines of the example below would work.
select
id
, `group`
, name
, sold
from
products
where
sold = 0
and id = (
select
min(p.id)
from
products p
where
p.`group` = products.`group`
and p.sold = 0
);
First, change your field named Group to something like Group_Name. GROUP is a reserved keyword, and if it is not causing you problems now it probably will later.
Second, you should ask yourself what you are really after. The following query should generate your desired result. It adds an additional condition where the IDs that are returned are the lowest numbered ID in each group.
SELECT * FROM products
WHERE sold = 0
AND ID IN (SELECT MIN(ID) FROM products WHERE sold = 0 GROUP BY Group_Name)
Why do you want that, though? That is not a normal desired end state. You should ask yourself why you care about the ID. It looks like your goal is to figure out which products have not sold anything. In that case, I would recommend this instead:
SELECT DISTINCT Group_Name, Name
FROM products
WHERE sold = 0
ORDER BY Group_Name, Name
I found the solution by using the statement GROUP BY,
SELECT * FROM products WHERE sold = 0 GROUP BY group
in the results now, i get only one record for each group and the minimal id without adding any other statement, and in my real table i am using product_group instead of group because it's a reserved word.
Try this:
SELECT `ID`, `Group`, `Name`, `Sold` FROM products WHERE sold = 0 GROUP BY `Group`;
My environment:
I have these tables:
Table ___Billing:
|----------|----------|----------|--------------|---------------------|
| BIL_Id | BIL_Item | BIL_Rate | BIL_Quantity | BIL_ApplicableTaxes |
|----------|----------|----------|--------------|---------------------|
| 1 | Hot-Dog | 4.50 | 2 | 3 |
| 2 | Tea | 3.25 | 3 | 3,4 |
|----------|----------|----------|--------------|---------------------|
BIL_Id = the ID for this item in this table.
BIL_Item = the id of the item (referring to the ___SalesTaxes table).
BIL_Quantity = the quantity of the item the customer used.
BIL_ApplicableTaxes = the id of the applicable taxes for this item. Each taxe id is comma separated.
Table ___SalesTaxes:
|----------|--------------|------------|
| STX_Id | STX_TaxeName | STX_Amount |
|----------|--------------|------------|
| 3 | Tax 1 | 3.50 |
| 4 | Tax 2 | 6.55 |
|----------|--------------|------------|
STX_Id = the ID for this item in this table.
STX_TaxeName = the name of the taxe.
STX_Amount = the amount in percentage of the taxe.
My question:
How is it possible from these two tables, to loop into the ___Billing table in order to get the taxes in percentage I need to applied ?
For example :
For the first row, I should have: 3.50.
For the second row, I should have : 3.50, 6.55.
Hope the question is clear.
Thanks.
If you phrase your query correctly, you can use MySQL's FIND_IN_SET() function to match tax IDs agains the CSV list you have in the BIL_ApplicableTaxes column:
SELECT t1.BIL_Id,
t1.BIL_Rate,
GROUP_CONCAT(COALESCE(t2.STX_Amount, 'NA')) AS tax_due
FROM ___Billing t1
LEFT JOIN ___SalesTaxes t2
ON FIND_IN_SET(t2.STX_Id, t1.BIL_ApplicableTaxes) > 0
GROUP BY t1.BIL_Id
However, you should seriously consider normalizing the ___Billing table and removing the CSV data. Instead, each tax entry should have its own record.
Demo here:
SQLFiddle