Finding Sum of values of a particular group in Table - mysql

I have two tables in MySQL. The first one is Catalog with one of its columns as Category, and another table Stock with SP and CP as columns. I want to find the total profit of each category.
I am not able to come up with an appropriate query. Every query I try gives an error. Can anyone help?
select A.Category,sum(B.SP group by A.Category) as TotalSP,sum(B.CP group by A.Category) as TotalCP,
case
when TotalSP>TotalCP then ((TotalSP-TotalCP)/TotalSP)*100
when TotalSP<TotalCP then ((TotalSP-TotalCP)/TotalCP)*100
end
as TotalProfit
from Catalog A,Stock B;
Note: negative profit automatically considered as loss.

select A.Category,sum(B.SP) as TotalSP,sum(B.CP) as TotalCP,
case
when TotalSP>TotalCP then ((TotalSP-TotalCP)/TotalSP)*100
when TotalSP<TotalCP then ((TotalSP-TotalCP)/TotalCP)*100
end
as TotalProfit
from Catalog A
INNER JOIN Stock B ON B.catalog_id = A.id/*Write how your tables linked here*/
group by A.Category;
This query selects all catalog records, joins stock data to it, then groups results by category, counts sums in each group.

Related

Combine 3 tables with sum mysql

I have a problem with my query. I've managed to display data from 2 tables. Next I want to add up SALE_AMT from the 3rd table, the problem is that order by cannot, the program output adds up all data not by order by
This my query:
SELECT
customer.CUST_NO, customer.CUST_NM,
store.STR_CD, store.STR_NM, SUM(SALE_AMT)
FROM
customer, store, cust_sale_pnt
GROUP BY
CUST_NO
table customer
table cust_sale_pnt
table store
My result
my result
Can you guys help me to sum data based on grouping cust_no?
The problem is, that you don't specify a condition that's used to combine the records of the different tables. In effect, you're creating a 'cross join'.
That is, any record of table customer is combined with any record of table store and the result is combined with any record of table cust_sale_pnt.
In effect, you get each of your 3 customers combined with each of your 6 sales. Therefore the sum over sale_amt is identical for each customer.
You want an INNER JOIN to combine the records:
SELECT c.CUST_NO, c.CUST_NM, SUM(csp.SALE_AMT)
FROM customer c
INNER JOIN cust_sale_pnt csp ON c.CUST_NO = csp.CUST_NO
INNER JOIN store ON csp.STR_CD = store.STR_CD
GROUP BY c.CUST_NO, c.CUST_NM;

How can I fix this syntax error involving the FROM clause?

I am working on a simple problem set, and I cannot seem to find the issue that is generating this same error: "Syntax Error in FROM Clause".
The question involves the use of various databases in this instant to find "Which employee has sold the most product?"
Here is my code
SELECT (Employees.FirstName + Employees.LastName) as Employee, SUM(Orders.Quantity)
FROM Employees, Orders
JOIN Employees ON Orders.EmployeeID=Employees.EmployeeID
JOIN OrderDetails ON Orders.OrderID=OrderDetails.OrderID
GROUP BY Employee
ORDER BY max(SUM(Quantity)) DESC;
If I am misinterpreting the use of some syntax, please let me know. I am still learning.
Thanks for your help!
When you're using ANSI JOIN you don't list all the tables in the FROM clause. Just list the first table, and the other tables are in JOIN.
You also can't nest aggregate functions as MAX(SUM(Quantity)). If you want to find the employee who sold the most, order by quantity, and use TOP 1 to get the first row.
There's no need to join with OrderDetails, since you're not using anything from that table.
The query should be:
SELECT TOP 1 (Employees.FirstName + Employees.LastName) as Employee, SUM(Orders.Quantity) AS Quantity
FROM Employees
JOIN Orders ON Orders.EmployeeID=Employees.EmployeeID
GROUP BY Employee
ORDER BY Quantity DESC;
Note that if there's a tie for the most sold, this will just show one of them. Getting all of them is more complex, because you need a second query to get that maximum. See sql HAVING max(count()) return zero rows

How to store SQL Query result in table column

I'm aware of the INSERT INTO table_name QUERY; however, I'm unsure how to go about achieving the desired result in this case.
Here's a slightly contrived example to explain what I'm looking for, but I'm afraid I cannot put it more succiently.
I have two tables in a database designed for a hotel.
BOOKING and CUSTOMER_BOOKING
Where BOOKING contains PK_room_number, room_type, etc. and CUSTOMER_BOOKING contains FK_room_number, FK_cusomer_id
CUSTOMER_BOOKING is a linking table (many customers can make many bookings, and many bookings can consist of many customers).
Ultimately, in the application back-end I want to be able to list all rooms that have less than 3 customers associated with them. I could execute this a separate query and save the result in the server-side scripting.
However, a more elegant solution (from my point of view) is to store this within the BOOKING table itself. That is to add a column no_of_bookings that counts the number of times the current PK_room_number appears as the foreign key FK_room_number within the CUSTOMER_BOOKING table. And why do this instead? Because it would be impossible for me to write a single complicated query which will both include the information from all ROOMS, among other tables, and also count the occurrences of bookings, without excluding ROOMS that don't have any bookings. A very bad thing for a hotel website attempting to show free rooms!
So it would look like this
BOOKING: PK_room_number (104B) room_type (double) room_price (high), no_of_bookings (3)
BOOKING: PK_room_number (108C) room_type (single) room_price (low), no_of_bookings (1)
CUSTOMER_BOOKING: FK_room_number (104B) FK_customer_id (4312)
CUSTOMER_BOOKING: FK_room_number (104B) FK_customer_id (6372)
CUSTOMER_BOOKING: FK_room_number (104B) FK_customer_id (1112)
CUSTOMER_BOOKING: FK_room_number (108C) FK_customer_id (9181)
How would I go about creating this?
Because it would be impossible for me to write a single complicated
query which will both include the information from all ROOMS, among
other tables, and also count the occurrences of bookings, without
excluding ROOMS that don't have any bookings.
I wouldn't say it's impossible and unless you're running into performance issues, it's easier to implement than adding a new summary column:
select b.*, count(cb.room_number)
from bookings b
left join customer_booking cb on b.room_number = cb.room_number
group by b.room_number
Depending on your query may need to use a derived table containing the booking counts for each room instead instead
select b.*, coalesce(t1.number_of_bookings,0) number_of_bookings
from bookings b
left join (
select room_number, count(*) number_of_bookings
from customer_booking
group by room_number
) t1 on t1.room_number = b.room_number
You have to left join the derived table and select coalesce(t1.number_of_bookings,0) in case a room does not have any entries in the derived table (i.e. 0 bookings).
A summary column is a good idea when you're running into performance issues with counting the # of bookings each time. In that case I recommend creating insert and delete triggers on the customer_booking table that either increment or decrement the number_of_bookings column.
You could do it in a single straight select like this:
select DISTINCT
b1.room_pk,
c1.no_of_bookings
from cust_bookings b1,
(select room_pk, count(1) as no_of_bookings
from cust_bookings
group by room_pk) c1
where b1.room_pk = c1.room_pk
having c1.no_of_bookings < 3
Sorry i used my own table names to test it but you should figure it out easily enough. Also, the "having" line is only there to limit the rows returned to rooms with less than 3 bookings. If you remove that line you will get everything and could use the same sql to update a column on the bookings table if you still want to go that route.
Consider below solutions.
A simple aggregate query to count the customers per each booking:
SELECT b.PK_room_number, Count(c.FK_customer_id)
FROM Booking b
INNER JOIN Customer_Booking c ON b.PK_room_number = c.FK_room_number
GROUP BY b.PK_room_number
HAVING Count(c.FK_customer_id) < 3; # ADD 3 ROOM MAX FILTER
And if you intend to use a new column no_of_booking, here is an update query (using aggregate subquery) to run right after inserting new value from web frontend:
UPDATE Booking b
INNER JOIN
(SELECT b.PK_room_number, Count(c.FK_customer_id) As customercount
FROM Booking b
INNER JOIN Customer_Booking c ON b.PK_room_number = c.FK_room_number
GROUP BY b.PK_room_number) As r
ON b.PK_room_number = r.PK_room_number
SET b.no_of_booking = r.customercount;
the following generates a list showing all of the bookings and a flag of 0 or 1 if the the room has a customer for each of the rooms. it will display some rooms multiple times if there are multiple customers.
select BOOKING.*,
case CUSTOMER_BOOKING.FK_ROOM_NUMBER is null THEN 0 ELSE 1 END AS BOOKING_FLAG
from BOOKING LEFT OUTER JOIN CUSTOMER_BOOKING
ON BOOKING.PK_room_numer = CUSTOMER_BOOKING.FK_room_number
summing and grouping we arrive at:
select BOOKING.*,
SUM(case when CUSTOMER_BOOKING.FK_ROOM_NUMBER is null THEN 0 ELSE 1 END) AS BOOKING_COUNT
from BOOKING LEFT OUTER JOIN CUSTOMER_BOOKING
ON BOOKING.PK_room_number = CUSTOMER_BOOKING.FK_room_number
GROUP BY BOOKING.PK_room_number
there are at least two other solutions I can think of off the top of my head...

Invalid results returning from a multi-table SELECT Statement

I have a Database with the following structure:
http://i.imgur.com/DFZz3Py.png
I'm trying to run a select statement, getting information from multiple tables, however it keeps bringing me duplicate results. The statement I'm using is:
SELECT StockReceipts.StockID, StockReceipts.Quantity, StockPriceHistory.Price
FROM StockReceipts,StockPriceHistory,Receipts
WHERE (Receipts.ReceiptID = 1) AND (Receipts.OrderDate BETWEEN StockPriceHistory.DateStart AND StockPriceHistory.DateEnd)
And the results i'm getting are:
http://i.imgur.com/2ZrgYyZ.png
What I actually want is matching rows from the stockreceipts table,
but with the price for each item of stock (the price that was within the date & time of ordering - OrderDate taken from the Receipts table) as well, taken from the StockPriceHistory table. I don't understand why it's making up duplicate/incorrect rows when there are only two rows in the StockReceipts table for that receipt.
Can anyone help? Thanks
SELECT
SR.StockID,
SR.Quantity,
SPH.Price
FROM
Receipts R
JOIN StockReceipts SR
on R.ReceiptID = SR.ReceiptID
JOIN StockPriceHistory SPH
on SR.StockID = SPH.StockID
WHERE
R.ReceiptID = 1
AND R.OrderDate BETWEEN SPH.DateStart AND SPH.DateEnd
You had no JOIN conditions between the tables leaving it a Cartesian result... For every record in one, grabbed entries from all other table rows.

Comparing the value of a field in one table to the sum of multiple fields in another table

I have two tables
packages table: packageid, quantity
transactions table: transactionid, packageid, quantity
Whenever a 'package' is obtained, an entry is made in the 'transactions' table, together with the quantity obtained. multiple 'transactions' can be performed and when the package is fully obtained, the sum of all transactions for that package should be equal to the quantity for that package in the packages table.
I want to write a SQL statement to check if a particular packageid has NOT been fully obtained, ie:
no transactions were performed
transactions were performed but the sum of the quantities of all
transactions is less than the quantity in the package table
how can I achieve this? every join i try to use is only catering for number 2 but does not factor in packages that have had no transactions
i tried:
select package.packageid, package.quantity, sum(transactions.quantity) from package join transactions on package.packageid = transactions.packageid where package.quantity > sum(transactions.quantity)
also tried left join but it does not work. ideally i want all packages listed from the left table in the result with null values on the right where necessary
If you want to include the packages which have no transactions, then you need to left outer join. This will keep all of the packages in the results and match transactions where transactions exists.
Then you need to group by package.packageid so that your sum operation only works on transactions records that belong to a certain packageid.
Then you use a having clause, which is similar to a where clause, only it works with aggregate functions like sum.
select
p.packageid,
p.quantity,
sum(t.quantity) as transaction_sum
from
packages p
left outer join transactions t on (t.packageid = p.packageid)
group by
p.packageid,
p.quantity
having
coalesce(sum(t.quantity), 0) != p.quantity
;
The coalesce just says, "take the first non-null value." So if sum(t.quantity) returns null then it will use 0 instead. This will make sure that packages with a quantity, but no transactions are included in the result set.
I would advise that you read up on group by and having. They are indispensable when working with sql. In essence, group by splits your data into groups based on criteria they have in common. Then having filters out certain results based on qualities of the group.