I have 2 tables: tbl_taxclasses, tbl_taxclasses_regions
This is a one to many relationship, where the main record ID is classid.
I have a column inside the first table called regionscount
So, I create a Tax Class, in table 1. Then I add regions/states in table 2, assigning the classid to each region.
I perform a SELECT statement to count the regions with that same classid, and then I perform an UPDATE statement on tbl_taxclasses with that number. I update the regionscount column.
This means I'm writing 2 queries. Which is fine, but I was wondering if there was a way to do a SELECT statement inside the UPDATE statement, like this:
UPDATE `tbl_taxclasses` SET `regionscount` = [SELECT COUNT(regionsid) FROM `tbl_taxclasses_regions` WHERE classid = 1] WHERE classid = 1
I'm reaching here, since I'm not sure how robust MySQL is, but I do have the latest version, as of today. (5.5.15)
You could use a non-correlated subquery to do the work for you:
UPDATE
tbl_taxclasses c
INNER JOIN (
SELECT
COUNT(regionsid) AS n
FROM
tbl_taxclasses_regions
GROUP BY
classid
) r USING(classid)
SET
c.regionscount = r.n
WHERE
c.classid = 1
Turns out I was actually guessing right.
This works:
UPDATE `tbl_taxclasses`
SET `regionscount` = (
SELECT COUNT(regionsid) AS `num`
FROM `tbl_taxclasses_regions`
WHERE classid = 1)
WHERE classid = 1 LIMIT 1
I just needed to replace my brackets [] with parenthesis ().
Related
I have a new column in my database and I need fill it up with the value of the same column from one specific row. I want to create a feature "copy to all"
For example add the same price to all the products taken from the first row:
ID NAME PRICE
1 PROD1 5
2 PROD2 0
3 PROD3 0
4 PROD4 0
I am trying to select the PRICE of the first row (ID 1) and copy it to all the other rows.
I have tried:
UPDATE PRODUCTS SET PRICE = (select PRICE from PRODUCTS where ID = 1);
I want to end up with this
ID NAME PRICE
1 PROD1 5
2 PROD2 5
3 PROD3 5
4 PROD4 5
But I get this error:
Table 'PRODUCTS' is specified twice,
both as a target for 'UPDATE' and as a separate source for data
I tried specifying each table separately
UPDATE PRODUCTS as a SET a.PRICE = (select b.PRICE from PRODUCTS as b where b.ID = 1);
But I get the same error.
Table 'a' is specified twice,
both as a target for 'UPDATE' and as a separate source for data
Maybe I have to create a temporary table and copy from it?
Any hints on how to accomplish this?
Thanks.
You can do it by nesting the select query:
UPDATE PRODUCTS
SET PRICE = (
select PRICE from (select PRICE from PRODUCTS where ID = 1) t
);
See the demo.
Another way to do it, with a self CROSS JOIN:
UPDATE PRODUCTS p CROSS JOIN (
select PRICE from PRODUCTS where ID = 1
) t
SET p.PRICE = t.PRICE;
See the demo.
This wouldn't work logically, as SQL would try to fetch the data it is updating.
Try running your nested statement select PRICE from PRODUCTS where ID = 1 seperately, saving the response and then running your main statement: "UPDATE PRODUCTS SET PRICE = " + newPrice
If this is a SQL script, i suggest you to break it into 2 queries using a variable:
select #var := PRICE from PRODUCTS where ID = 1;
UPDATE PRODUCTS SET PRICE = #var;
Variables are much more easier than temporary table in my opinion.
I've not fully tested, but the syntax should be that
After looking at many answers on other posts and websites, (none of them had the precise answer), I found the solution with this query, and yes we need temp tables:
CREATE TEMPORARY TABLE tmptable SELECT ID, PRICE FROM PRODUCTS WHERE ID = 1;
UPDATE PRODUCTS SET `PRICE` = (select tmptable.`PRICE` from tmptable where tmptable.ID = 1);
BUT! #forpas solution's is really good and works without creating a temp table.
enjoy.
NEW question: is the temp table removed automatically? Leave me a comment.
Cheers
Definitely a basic question, but I couldn't find an example.
I'm writing a procedure which merges two rows into the good row. It moves all child rows' ids to being the correct one, replaces all NULL values with available values in the row being removed before finally deleting the 'bad' row.
What I have so far is this:
CREATE DEFINER=`danielv`#`%`
PROCEDURE `emp_merge`(IN `#core_emp_id` int, IN `#bad_emp_id` int)
BEGIN
UPDATE claim SET employee_id = #core_emp_id
WHERE employee_id = #bad_emp_id;
WITH bad_employee_values AS (
SELECT * FROM employee WHERE employee_id = #bad_emp_id
)
UPDATE employee SET
employee.employment_date = COALESCE(employee.employment_date, bad_employee_values.employment_date),
WHERE employee_id = #core_emp_id;
DELETE FROM employee WHERE employee_id = #bad_emp_id;
END
However, I'm getting non-descript error messages and I'm not sure why. I suspect there's an issue with how I'm handling my CTE and coalesce function, but I'm not sure where the gap in my understanding is.
In this statement :
WITH bad_employee_values AS (SELECT * FROM employee WHERE employee_id = #bad_emp_id)
UPDATE employee SET
employee.employment_date = COALESCE(employee.employment_date, bad_employee_values.employment_date),
WHERE employee_id = #core_emp_id;
You are defining CTE bad_employee_values but you are not using it in the UPDATE part of the query, hence you cannot access its columns : for MySQL, bad_employee_values.employment_date is unknown.
It looks like you could simply avoid a CTE here. You could just self-join the table, like so :
UPDATE employee e_core
INNER JOIN employee e_bad ON e_bad.employee_id = #bad_emp_id
SET e_core.employment_date = e_bad.employment_date,
WHERE employee_id = #core_emp_id AND e_core.employment_date IS NULL
This query will simply select the record identified by #core_emp_id, join it with the corresponding "bad" record, and copy the value of employment_date. The second condition in the WHERE clause prevents records whose employment_date is not null from being selected.
I am trying to update a value to be NULL where tracker_unique_id can be found in ab_split_tracker_unique_link where that ones ab_split_id can be found in a 3rd table ab_split_candidate.
I cant do it by giving it different values as they can be different from user to user on locals
UPDATE trustpilot_links SET `invite_after_enquiry` = NULL
WHERE EXISTS (
SELECT tracker_unique_id, ab_split_tracker_unique_link.ab_split_candidate_id
FROM ab_split_tracker_unique_link
WHERE EXISTS (
SELECT ab_split_candidate_id
FROM ab_split_candidate LEFT JOIN ab_split
ON ab_split_candidate.ab_split_id = ab_split.ab_split_id WHERE ab_split.reference="review_invite_after_enquiry"
)
);
Edit:
Table examples
Trustpilot Links
trustpilot_link_id | invite_after_enquiry | tracker_unique_id
1 1 123
2 0 1234
ab_split_tracker_unique_link
tracker_unique_id | ab_split_id
1234 32
Ab Split
ab_split_id | reference
32 review_invite_after_enquiry
I want to set values to null if there tracker cannot be found in table ab_split_tracker_unique_link with an ab_split_id that is equal to review_invite_after_enquiry in ab_split
Your subqueries are not related to their parent queries as they should be. Let's look at your inner-most query:
SELECT ab_split_candidate_id
FROM ab_split_candidate
LEFT JOIN ab_split ON ab_split_candidate.ab_split_id = ab_split.ab_split_id
WHERE ab_split.reference = 'review_invite_after_enquiry'
Well, first of all your WHERE clause dismisses outer-joined records, so this is essentially an INNER JOIN. But then: either there are such records or not. This has nothing to do with the record your are potentially updating, nor with the ab_split_tracker_unique_link you are looking up.
So either you are updating all records or none.
We would rather expect something like
UPDATE trustpilot_links tl
SET invite_after_enquiry = NULL
WHERE EXISTS
(
SELECT *
FROM ab_split_tracker_unique_link stul
WHERE stul.tracker_unique_id = tl.tracker_unique_id
AND ...
);
So add WHERE clauses that relate the subqueries to their parent queries.
Hi I am making a webrowser game and I am trying to get monsters into my data base when I get the error:
Subquery returns more then 1 row
here is my code
INSERT INTO monster_stats(monster_id,stat_id,value)
VALUES
( (SELECT id FROM monsters WHERE name = 'Necroborg!'),
(SELECT id FROM stats WHERE short_name = 'atk'),
2);
any ideas how to fix this problem?
Try use LIMIT 1
INSERT INTO monster_stats(monster_id,stat_id,value) VALUES ((SELECT id FROM monsters WHERE name = 'Necroborg!' LIMIT 1),(SELECT id FROM stats WHERE short_name = 'atk' LIMIT 1),2);
Or you could use Insert from select, with join, if you have relations with 2 tables.
INSERT INTO monster_stats(monster_id,stat_id,value)
(SELECT monsters.id, stats.id, 2 as value FROM monsters
LEFT JOIN stats on monsters.id = stats.monsters_id
WHERE monsters.name = 'Necroborg!'
AND stats.short_name = 'atk'
)
MYSQL insert from select:
http://dev.mysql.com/doc/refman/5.1/en/insert-select.html
The problem is one or both of the following:
There is more than one monster named 'Necroborg!'.
There is more than on stat named 'atk'.
You need to decide what you want to do. One option (mentioned elsewhere) is to use limit 1 to get only one value from each statement.
A second option is to better specify the where clause so you get only one row from each table.
Another is to insert all combinations. You would do this with insert . . . select and a cross join:
INSERT INTO monster_stats(monster_id, stat_id, value)
SELECT m.id, s.id, 2
FROM (SELECT id FROM monsters WHERE name = 'Necroborg!') m CROSS JOIN
(SELECT id FROM stats WHERE short_name = 'atk');
A third possibility is that there is a field connecting the two tables, such as monster_id. But, based on the names of the tables, I don't think that is true.
Have is an example of the problem I'm facing. The database tables are a little different than usual, but needed to be setup this way.
Items: id, order_id, other fields
Items_Drinks: id, drinks, other fields
Orders: id, other fields
Orders_Drinks: id, drinks, other fields
I need to have an update query that will update the Orders_Drinks table with the sum of the Items_Drinks drinks field that have the same order_id as Orders_Drinks id field.
Items: 1 1 ...
Items: 2 1 ...
Items_Drinks: 1 4 ...
Items_Drinks: 2 5 ...
Orders: 1 ...
Orders_Drinks: 1 9 ...
The Orders_Drinks is currently correct, but if I were to update Items_Drinks with id of 1 to 5, I would need an update command to get Orders_Drinks with id 1 to equal 10.
It would be best if the command would update every record of the Orders_Drinks.
I know my database is not typical, but it is needed for my application. This is because the Drinks table is not needed for all entries. The Drinks table has over 5000 fields in it, so if every record had these details the database would grow and slow for no real reason. Please do not tell me to restructure the database, this is needed.
I am currently using for loops in my C# program to do what I need, but having 1 command would save a ton of time!
Here is my best attempt, but it gives an error of "invalid group function".
update Orders_Drinks join Items on Items.order_id=Orders_Drinks.id join Items_Drinks on Items_Drinks.id=Items.id set Orders_Drinks.drinks=sum(Item_Drinks.drinks);
I think this is what you're wanting.
Edited:
UPDATE `Order_Drinks` a
SET a.`drinks` = (SELECT SUM(b.`drinks`) FROM `Items_Drinks` b INNER JOIN `Items` c ON (b.`id` = c.`id`) WHERE a.`id` = c.`order_id`)
That should give you a total of 9 for the Order_Drinks table for the row id of 1.
This is assuming that Orders.id == Orders_Drinks.id and that Items.id == Items_Drinks.id.
You need to do an aggregation. You can do this in the join part of the update statement:
update Orders_Drinks od join
(select i.order_id, sum(id.drinks) as sumdrinks
from Items i join
Items_Drinks id
on id.id = i.id
) iid
on iid.order_id = od.id
set od.drinks = iid.sumdrinks;
Something like this will return the id from the orders_drinks table, along with the current value of the drinks summary field, and a new summary value derived from the related items_drinks tables.
(Absent the name of the foreign key column, I've assumed the foreign key column names are of the pattern: "referenced_table_id" )
SELECT od.id
, od.drinks AS old_drinks
, IFNULL(td.tot_drinks,0) AS new_drinks
FROM orders_drinks od
LEFT
JOIN ( SELECT di.orders_drinks_id
, SUM(di.drinks) AS tot_drinks
FROM items_drinks di
GROUP BY di.orders_drinks_id
) td
ON td.orders_drinks_id = od.id
Once we have SELECT query written that gets the result we want, we can change it into an UPDATE statement. Just replace SELECT ... FROM with the UPDATE keyword, and add a SET clause, to assign/replace the value to the drinks column.
e.g.
UPDATE orders_drinks od
LEFT
JOIN ( SELECT di.orders_drinks_id
, SUM(di.drinks) AS tot_drinks
FROM items_drinks di
GROUP BY di.orders_drinks_id
) td
ON td.orders_drinks_id = od.id
SET od.drinks = IFNULL(td.tot_drinks,0)
(NOTE: the IFNULL function is optional. I just used it to substitute a value of zero whenever there are no matching rows in items_drinks found, or whenever the total is NULL.)
This will update all rows (that need to be updated) in the orders_drinks table. A WHERE clause could be added (after the SET clause), if you only wanted to update particular rows in orders_drinks, rather than all rows:
WHERE od.id = 1
Again, to get to this, first get a SELECT statement working to return the new value to be assigned to the column, along with the key of the table to be updated. Once that is working, convert it into an UPDATE statement, moving the expression that returns the new value down to a SET clause.