update row if count(*) > n - mysql

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

Related

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

run a query if a value on the last row is correct

Id like to run a query only if a value in the last row is correct. In my exemple if the value in ColumnA is 1 on the last row then i want to run MyQuery. But if the value is not 1 stop there and do nothing.
i've try with case and count(*) and also with If exists. but i keep getting error
SELECT CASE WHEN ((SELECT COUNT(*) FROM
(Select a.* from table as a order by a.index desc limit 1) as b
where b.ColumnA = 1)) > 0 )
THEN (MyQuery)
END
i've also try with if exists but it doesn'work either
if exists Select b.* from (Select a.* from table as a order by a.index desc limit 1) where b.ColumnA = 1
begin
(MyQuery)
end
can you point me what wrong in those query or maybee there's a better way to achive this.
EDIT. This query will be run on a trigger after each insert in that table the goal is to avoid running MyQuery on row that dont required it. MyQuery is a bit slow and most row dont required it to run.
I think we can rephrase your logic here to make it work as you want:
WITH cte AS (
SELECT ColumnA, ROW_NUMBER() OVER (ORDER BY index DESC) rn
FROM yourTable
)
(your query here)
WHERE (SELECT ColumnA FROM cte WHERE rn = 1) = 1;
The WHERE clause above would return either true or false, and would apply to all records in the potential result set from your query. That is, if the ColumnA value from the "last" record were 1, then you would get back the entire result set, otherwise it would be empty set.
Assuming your version of MariaDB supports neither ROW_NUMBER nor CTEs, then use:
(your query here)
WHERE (SELECT ColumnA FROM yourTable ORDER BY index DESC LIMIT 1) = 1;
It depends on what your query is.
INSERT ...
SELECT ... WHERE ... -- this could lead to zero rows being inserted
DELETE ...
WHERE NOT EXISTS ( SELECT ... ) -- this could lead to zero rows being deleted
UPDATE t1 JOIN t2 ... -- the JOIN may cause no rows to be updated
Note:
(Select a.* from table as a order by a.index desc limit 1) as b
where b.ColumnA = 1)) > 0 )
can be simplified (and sped up) to
( ( SELECT ColumnA FROM table ORDER BY index DESC LIMIT 1 ) = 1 )
Note that that is a true/false "expression", so it can be used in various places.

How to combine two fields value in mysql

This is output
but i want
in this table tb_user_connection i get 4 row but i want only 3 so, if i get connection in 1 and 2 so not get repeated column.
i try below query and this is codeigniter project.
my query:
SELECT * FROM `tb_user_connection` WHERE `connection_type` = 'a' AND (user_id = '1' OR connection_id='1')
You can do this:
select *
from your_table
where user_id <= connection_id
union all
select *
from your_table t
where user_id > connection_id
and not exists (
select 1
from your_table t1
where t1.user_id = t.connection_id
and t1.connection_id = t.user_id
) t1;

Select rows around a specified row, combined with WHERE and ORDER BY

I have a table with 3 fields: id, public, date.
How can I make a MySQL query so that the rows are ordered by date, rows having public = false are omitted, and only the 5 rows before and the 5 rows after the row with id = x are fetched? (The row with id = x can of course also be fetched.)
Assuming "before" and "after" refer to the date, then you can use union all:
(select t.*
from table t
where t.public = false and
t.date <= (select t2.date from table t2 where t2.id = x)
order by date desc
limit 6
)
union all
(select t.*
from table t
where t.public = false and
t.date > (select t2.date from table t2 where t2.id = x)
order by date asc
limit 5
)
The first subquery gets 6 rows whose date is one or before the "x" date. The second gets 5 rows strictly afterwards.

Select a row with least value of a column using where and group by

Sample table:
id------user_id------grade_id------time_stamp
1---------100----------1001---------2013-08-29 15:07:38
2---------101----------1002---------2013-08-29 16:07:38
3---------100----------1001---------2013-08-29 17:07:38
4---------102----------1003---------2013-08-29 18:07:38
5---------103----------1004---------2013-08-29 19:07:38
6---------105----------1002---------2013-08-29 20:07:38
6---------100----------1002---------2013-08-29 21:07:38
I want to select rows whose user_id = 100 group by grade_id only if its time_stamp is least for that particular grade_id.
so, from the above table, it should be:
row 1 because its time_stamp is least for that value of grade_id(1001)
but not row 2 because I only want 1 row for a particular grade_id
also not row 6 because that particular grade_id has least value for user_id 105.
I tried few things, which are too basic and obviously not worth posting.
Thank You
You could try nested queries:
SELECT grade_id, COUNT(grade_id) FROM SAMPLE_TABLE ST WHERE time_stamp = (SELECT MIN(time_stamp) FROM SAMPLE_TABLE STT WHERE STT.grade_id = ST.grade_id) AND user_id = 100 GROUP BY grade_id;
In this case, the nested query will give you the minimun timestamp for each specific 'grade_id' and you can use it in your WHERE filter.
SELECT t.*
FROM tableX AS t
JOIN
( SELECT grade_id, MIN(time_stamp) AS time_stamp
FROM tableX
GROUP BY grade_id
) AS g
ON g.grade_id = t.grade_id
AND g.time_stamp = t.time_stamp
WHERE t.user_id = 100 ;