Why my if else does not give the expected output? (mySQL) - mysql

I'm hoping you guys can help me in SQL because i'm a newbie iN sql. My problem is, the code does not give the expected output and i do not know how to fix that eventough i've searched through books and online resources. I have 2 tables (customer & order_status). The task is:
(1) select c_id,lname,address,city,description where c_id > 3
(2) select c_id,lname,address,o_status,item_total,remarks and update description to 'black' where c_id =3
(3)if item_total > 2, select o_status,item_total. ELse select o_status,item_total,remarks,order_no and update remarks to 'set'
So, here's the code:
#drop procedure if exists usp_GetAnything;
delimiter //
create procedure usp_GetAnything()
begin
declare total int ;
select total = item_total
from order_status;
select c_id,lname,address,city,description
from customer
where c_id > 3;
select c.c_id,c.lname,c.address,o.o_status,o.item_total,o.remarks,c.description
from customer c,order_status o
where c.c_id=o.c_id;
update customer
set description = 'black'
where c_id = 3;
if (total > 2) then
select o_status,item_total,remarks
from order_status
where item_total = total;
else
select o_status,item_total,remarks,order_no
from order_status
where item_total = total;
update order_status
set remarks = 'set';
end if;
end
I'm expecting the output to get the item_total for each row. If the item_total >2, it will just select, Else, it will update the remarks.. Each c_id have different no. of item_total.

Your code is a bit of a scramble, so I will try to help doing one piece at a time.
Your (1), where c_id > 3, what is the purpose of this, this will give ALL customers with ID greater than 3, but ok.
Formatting your queries for readability is also a good thing to get in the habit of. Also,
USE table aliases, especially when you get into longer table name references, makes it easier
to join/link/get fields/where/group by respectively. Even if one table.
select
c.c_id,
c.lname,
c.address,
c.city,
c.description
from
customer c
where
c.c_id > 3;
Your (2a). Your query does a simple join from the customer table to the order status based on
the customer's ID. This is ok, but it is also returning ALL customers that have an order,
and if someone has multiple orders, it will show that customer for EACH order they have.
Also, try to get in the habit of using JOIN conditions on tables instead of implied joins
within the WHERE clause. It makes it easier when you need to get into left/right join conditions.
select
c.c_id,
c.lname,
c.address,
o.o_status,
o.item_total,
o.remarks,
c.description
from
customer c
JOIN order_status o
ON c.c_id = o.c_id;
Now, if you only cared about a specific customer, add your WHERE clause for said customer ID.
Your (2b) component to update the description to 'black' where c_id =3, that was ok
update customer
set description = 'black'
where c_id = 3;
For #3, the item_total > 2. What is the basis of this item total. You had
declare total int ;
select total = item_total
from order_status;
This should not really do anything as it is going through every order and will just return
1(true) or 0(false) for every record. You have no criteria such as total per customer
or even just the number of orders for a given customer... Such as Person "A" has 5 orders on file,
and Person "B" has 1, Person "C" has 2. You will need to clarify your intent on that.
This poses the follow-up to your if/else based on the item_total > 2. select version(a) or (b)
of yet another query (not going to copy/paste that as no idea your intent)
In your if( total > 2 ) else condition, your update statement has no WHERE clause,
so it will update EVERY record in the order_status table (probably NOT what you intended).
So, with a bit more clarification, maybe some sample data myself and others could help.
Even as a newbie, if there are things that may be confidential otherwise, you can always mask
things by showing only the critical pieces, but not actually show production data, but show
sample data to help us follow your needs... now and in the future.

Related

MySQL: Tricky Query with regard to CASES

I have to solve a problem with a very complicated Query. Let´s sum it up for you:
The function should receive a customer number as an input parameter. If this
number is not contained in the tablecustomer, - 1 is to be output.
Otherwise, the customer bonus points are to be calculated. These result from
the reservations made by the customer.
The bonus points per reservation are calculated by multiplying the number of
reserved seats by the flight duration of the flight.
I already tried my best and worked this out:
CREATE FUNCTION customer_bonus(inputnumber INT(11))
RETURNS int
DETERMINISTIC
RETURN
CASE
WHEN
(SELECT COUNT(customer.ID) FROM customer WHERE inputnumber =
customer.ID) >= 1
THEN
(SELECT customer.ID, SUM(SELECT
flightexecution.FlightDurationInMinutes *
reservation.NoReservedSeats from customer, flightexecution,
reservation where inputnumber = customer.ID))
from customer, flightexecution, reservation
WHERE reservation.CustomerID = customer.ID AND customer.ID = inputnumber
AND flightexecution.FlightNo = reservation.FlightNo
AND reservation.DepartureDate = flightexecution.DepartureDate)
ELSE -1
END;
I run this query to test it:
select c.id, customer_bonus(c.ID) from customer c;
The result is just an 'OK' which means that something is wrong.
The funny thing: When i try out the simple Select Statements it works..
Could somebody of you help me?
The query you are using is likely returning a number of 2-field results. When attempting to return an int sourced from a query, the query can have only one result with a single field convertible to int. I think this is the query you are probably looking for:
SELECT SUM(fe.FlightDurationInMinutes * r.NoReservedSeats) AS bonusPoints
FROM reservation AS r
INNER JOIN flightexecution AS fe
ON r.FlightNo = fe.FlightNo
AND r.DepartureDate = fe.DepartureDate
WHERE r.CustomerID = inputnumber
You'll notice the query becomes much clearer when explicit JOINs are used. Honestly, I have no idea why anyone would teach implicit joins as anything more than a curiosity anymore. They've been seen as poor form (at best) for nearly two decades now.
With explicit joins, it would have been obvious that your SUM(SELECT subquery had no join conditions at all; meaning the sum you ended up calculating would have been of the [flight duration] * [reserved seats] of every combination of flightexecution and reservation, regardless of their relation to the customer or each other.

MySQL Select Latest Row of Specific Value

I'm battling to wrap my head around producing a single MySQL query that would heed the correct results.
I've got a table that is structured as followed:
workflow_status_history:
id reference status
1 308ffn3oneb Lead Received
2 308ffn3oneb Quoted
3 308ffn3oneb Invoiced
4 853442ec2fc Lead Received
As you can see, the workflow_status_history table keeps a history of all the statuses of each workflow on our system, rather than replacing or overwriting the previous status with the new status. This helps with in-depth reporting and auditing. A workflow will always have a starting status of Lead Received.
The problem however is that I need to select the reference field of each row in the table who's latest or only status is Lead Received. So in the example above, field number 4 would return, however fields 1, 2 and 3 would not return because the latest status for that workflow reference is Invoiced. But if 853442ec2fc (field number 4) gets a new status other than Lead Received, it also should not return the next time the query runs.
My current query is as followed:
SELECT *, MAX(id) FROM workflow_status_history WHERE 'status' = 'Lead Received' GROUP BY reference LIMIT 20
This, of course, doesn't return the desired result because the WHERE clause ensures that it returns all the rows that have a Lead Received status, irrespective of it being the latest status or not. So it will always return the first 20 grouped workflow references in the table.
How would I go about producing the correct query to return the desired results?
Thanks for your help.
This is a case for a left join with itself. The idea in this query is:
select all references with status 'Lead Received' which do not have a row with the same reference and a higher ID. I assume you only use the id for determining what is the 'newer' status, no timestamp etc.
SELECT
DISTINCT h1.reference
FROM
workflow_status_history h1 LEFT JOIN workflow_status_history h2 ON
h1.reference = h2.reference AND
h1.id < h2.id
WHERE
h1.status = 'Lead Received' AND
h2.id IS NULL
Although #Martin Schneider answer is correct, Below are 2 other ways to achieve expected output
Using inner join on same table
select a.*
from workflow_status_history a
join (
select reference,max(id) id
from workflow_status_history
group by reference
) b using(reference,id)
where a.status = 'Lead Received';
Using correlated sub query
select a.*
from workflow_status_history a
where a.status = 'Lead Received'
and a.id = (select max(id)
from workflow_status_history
where reference = a.reference)
DEMO

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...

Mysql query substract issue

To begin with I have two tables:
feeds:(id,content_id,author) and
feeds_ratings:(id,feed_id (FK to feeds(id)),user_id,rating)
What I want to do is get the total rating difference for a specific author by a certain user.
To explain a bit more, let's say we have three rows in the feeds table, (1,3245,test),(2,3215,test),(3,3122,test) and tree rows on the feeds_ratings table, (1,1,12,like), (1,2,12,like),(1,3,12,dislike)
The input will be the user_id and the author and I want the output to be the difference between the total dislikes and likes by the input user, for the specific input author. (In this example it will be 1 because of the two likes and the one dislike.
How can that be implemented in a mysql query? I tried searching and some code of my own but I can't make it work, so any help is appreciated!
Something like this will work using SUM with CASE -- add 1 for likes and -1 for dislikes:
SELECT SUM(CASE WHEN fr.rating = 'like' THEN 1 ELSE -1 END) TotalLikes
FROM Feeds F
INNER JOIN Feeds_Ratings FR ON F.id = FR.feed_id
WHERE F.author = 'test'
AND FR.user_id = 12
SQL Fiddle Demo
Obviously replace author and user_id with the appropriate values -- these are just for your sample input.

Get max row per group from a related table

This is my first time asking a question on here. It has been very helpful with learning.
I am trying to select a table and getting only rows that have a maximum value for its particular group in another table. One of the best answers that is very close but not quite there is this one (SQL Select only rows with Max Value on a Column) but it only relates to a single table. I have found some others with multiple table but not sure how exactly to use it.
I have a table with (simplified)
prodID, quantity, mach, etc
I then have a table with
prodStatusID, prodID, userID, subStatusID
a last table with sub status names
subStatusID, subStatusName
I am trying to get a table with all of the first table and the second table but only with the row that has the maximum status number and include the right status name.
My other concern which may not matter now but in a year or two when this thing starts to really fill up is performance. I dont know bad it is to have select inside a select but if I am trying to return all productions then it will be doing a query for every production.
Just to be clearer. in the second table prodStatus there might be 2 rows with prodID of 4 but the subStatusID for the first one would be 1 and the second one would be 2. The userID will be different. All I want to get back is the second row because it has the highest status number and I need the userID and statusName associated with that row.
I have been googling for 2 days to get this answer and I saw 1 about auctions but I just dont fully understand it even after researching it.
You need to create a subquery which get the maximum value of subStatusID for each prodID.
SELECT a.*, -- select only columns that you want to show
c.*, -- asterisks means all columns
d.*
FROM table1 a
INNER JOIN
(
SELECT prodID, max(subStatusID) maxID
FROM table2
GROUP BY prodID
) b ON a.prodID = b.prodID
INNER JOIN table2 c
ON b.prodID = c.prodID AND
b.maxID = c.subStatusID
INNER JOIN table3 d
ON c.subStatusID = d.subStatusID