SQL: update value based on following rows data - mysql

I have a table customer_service_types in phpmyadmin with these fields:
id (int) | customer_id (int) | service_type_int (int) | price (decimal) | created (datetime) | card (tinyint)
Here are some entries:
The application when the user adds a new service for a customer with a custom created date, is creating another service with id : 3 (payment) with today's date. I want to update all payments with the date of the following services.
So what I want to do is update the created field of the payments with the created value of the following services for specific client. So for example I need to update created of id: 168 with the value of created for the same customer_id but only of the following row if it has service_type_id != 3.
Tried so far:
UPDATE customer_service_types cst
SET cst.created = (SELECT created
FROM customer_service_types cst2
WHERE cst.id = (cst2.id - 1) AND cst2.service_type_id <> 3
)
WHERE cst.service_type_id = 3;
But I get this error:
You can't specify target table 'cst' for update in FROM clause
And I don't know if this query will produce the desired result

You can transfer your subquery to a Derived Table form instead. Try the following:
UPDATE customer_service_types AS cst
JOIN
(
SELECT id, created
FROM customer_service_types
WHERE service_type_id <> 3
) AS cst2
ON cst.id = (cst2.id - 1)
SET cst.created = cst2.created
WHERE cst.service_type_id = 3;

You can use a join:
UPDATE customer_service_types cst JOIN
customer_service_types cstnext
ON cstnext.id = cst.id and cstnext.service_type_id <> 3
SET cst.created = cstnext.created
WHERE cst.service_type_id = 3;

Related

MySQL Trigger Setting All Other Values to NULL When Run

I have two tables, Accounts and Person:
CREATE TABLE Person(
id INT NOT NULL PRIMARY KEY,
Person_Name VARCHAR(17) NOT NULL,
P_Location INT NOT NULL
);
INSERT INTO Person VALUES (1,"Adam",300),(2,"Betty",10),(3,"Louis",60);
CREATE TABLE Accounts(
Person_id INT PRIMARY KEY,
Balance INT DEFAULT 200);
INSERT INTO Accounts VALUES (1,2000),(2,1350),(3,800);
And one trigger, Bonuses:
CREATE TRIGGER Bonuses
AFTER UPDATE ON Person
FOR EACH ROW
UPDATE Accounts
SET Balance = CASE WHEN (SELECT P_Location FROM Person WHERE id = Person_id) = 3 THEN Balance - 150
WHEN (SELECT P_Location FROM Person WHERE id = Person_id) = 7 THEN Balance + 100
WHEN (SELECT P_Location FROM Person WHERE id = Person_id) = 15 THEN Balance - 30
WHEN (SELECT P_Location FROM Person WHERE id = Person_id) = 1 THEN Balance + 200
END;
And I want to make the trigger update the Accounts table according to certain instructions whenever the P_Location on the Person table changes to one of a select few values (3,7,15 and 1). However, as things are they result is incorrect. Assume I run the above code, the tables I get are:
Person
id
Player_Name
P_Location
1
Adam
300
2
Betty
10
3
Louis
60
Accounts
Person_id
Balance
1
2000
2
1350
3
800
Now if I run UPDATE Person SET P_Location = 3 WHERE id = 1; then the Accounts table should yield:
Person_id
Balance
1
1850
2
1350
3
800
However, what I get is
Person_id
Balance
1
1850
2
NULL
3
NULL
Any idea of what I'm doing wrong?
Well, that code did exactly what you said, though it wasn't what you meant!
That's the thing about UPDATE queries, EVERY row will get an update unless a WHERE clause is used to filter what actually gets modified. Nothing is found from the CASE with most records, so any of those will get assigned to NULL. To see this behavior, check this fiddle example.
However, there is good news, all that is needed in the trigger is to add a WHERE clause. Note that I simplified the CASE handling make use of the UPDATE trigger's NEW references:
CREATE TRIGGER Bonuses
AFTER UPDATE ON Person
FOR EACH ROW
UPDATE Accounts
SET Balance = CASE WHEN NEW.P_Location = 3 THEN Balance - 150
WHEN NEW.P_Location = 7 THEN Balance + 100
WHEN NEW.P_Location = 15 THEN Balance - 30
WHEN NEW.P_Location = 1 THEN Balance + 200
END
WHERE Person_id = NEW.id;
So starting with:
Then run: UPDATE Person SET P_Location = 3 WHERE id = 1;
Gives:
Example fiddle with your tables, the simplified trigger case handling, and the output examples from the update query.

Update max value from a mysql table

I'm trying to update a table's value using max(col) in a subquery, but for some reason, it's updating all values that match the user_id column I'm using.
This is my table structure:
user_id | steps | in_date
--------+-------+-----------
8 |10 | 1522246892
8 |10 | 1522250713
7 |10 | 1522250799
And this is my query:
UPDATE userdata
SET steps = (steps + 20)
WHERE user_id = 8
AND in_date = (SELECT max(in_date) WHERE user_id = 8);
I expected it to update only the second column, but it instead updates both columns with user_id = 8. Why isn't it working as expected? What am I doing wrong?
Edit: Thanks to Manoj's comment, I changed the query to the following, and it works:
UPDATE userdata
SET steps = (steps + 20)
WHERE user_id = 8
ORDER BY in_date DESC LIMIT 1;
Doing it his way is even better, since I don't have to run two queries, and already gets the highest one by id.
You will encounter sql error "You can't specify target table 'userdata' for update in FROM clause" when you use same table in the sub-query to update the same table.
Its strange that you say its running because,
- you missed from clause in your sub-query
- you can't use same table
Can you please be more specific.
seems you missed the from clause in subselect and for avoid conflit between select and updated value you could build a temp table using ainner subselect
UPDATE userdata
SET steps = (steps + 20)
WHERE user_id = 8
AND in_date = ( select max_date from (
SELECT max(in_date) max_date FROM userdata WHERE user_id = 8) t);
and in this way the AND clause could be always true

MySql update field value based on other field values

Im trying to create a query that will do the following:
Here is my table:
ID total paid own status
------------------------------------------------
1 100.00 100.00 100.00 0
and here is the query:
$total = 50;
UPDATE cart table SET paid = $total, status = CASE WHEN $total >= own THEN 1 ELSE 2 END;
The idea is if total amount of field "total" is equal or greater than amount in field "own" update field "status" to 1, otherwize to 2.
Im not sure if I can do this with only one query, or I will need to update cart table, pull the results, perform calculations and than update again.
Try this:
UPDATE cart table SET paid = $total, status = If($total >= own, 1, 2)
Where ID = 1;
Its called conditional update query.
Your query will do what you mentioned. Except table keyword you defined it should not be there otherwise your query is fine:
UPDATE cart
SET paid = $total,
status = CASE WHEN $total >= own THEN 1 ELSE 2 END
WHERE
--your condition
A bit of a hack and SQL version dependent but what about:
UPDATE cart SET paid=$total, status=-($total>=own)+2;

inserting into one field value with different conditions using mysql

i have table visits
with columns like the below
visit_id
member_id
logout_datetime(format like this ...'yyyy-MM-dd HH:mm:ss')
visit_message (like accept, refuse)
i want to insert the logoutdatetime value into visits table
where member_id = 1 and visit_message = "accept"
how can i do that using mysql
would any one pls help on this ...
many thanks in advance..
What about:
UPDATE visits
SET logout_datetime = NOW()
WHERE member_id = 1 AND visit_message = "accept"
This is an UPDATE, not an INSERT, since the row already exists where member_id = 1 and visit_message = 'accept':
UPDATE visits SET logout_datetime = '2011-10-24 07:01:22' WHERE member_id = 1 AND visit_message = 'accept';
If you intend to use the current timestamp for logout_datetime, substitute the function NOW() for the literal date in my example.

MySQL - What's wrong with the query?

I am trying to query a database to find the following.
If a customer searches for a hotel in a city between dates A and B, find and return the hotels in which rooms are free between the two dates.
There will be more than one room in each room type (i.e. 5 Rooms in type A, 10 rooms in Type B, etc.) and we have to query the database to find only those hotels in which there is at least one room free in at least one type.
This is my table structure:
**Structure for table 'reservations'**
reservation_id
hotel_id
room_id
customer_id
payment_id
no_of_rooms
check_in_date
check_out_date
reservation_date
**Structure for table 'hotels'**
hotel_id
hotel_name
hotel_description
hotel_address
hotel_location
hotel_country
hotel_city
hotel_type
hotel_stars
hotel_image
hotel_deleted
**Structure for table 'rooms'**
room_id
hotel_id
room_name
max_persons
total_rooms
room_price
room_image
agent_commision
room_facilities
service_tax
vat
city_tax
room_description
room_deleted
And this is my query:
$city_search = '15';
$check_in_date = '29-03-2010';
$check_out_date = '31-03-2010';
$dateFormat_check_in = "DATE_FORMAT('$reservations.check_in_date','%d-%m-%Y')";
$dateFormat_check_out = "DATE_FORMAT('$reservations.check_out_date','%d-%m-%Y')";
$dateCheck = "$dateFormat_check_in >= '$check_in_date' AND $dateFormat_check_out <= '$check_out_date'";
$query = "SELECT $rooms.room_id,
$rooms.room_name,
$rooms.max_persons,
$rooms.room_price,
$hotels.hotel_id,
$hotels.hotel_name,
$hotels.hotel_stars,
$hotels.hotel_type
FROM $hotels,$rooms,$reservations
WHERE $hotels.hotel_city = '$city_search'
AND $hotels.hotel_id = $rooms.hotel_id
AND $hotels.hotel_deleted = '0'
AND $rooms.room_deleted = '0'
AND $rooms.total_rooms - (SELECT SUM($reservations.no_of_rooms) as tot
FROM $reservations
WHERE $dateCheck
GROUP BY $reservations.room_id) > '0'";
The number of rooms already reserved in each room type in each hotel will be stored in the reservations table.
The thing is the query doesn't return any result at all. Even though it should if I calculate it myself manually.
I tried running the sub-query alone and I don't get any result. And I have lost quite some amount of hair trying to de-bug this query from yesterday. What's wrong with this? Or is there a better way to do what I mentioned above?
Edit: Code edited to remove a bug. Thanks to Mark Byers.
Sample Data in reservation table
1 1 1 2 1 3 2010-03-29 2010-03-31 2010-03-17
2 1 2 3 3 8 2010-03-29 2010-03-31 2010-03-18
5 1 1 5 5 4 2010-03-29 2010-03-31 2010-03-12
The sub-query should return
Room ID : 1 Rooms Booked : 7
Room ID : 2 Rooms Booked : 8
But it does not return any value at all.... If i remove the dateCheck condition it returns
Room ID : 2 Rooms Booked : 8
Your problem is here:
$rooms.total_rooms - (SELECT SUM($reservations.no_of_rooms) as tot,
$rooms.room_id as id
FROM $reservations,$rooms
WHERE $dateCheck
GROUP BY $reservations.room_id) > '0'"
You are doing a subtraction total_rooms - (tot, id) where the first operand is a scalar value and the second is a table with two columns. Remove one of the columns in the result set and make sure you only return only one row.
You also should use the JOIN keyword to make joins instead of separating the tables with commas. That way you won't forget to add the join condition.
You probably want something along these lines:
SELECT column1, column2, etc...
FROM $hotels
JOIN $rooms
ON $hotels.hotel_id = $rooms.hotel_id
JOIN (
SELECT SUM($reservations.no_of_rooms) as tot,
$rooms.room_id as id
FROM $reservations
JOIN $rooms
ON ??? /* Aren't you missing something here? */
WHERE $dateCheck
GROUP BY $reservations.room_id
) AS T1
ON T1.id = room_id
WHERE $hotels.hotel_city = '$city_search'
AND $hotels.hotel_deleted = '0'
AND $rooms.room_deleted = '0'
AND $rooms.total_rooms - T1.tot > '0'