How to SELECT inside UPDATE in a MYSQL query - mysql

Multiples posts asking about it already exist, solutions have been given and they worked for my previous queries.
But it won't work now that I try the same query to SELECT inside an UPDATE from the same table.
Here are the differents queries I tried :
UPDATE usersDB.random
SET total_partners =
(
SELECT total_partners FROM usersDB.random
WHERE year = 2022
)
WHERE uid = 1
and
UPDATE usersDB.random
SET total_partners =
(
SELECT total_partners
FROM usersDB.random
AS x
WHERE year = 2022
)
WHERE uid = 1
But I get this error message everytime :
Error Code: 1093. You can't specify target table 'random' for update
in FROM clause
The same query but with two differents table work tho :
UPDATE usersDB.random
SET total_partners =
(
SELECT COUNT(*)
FROM usersDB.partners AS x
)
WHERE (month = MONTH(CURRENT_DATE()) and year = YEAR(CURRENT_DATE())) OR uid = 1
How can I make it work when it's the same table ? Version of Mysql is 8.0
Here is how the table random looks like https://i.imgur.com/HYSS5DJ.png
I use Mysql Workbench to send the queries

Because you did not provide a simple test data Create Table/Insert Into (copypaste text) script I had to experiment with my own table scheme. See this with aTemp temporary table how I use two-level inner table.
Update person Set
city=(
Select Concat(now(),"-",aTemp.val1) from
(select count(*) as val1 from person) as aTemp
)
Where id=4
So out of my mind it may translate to this query.
UPDATE usersDB.random
SET total_partners=(
Select aTemp.val1 From
(SELECT count(*) as val1 FROM usersDB.random WHERE year=2022) aTemp
)
WHERE uid = 1

Related

Referencing the outer query from the case of a subquery in SQL server 2014

I'm trying to build a stored procedure that transforms a lot of data from a very condensed table with a key to find any given information, to a wide table with all of the information in columns per group. This is to preprocess information and alleviate at point of use. I've structured my query like below.
Select distinct a.group_,
, (select value from mytable b where a.group = b.group) as Information 1
from mytable a
However, when I want to use a case statement, it breaks the reference to the outer query
Select distinct a.group_,
, (case
when (select value from mytable b where a.group = b.group) is not null -- this breaks
then (select value from mytable b where a.group = b.group)
else (select anothervalue from mytable b where a.group = b.group)
end ) as Information 1
from mytable a
I thought about a work around with a simple case to find if the value is null, and execute an else statement, but I found that my 'is not null' didn't work in the when statment, and I needed to reference the outer query anyways for the else. So ultimately, I need some method to be able to conditionally select values for one column and have it tied to the group that I'm trying to transform. Any help would be appreciated, thanks.
Edit:
Below is an example. To clarify, I need to be able to conditionally select information from potentially multiple sources together into the same column for any given group. It's also going to be working on a large amount of data so I need it to be as minimally computationally intensive as possible. From this table, I will combine all of these groups in different combinations to have one line per collection of groups, with each group at least one column in the final table. It's a little more complicated than that, but that's the general idea.
if OBJECT_ID(N'tempdb..#tt1') is not null
drop table #tt1
declare #counter INT
set #counter = 1
create table #tt1 (group_ int, id1 int, id2 int, info varchar(10))
while (#counter <= 5)
begin
insert into #tt1 (group_, id1, id2, info)
select #counter, #counter, #counter, CONCAT('info ', cast(#counter as varchar(10)))
set #counter = #counter +1
end
set #counter = 1
while (#counter <= 5)
begin
insert into #tt1 (group_, id1, id2, info)
select #counter, #counter, #counter+1, CONCAT('info ', cast(#counter+5 as varchar(10)))
set #counter = #counter +1
end
select distinct a.group_,
(select info from #tt1 b where a.group_ = b.group_ and id1 = 1 and id2=2) as Group1Info6
from #tt1 a
--The above works fine
select distinct a.group_,
(case
when (select info from #tt1 b where A.group_ = b.group_ and id1=1 and id2 =2) is < 6
then (select info from #tt1 b where A.group_ = b.group_ and id1=1 and id2 =2)
else (select info from #tt1 b where A.group_ = b.group_ and id1=1 and id2=1)
end) as Group1info
from #tt1 a
--The above does not.
Edit2:
My desired results would look something like this. In my actual data there are many group 1's, with many group 1 info columns.
group_
Group1Info
Group2Info
Group3Info
Group4Info
Group5Info
1
info
2
info
3
info
4
info
5
info
Then I'll present the information more cleanly to the end user like this.
group_
Group1Info
Group2Info
Group3Info
Group4Info
Group5Info
1-5
info
info
info
info
info

Update int column values mysql

I want to update an int column values where I want to set the value of the int column to 1 for the latest inserted record and increment it by 1 for all the preceding records until the record with Primary Id = 1 is reached. I've some 1400 records so what I want is that 1400 record should get 1 for that int column and the 1399 should get 2 and so on until all records are finished. How should the update query be written so that can be achieved. Thanks
Note this is going to be one time operation.
If you are running MySQL 8.0, you can use row_number() for this. Assuming that you have a unique, ordering column called id, and that you want to update column new_id:
update mytable t
inner join (select id, row_number() over(order by id desc) new_id from mytable) x
on t.id = x.id
set t.new_id = x.new_id
In earlier versions, one option is to emulate the window function with a user variable:
update mytable t
inner join (
select t.id, #new_id := #new_id + 1 new_id
from (select id from mytable order by id desc) t
cross join (select #new_id := 0) x
) x on t.id = x.id
set t.new_id = x.new_id

SQL Update for Selected rows

I have this SQL Code:
SELECT * FROM `clients_branches`
WHERE NULLIF(clients_branches.invoice_email, '') IS NULL
GROUP BY client_code
HAVING COUNT(*) = 1
It returns all rows which appears only once in the database, also it returns only the ones with no email set. Now I need to apply UPDATE function to all of this select statement. How could I do it? I need to set clients_branches.invoice_send to 0 for all these rows.
I can't seem to use HAVING COUNT on UPDATE statement like this:
UPDATE `clients_branches`
SET clients_branches.invoice_send = 0
WHERE NULLIF(clients_branches.invoice_email, '') IS NULL
HAVING COUNT(*) = 1
Without HAVING COUNT I will change all of the rows which repeats at least once in this table. And I need to change only the ones with count = 1.
You could use a join for allow the use of the table for update and the result of your query
update `clients_branches`
JOIN
(
select client_code, count(*)
FROM `clients_branches`
WHERE NULLIF(clients_branches.invoice_email, '') IS NULL
group by client_code
HAVING COUNT(*) = 1
) t on t.client_code = `clients_branches`.client_code
set clients_branches.invoice_send = 0
;

Update entire column with 1 to n

I have table where there is column named uid , It uses Autoincrement and its updated with 1,2,3 etc. Now I have cron job that deleted rows older than 2 days.So now my uid column is 2345 to n..I want to reset it to 1 to n again.I tried below code
UPDATE `tv` SET `uid` = ''
I was thinking to loop through all rows and update uid via php script, Is there any other alternative with single SQL command ?
You can try something like this:
UPDATE `tv` t
set t.`uid` = (SELECT count(*)
from `tv` s
WHERE t.`uid` >= s.`uid`)
This will count how many uid's are there that are smaller or equal then the one being updated, so when the first UID, lets say 2345 is being updated, there is only 1 uid that is smaller/equal to him so it will get the value 1 and so on...
EDIT: Try this-
UPDATE `tv` t
INNER JOIN(SELECT s.`uid`,count(*) as cnt
from `tv` s
INNER JOIN `tv` ss
ON(s.`uid` >= ss.`uid`)
GROUP BY s.`uid) tt
ON(t.`uid`=tt.`uid`)
SET t.`uid` = tt.cnt
Why don't decrease the uid by:
update tv set uid = uid -1

update row if count(*) > n

my DB has this structure:
ID | text | time | valid
This is my current code. I'm trying to find a way to do this as one query.
rows = select * from table where ID=x order by time desc;
n=0;
foreach rows{
if(n > 3){
update table set valid = -1 where rows[n];
}
n++
}
I'm checking how many rows exist for a given ID. Then I need to set valid=-1 for all rows where n >3;
Is there a way to do this with one query?
You can use a subquery in the WHERE clause, like this:
UPDATE table
SET valid=-1
WHERE (
SELECT COUNT(*)
FROM table tt
WHERE tt.time > table.time
AND tt.ID = table.ID
) > 3
The subquery counts the rows with the same ID and a later time. This count will be three or less for the three latest rows; the remaining ones would have a greater count, so their valid field would be updated.
Assuming that (id,time) has a UNIQUE constraint, i.e. no two rows have the same id and same time:
UPDATE
tableX AS tu
JOIN
( SELECT time
FROM tableX
WHERE id = #X -- the given ID
ORDER BY time DESC
LIMIT 1 OFFSET 2
) AS t3
ON tu.id = #X -- given ID again
AND tu.time < t3.time
SET
tu.valid = -1 ;
update table
set valid = -1
where id in (select id
from table
where id = GIVEN_ID
group by id
having count(1) >3)
Update: I really like dasblinkenlight's solution because is very neat, but I wanted to try also to do it in my way, a quite verbose one:
update Table1
set valid = -1
where (id, time) in (select id,
time
from (select id,time
from table1
where id in (select id
from table1
group by id
having count(1) >3)
-- and id = GIVEN_ID
order by time
limit 3, 10000000)
t);
Also in SQLFiddle
to do it for all ids, or only for one if you set a where in the a subquery
UPDATE TABLE
LEFT JOIN (
SELECT *
FROM (
SELECT #rn:=if(#prv=id, #rn+1, 1) AS rId,
#prv:=id AS id,
TABLE.*
FROM TABLE
JOIN ( SELECT #prv:=0, #rn:=0 ) tmp
ORDER BY id, TIMESTAMP
) a
WHERE rid > 3
) ordered ON ordered.id = TABLE.id
AND ordered.TIMESTAMP = TABLE.TIMESTAMP
AND ordered.text = TIMESTAMP.text
SET VALID = -1
WHERE rid IS NOT NULL