update other rows in same table when one row is deleted - mysql

table Lets call it test
----------------------------
| id | catid | value |
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 1 | 3 |
| 5 | 1 | 4 |
| 6 | 1 | 5 |
| 7 | 2 | 2 |
----------------------------
Suppose i deleted the 2nd row from table. Now the table will become:
----------------------------
| id | catid | value |
| 1 | 1 | 1 |
| 3 | 2 | 1 |
| 4 | 1 | 3 |
| 5 | 1 | 4 |
| 6 | 1 | 5 |
| 7 | 2 | 2 |
----------------------------
So in catid1, there is no value 2. so the values will become, 1,3,4,5,.... so far so on....
Goal I need to update the values as subtracting 1 from their previous value so that the continuity is maintined (all values goes one up if the condition is met i.e. if bgger than the deleted value).
CODE
After delete, i am trying to perform an update query.
UPDATE `test`
SET `value` =
(
SELECT t.param FROM (
SELECT `value`-1 AS param
FROM `test`
WHERE
`catid` = 1 AND `value` > 2
) AS t
)
WHERE
`catid` = 1
AND `value` > 2
Now, the most inner query will return all the rows which has value >2 where as the update expects a scalar value of string or numeric.
So the question is how can i return the value in the innermost query with respect to what row is update query targeting?
e.g. If update query is trying to update the row with id 4, the innermost query will retreive the value column with the same row id only and give it back to outer query (after subtracting 1).
Is there any alternative approach in this?

JOIN that table with this subquery like this:
UPDATE test t1
INNER JOIN
(
SELECT
id, catid, value - 1 AS value
FROM test
WHERE catid = 1
AND value > 2
) AS t2 ON t1.id = t2.id
SET t1.value = t2.value;
SQL fiddle demo.
But there is no need for this subquery, you can do this directly like this:
UPDATE test t1
SET value = value - 1
WHERE catid = 1
AND value > 2;
However, You might need to fix that column id to match those values like this:
UPDATE test AS t1
INNER JOIN
(
SELECT
*,
(#rownum := #rownum + 1) AS newID
FROM test, (SELECT #rownum := 0) AS t
ORDER BY Id
) AS t2 ON t1.id = t2.id
SET t1.id = t2.newId,
t1.value = CASE
WHEN t1.catid = 1 AND t1.value > 2 THEN t2.value - 1
ELSE t1.value
END;
Updated SQL Fiddle Demo.

Related

how to get next/prev row from mysql by group

I have a table on MySQL like this:
ID Name Group
1 One A
2 Two B
3 Three A
4 Fore C
5 Five B
6 Six A
7 Seven B
I want to get the previous row/ next row in same group from my selected row. Like if I have selected row with ID=5, now how can I get the same group previous row(ID=2) when I haven't any information about the row and same with next row(ID=7).
You are looking for LEAD or LAG with Windows function, but it's was supported mysql higher version than 8.0. so you can instead write a subquery on select
look like this.
TestDLL
CREATE TABLE T(
ID int,
Name VARCHAR(100),
`Group` VARCHAR(5)
);
INSERT INTO T VALUES (1,'One','A');
INSERT INTO T VALUES (2,'Two','B');
INSERT INTO T VALUES (3,'Three','A');
INSERT INTO T VALUES (4,'Fore','C');
INSERT INTO T VALUES (5,'Five','B');
INSERT INTO T VALUES (6,'Six','A');
INSERT INTO T VALUES (7,'Seven','B');
Query
select *,IFNULL((
SELECT t2.ID
FROM T t2
WHERE t1.Group = t2.Group and t1.ID > t2.ID
ORDER BY t2.ID DESC
LIMIT 1
),t1.ID)previousID
,IFNULL((
SELECT t2.ID
FROM T t2
WHERE t1.Group = t2.Group and t1.ID < t2.ID
ORDER BY t2.ID
LIMIT 1
),t1.ID) nextID
from T t1
[Results]:
| ID | Name | Group | previousID | nextID |
|----|-------|-------|------------|--------|
| 1 | One | A | 1 | 3 |
| 2 | Two | B | 2 | 5 |
| 3 | Three | A | 1 | 6 |
| 4 | Fore | C | 4 | 4 |
| 5 | Five | B | 2 | 7 |
| 6 | Six | A | 3 | 6 |
| 7 | Seven | B | 5 | 7 |
If your mysql support windows function, you can try this.
select *,
LAG(ID)previousID,
LEAD(ID) nextID
from T
sqlfiddle

MySql: update row that requires some logic processing

Given the following table:
id | company | names | group
-------------------------------------
0 | 1 | John | 1
1 | 1 | Doe | null //populate with preceding group number (i.e. 1)
2 | 1 | Yo | null //populate with preceding group number (i.e. 1)
3 | 1 | Zoe | null //populate with preceding group number (i.e. 1)
4 | 1 | Jack | 2
5 | 1 | Doe | null //populate with preceding group number (i.e. 2)
6 | 1 | Yo | null //populate with preceding group number (i.e. 2)
May I know how i can update only the null values to its preceding group number via sql statements? Thanks.
Try this:
UPDATE tablename t1
JOIN (
SELECT ID, #s:=IF(`group` IS NULL, #s, `group`) `group`
FROM (SELECT * FROM tablename ORDER BY ID) r,
(SELECT #s:=NULL) t
) t2
ON t1.ID = t2.ID
SET t1.`group`= t2.`group`
SQLFIDDLE DEMO

I need to get the average for every 3 records in one table and update column in separate table

Table Mytable1
Id | Actual
1 ! 10020
2 | 12203
3 | 12312
4 | 12453
5 | 13211
6 | 12838
7 | 10l29
Using the following syntax:
SELECT AVG(Actual), CEIL((#rank:=#rank+1)/3) AS rank FROM mytable1 Group BY rank;
Produces the following type of result:
| AVG(Actual) | rank |
+-------------+------+
| 12835.5455 | 1 |
| 12523.1818 | 2 |
| 12343.3636 | 3 |
I would like to take AVG(Actual) column and UPDATE a second existing table Mytable2
Id | Predict |
1 | 11133
2 | 12312
3 | 13221
I would like to get the following where the Actual value matches the ID as RANK
Id | Predict | Actual
1 | 11133 | 12835.5455
2 | 12312 | 12523.1818
3 | 13221 | 12343.3636
IMPORTANT REQUIREMENT
I need to set an offset much like the following syntax:
SELECT #rank := #rank + 1 AS Id , Mytable2.Actual FROM Mytable LIMIT 3 OFFSET 4);
PLEASE NOTE THE AVERAGE NUMBER ARE MADE UP IN EXAMPLES
you can join your existing query in the UPDATE statement
UPDATE Table2 T2
JOIN (
SELECT AVG(Actual) as AverageValue,
CEIL((#rank:=#rank+1)/3) AS rank
FROM Table1, (select #rank:=0) t
Group BY rank )T1
on T2.id = T1.rank
SET Actual = T1.AverageValue

MySQL select from specific ID until match condition

Given this table:
+----+-----------+--------+
| id | condition | values |
+----+-----------+--------+
| 1 | a | 1 |
+----+-----------+--------+
| 2 | a | 2 |
+----+-----------+--------+
| 3 | a | 3 |
+----+-----------+--------+
| 4 | a | 4 |
+----+-----------+--------+
| 5 | b | 5 |
+----+-----------+--------+
| 6 | b | 6 |
+----+-----------+--------+
How can I get a new table that begins on id=3 (including) and goes until condition = b (excluding):
+----+-----------+--------+
| id | condition | values |
+----+-----------+--------+
| 3 | a | 3 |
+----+-----------+--------+
| 4 | a | 4 |
+----+-----------+--------+
added fiddle: http://sqlfiddle.com/#!2/9882f7
Basically I want a table between a matching first condition (over a specific column - id) and a second one (over a different column - condition)
You need to stop thinking of SQL data as having any order. Think of SQL data in sets; you have to search by values, not by positions.
SELECT t1.*
FROM t AS t1
JOIN (
SELECT MIN(id) AS id FROM t
WHERE id >= 3 AND `condition` = 'b'
) AS t2
WHERE t1.id >= 3 AND t1.id < t2.id
ORDER BY t1.id
Something like this:
select t.*
from table t
where id >= 3 and id < (select min(t2.id) from table t2 where t2.condition = 'b');
EDIT:
This query works fine on the SQL Fiddle:
select t.*
from t
where id >= 3 and id < (select min(t2.id) from t t2 where t2.condition = 'b');
If I understand what you are asking for, I believe this will work for you:
SELECT id, condition, values
FROM tableName
WHERE id > 2
AND condition != b
ORDER BY id
I hope that works for you.

How can I get last inserted values for a user?

I have this MYSQL table...
+----+---------+-----------+---------------+------------+---------------------+
| id | user | val1 | val2 | val3 | last_modified_date |
+----+---------+-----------+---------------+------------+---------------------+
| 1 | 0001 | 1 | 1 | 1 | 2014-03-31 16:53:29 |
| 2 | 0100 | 1 | 1 | 1 | 2014-04-01 10:32:50 |
| 3 | 0200 | 1 | 0 | 0 | 2014-04-01 10:34:13 |
| 4 | 0200 | 1 | 1 | 1 | 2014-04-01 14:43:47 |
+----+---------+-----------+---------------+------------+---------------------+
What I'm trying to achieve is getting all the last (in order of time) set of values created for the users.
In the example given we insert 0 for user 0200 but after 4 hours we insert 1. I want to be able to get the last value inserted (1).
I have this query:
select *
from table
where val3 = 1
group by user
order by last_modified_date desc;
And a more generic one
select * from table group by user order by last_modified_date desc;
These queries seems to be working fine for the example table. Are they correct for every case? How can I tell "select all the users that have val3 = 1 as their last inserted value"?
You can use one of these two methods:
Option 1 - get the last record for each user and filter for t.val3:
SELECT t.*
FROM table t
JOIN (
SELECT MAX(last_modified_date) as last_modified, user
FROM table
GROUP BY user
) as tt ON t.user = tt.user AND t.last_modified_date = tt.last_modified_date
GROUP BY user
HAVING val3 = 1
Option 2 - get all the records for t.val3 = 1 and remove those which are not last:
SELECT t.*
FROM table t
LEFT JOIN table tt ON tt.user = t.user AND tt.last_modified_date > t.last_modified_date
WHERE t.val3 = 1
AND tt.id IS NULL
Try this
select * from table as t1
where val3 = 1
and last_modified_date =
(select max(last_modified_date) as last_modified_date from table as t2
where t1.user=t2.user and t2.val3 = 1 );