I have two main tables bills and billing_items:
bills
_______
id
..
amount
balance
billing_items
______________
id
...
bill_id
I need to get the sum of the bill amount and balance based on certain criteria in the billing_items table (the table references other tables of interest)
when I use the below query I get duplicates:
select sum(b.amount), sum(b.balance)
from bills b left join billing_items bi
on b.id=bi.bill_id;
I can't use subqueries such as the one below because of the ORM I'm using (subqueries not supported):
select sum(a) from
(select b.amount as a, b.balance
from bills b left join billing_items bi
on b.id=bi.bill_id group by b.id) t;
the criteria on billing_items and its referencing tables is ommited but I need to reference billing_items
not sure if your ORM allows the use of user variables or not in your SQL, if yes you could try this, basically it's ORDER BY bi.bill_id and only sum amount when there's a new bill_id
select sum(IF (#prevBillId IS NULL OR #prevBillId != bi.bill_id,b.amount,0)) as sumAmount,
sum(IF (#prevBillId IS NULL OR #prevBillId != bi.bill_id,b.balance,0)) as sumBalance,
#prevBillId:=bi.bill_id
from bills b left join billing_items bi
on b.id=bi.bill_id
ORDER BY bi.bill_id;
see this sqlFiddle
Hopefully this will work...
So your issue is your left join to billing_items is causing multiple rows per billing item and your sum(b.amount) is being multiplied by the number of rows you have. So solution...divide your sum by your count.
select b.id,sum(b.amount) / count(*) as amount, sum(b.balance)
from bills b left join billing_items bi
on b.id=bi.bill_id;
group by b.id
Give that a try...hoping it works out.
Related
I have two table with one to many relationship. One is header and the other is detail.
Header Table Columns are State, Item, Bill, Quantity
Detail Table Columns are Header_ID, Allocated To, Allocated Quantity
I need to get the sum of Header Quantity and Sum of Detail Quantity using left join as detail may not exists sometime.
Here is how it looks after joining (Before grouping)
My required result after grouping should be
State, Item, Total Quantity (Header), Total Allocated (Detail Quantity).
But my problem is i am doing left join so header value is repeating, when i sum the duplicate values are also summing up.
Here is the Query i tried
SELECT
a.State,
a.Item,
b.Allocated,
sum(a.Quantity) AS Header_Total, -- Should take unique/single
sum(b.Quantity) AS Detail_Taol
FROM table1 a
LEFT JOIN table2 b ON a.ID = b.Header_ID
GROUP BY
a.State,
a.Item,
b.Allocated;
Please help in querying this to get the desired result.
JOIN (including LEFT JOIN) creates a result set containing all pairs of rows allowed by the ON clause. So your SUM(a,Quantity) expression adds up multiple copies of that value and so generates wrong results in a lot of cases.
This is sometimes called a combinatorial explosion or a cardinality error.
Fix: take off the SUM and add a.Quantity to the GROUP BY.
SELECT
a.State,
a.Item,
b.Allocated,
a.Quantity AS Header_Total,
sum(b.Quantity) AS Detail_Taol
FROM table1 a
LEFT JOIN table2 b ON a.ID = b.Header_ID
GROUP BY
a.State,
a.Item,
b.Allocated,
a.Quantity;
I have the following three tables:
I have Following Query to Join Above 3 Tables
customer.customer_id,
customer.name,
SUM(sales.total),
sales.created_at,
SUM(sales_payments.amount)
FROM
sales INNER JOIN customer ON customer.customer_id = sales.customer_id
INNER JOIN sales_payments ON sales.customer_id = sales_payments.customer_id
WHERE sales.created_at ='2020-04-03'
GROUP By customer.name
Result for Above Query is given below
Sum of sales.total is double of the actual sum of sales.total column which has 2-row count, I need to have the actual SUM of that column, without doubling the SUM of those rows, Thank you, for your help in advance..
PROBLEM
The problem here is that there are consecutive inner joins and the number of rows getting fetched in the second inner join is not restricted. So, as we have not added a condition on sales_payment_id in the join between the sales and sales_payment tables, one row in sales table(for customer_id 2, in this case) would be mapped to 2 rows in the payment table. This causes the same values to be reconsidered.
In other words, the mapping for customer_id 2 between the 3 tables is 1:1:2 rather than 1:1:1.
SOLUTION
Solution 1 : As mentioned by Gordon, you could first aggregate the amount values of the sales_payments table and then aggregate the values in sales table.
Solution 2 : Alternatively (IMHO a better approach), you could add a foreign key between sales and sales_payment tables. For example, the sales_payment_id column of sales_payment table can be introduced in the sales table as well. This would facilitate the join between these tables and reduce additional overheads while querying data.
The query would then look like:
`SELECT c.customer_id,
c.name,
SUM(s.total),
s.created_at,
SUM(sp.amount)
FROM customer c
INNER JOIN sales s
ON c.customer_id = s.customer_id
INNER JOIN sales_payments sp
ON c.customer_id = sp.customer_id
AND s.sales_payments_id = sp.sales_payments_id
WHERE s.created_at ='2020-04-03'
GROUP BY c.customer_id,
c.name,
s.created_at ;`
Hope that helps!
You have multiple rows for sales_payments and sales per customer. You need to pre-aggregate to get the right value:
SELECT c.customer_id, c.name, s.created_at, s.total, sp.amount
FROM customer c JOIN
(SELECT s.customer_id, s.created_at, SUM(s.total) as total
FROM sales s
WHERE s.created_at ='2020-04-03'
GROUP BY s.customer_id, s.created_at
) s
ON c.customer_id = s.customer_id JOIN
(SELECT sp.customer_id, SUM(sp.amount) as amount
FROM sales_payments sp
GROUP BY sp.customer_id
) sp
ON s.customer_id = sp.customer_id
I imagine I'm missing something pretty obvious here.
I'm trying to display a list of 'bookings' where the total charges is higher than the total payments for the booking. The charges and payments are stored in separate tables linked using foreign keys.
My query so far is:
SELECT `booking`.`id`,
SUM(`booking_charge`.`amount`) AS `charges`,
SUM(`booking_payment`.`amount`) AS `payments`
FROM `booking`
LEFT JOIN `booking_charge` ON `booking`.`id` = `booking_charge`.`booking_id`
LEFT JOIN `booking_payment` ON `booking`.`id` = `booking_payment`.`booking_id`
WHERE `charges` > `payments` ///this is the incorrect part
GROUP BY `booking`.`id`
My tables look something like this:
Booking (ID)
Booking_Charge (Booking_ID, Amount)
Booking_Payment (Booking_ID, Amount)
MySQL doesn't seem to like comparing the results from these two tables, I'm not sure what I'm missing but I'm sure it's something which would be possible.
try HAVING instead of WHERE like this
SELECT `booking`.`id`,
SUM(`booking_charge`.`amount`) AS `charges`,
SUM(`booking_payment`.`amount`) AS `payments`
FROM `booking`
LEFT JOIN `booking_charge` ON `booking`.`id` = `booking_charge`.`booking_id`
LEFT JOIN `booking_payment` ON `booking`.`id` = `booking_payment`.`booking_id`
GROUP BY `booking`.`id`
HAVING `charges` > `payments`
One of the problems with the query is the cross join between rows from `_charge` and rows from `_payment`. It's a semi-Cartesian join. Each row returned from `_charge` will be matched with each row returned from `_payment`, for a given `booking_id`.
Consider a simple example:
Let's put a single row in `_charge` for $40 for a particular `booking_id`.
And put two rows into `_payment` for $20 each, for the same `booking_id`.
The query will would return total charges of $80. (= 2 x $40). If there were instead five rows in \'_payment\' for $10 each, the query would return a total charges of $200 ( = 5 x $40)
There's a couple of approaches to addressing that issue. One approach is to do the aggregation in an inline view, and return the total of the charges and payments as a single row for each booking_id, and then join those to the booking table. With at most one row per booking_id, the cross join doesn't give rise to the problem of "duplicating" rows from _charge and/or _payment.
For example:
SELECT b.id
, IFNULL(c.amt,0) AS charges
, IFNULL(p.amt,0) AS payments
FROM booking b
LEFT
JOIN ( SELECT bc.booking_id
, SUM(bc.amount) AS amt
FROM booking_charge bc
GROUP BY bc.booking_id
) c
ON c.booking_id = b.id
LEFT
JOIN ( SELECT bp.booking_id
, SUM(bp.amount) AS amt
FROM booking_payment bp
GROUP BY bp.booking_id
) p
ON p.booking_id = b.id
WHERE IFNULL(c.amt,0) > IFNULL(p.amt,0)
We could make use of a HAVING clause, in place of the WHERE.
The query in this answer is not the only way to get the result, nor is it the most efficient. There are other query patterns that will return an equivalent result.
I am trying to figure out this question on a practice page online with the following tables:
Question:
For all cases in which the same customer rated the same product
more than once, and in some point in time gave it a lower rating
than before, return the customer name, the name of the product,
and the lowest star rating that was given.
I cant seem to figure out why this isnt correct - would anyone be able to help?
Here is what I have so far (without sample data):
SELECT
Customer.customer_name,
Product.product_name,
MIN(Rating.rating_stars)
FROM Rating
JOIN Product ON Rating.prod_id = Product.prod_id
JOIN Customer ON Rating.cust_id = Customer.prod_id
GROUP BY Customer.customer_name, Product.product_name
HAVING COUNT(Product.prod_id) > 1
This query will return the minimum rating stars of a product that has been reviewed more than once by the same customer, with any of the newer ratings lower than an older rating:
SELECT
r1.prod_id,
r1.cust_id,
MIN(r1.rating_star) AS min_rating
FROM
rating r1 INNER JOIN rating r2
ON r1.prod_id=r2.prod_id
AND r1.cust_id=r2.cust_id
AND r1.rating_date>r2.rating_date
AND r1.rating_star<r2.rating_star
GROUP BY
r1.prod_id,
r1.cust_id
you can then join this query with products and customers table:
SELECT
customer.customer_name,
product.product_name,
m.min_rating
FROM (
SELECT
r1.prod_id,
r1.cust_id,
MIN(r1.rating_star) AS min_rating
FROM
rating r1 INNER JOIN rating r2
ON r1.prod_id=r2.prod_id
AND r1.cust_id=r2.cust_id
AND r1.rating_date>r2.rating_date
AND r1.rating_star<r2.rating_star
GROUP BY
r1.prod_id,
r1.cust_id) m
INNER JOIN customer on m.cust_id = customer.cust_id
INNER JOIN product ON m.product_id = product.product_id
Just a few points:
You cant specify the tables in the FROM clause the sane way you specify attributes in the SELECT. You can only have a single table in the FROM, and one more for each Join you use.
SELECT a, b, c FROM a; <----------fine
SELECT a, b, c FROM a, b; <----not fine
SELECT a, b, c FROM a JOIN b; <---fine
When it comes to the tables in the FROM/JOIN, you dont use "AS" to give them an alias, just the table name followed by the alias.
FROM atable a JOIN btable b; <--This assigns alias a to "atable" and b to "btable".
You also have to specify the common attribute that the tables are going to be joined on:
customer JOIN rating ON customer.cust_id = rating.cust_id;
As for the rest you can probably work out the correct WHERE clauses to use once you have the syntax down.
I am trying to create a query that will take information out of four tables for a billing system that I am creating. I have the following tables:
Table Invoice
InvoiceID (PK)
ClientID
Date
Status
...
Table Client
ClientID (PK)
ClientName
...
Table InvoiceItem
ItemID (PK)
InvoiceID
Amount
...
Table Payments
PaymentID (PK)
InvoiceID
Amount
...
I need to create a query where I can access information from the Invoice table along with the client name, and the sum of all invoice items and payments associated with the invoice.
I have tried the following:
SELECT
Invoice.InvoiceID,
Invoice.`Date`,
Invoice.Terms,
Invoice.DateDue,
Invoice.Status,
Client.ClinicName,
SUM(InvoiceItem.Amount),
SUM(Payment.PaymentAmount)
FROM Invoice
JOIN (Client, InvoiceItem, Payment) ON
(Client.ClientID=Invoice.ClientID AND
InvoiceItem.InvoiceID=Invoice.InvoiceID AND
Payment.InvoiceID=Invoice.InvoiceID)
And while this kind-of works, it is multiplying the SUM() by the number of records used to get the sum (i.e. if there are two payments - 800,400 - It gives me (800+400)*2 -- 2400). I am guessing that there is something with how I am using the join, and I have honestly never had to use join for more than one table, and I would always use GROUP BY, but I can't seem to get that to work correctly.
To make matters worse, I have been lost to the world of vb.net/MSSQL client-side programming for the past several years, so my MySQL is rather rough.
Your problem is that you can't aggregate over two independent tables at once in a single query. However you can do it using subqueries.
SELECT Invoice.InvoiceID, Invoice.`Date`, Invoice.Terms, Invoice.DateDue, Invoice.Status, Client.ClinicName, InvoiceItemSum.SumOfAmount, PaymentSum.SumOfPaymentAmount
FROM Invoice
INNER JOIN Client ON Client.ClientID = Invoice.ClientID
INNER JOIN (
SELECT InvoiceID, SUM(Amount) AS SumOfAmount
FROM InvoiceItem
GROUP BY InvoiceID
) InvoiceItemSum ON InvoiceItemSum.InvoiceID = Invoice.InvoiceID
INNER JOIN (
SELECT InvoiceID, SUM(PaymentAmount) AS SumOfPaymentAmount
FROM Payment
GROUP BY InvoiceID
) PaymentSum ON PaymentSum.InvoiceID = Invoice.InvoiceID
Here try this one
SELECT a.InvoiceID,
a.`Date`,
a.Terms,
a.DateDue,
a.Status,
b.ClinicName,
SUM(c.Amount),
SUM(d.PaymentAmount)
FROM Invoice a
INNER JOIN Client b
on a.ClientID = b.ClientID
INNER JOIN InvoiceItem c
ON c.InvoiceID = a.InvoiceID
INNER JOIN JOIN Payment d
ON d.InvoiceID = a.InvoiceID
GROUP BY a.InvoiceID,
a.`Date`,
a.Terms,
a.DateDue,
a.Status,
b.ClinicName
can you elaborate more on this?
it is multiplying the SUM() by the number of records used to get the
sum (i.e. if there are two payments - 800,400 - It gives me
(800+400)*2 -- 2400)
Try this:
SELECT
Invoice.InvoiceID,
Invoice.`Date`,
Invoice.Terms,
Invoice.DateDue,
Invoice.Status,
Client.ClinicName,
SUM(InvoiceItem.Amount),
SUM(Payment.PaymentAmount)
FROM Invoice
JOIN Client ON Client.ClientID=Invoice.ClientID
JOIN InvoiceItem ON InvoiceItem.InvoiceID=Invoice.InvoiceID
JOIN Payment ON Payment.InvoiceID=Invoice.InvoiceID
group by 1,2,3,4,5,6;
I did two things to your query:
Created separated joins for each of the child tables
Added a group by, without which the sum won't work correctly (fyi, in all other databases, omitting the group by would actually result in a syntax error)
you can also achive it by "CROSS APPLY"
SELECT Invoice.InvoiceID, Invoice.`Date`, Invoice.Terms, Invoice.DateDue, Invoice.Status, Client.ClinicName, InvoiceItemSum.SumOfAmount, PaymentSum.SumOfPaymentAmount
FROM Invoice
INNER JOIN Client ON Client.ClientID = Invoice.ClientID
CROSS APPLY ( SELECT ISNULL(SUM(Amount),0) AS SumOfAmount
FROM InvoiceItem
WHERE InvoiceID = Invoice.InvoiceID
) InvoiceItemSum
CROSS APPLY ( SELECT ISNULL(SUM(PaymentAmount),0) AS SumOfPaymentAmount
FROM Payment
WHERE InvoiceID = Invoice.InvoiceID
) PaymentSum