Conditionally delete last row in mysql - mysql

How do I conditionally delete the last row in a mysql table?
me/my_machine#17:26:57>cat create_tables.sql
CREATE DATABASE IF NOT EXISTS test_db;
USE test_db;
CREATE TABLE IF NOT EXISTS err_hist_table (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
cl INT NOT NULL,
usr VARCHAR(16),
fault_code VARCHAR(10));
INSERT INTO err_hist_table (cl,usr,fault_code) VALUES (1,'pA','A'), (2,'pA','NULL'),(3,'pC','B'),(4,'pB','NULL');
The above SQL commands create a table that is like this:
MySQL [test_db]> SELECT * FROM err_hist_table;
+----+----+------+------------+
| id | cl | usr | fault_code |
+----+----+------+------------+
| 1 | 1 | pA | A |
| 2 | 2 | pA | NULL |
| 3 | 3 | pC | B |
| 4 | 4 | pB | NULL |
+----+----+------+------------+
4 rows in set (0.00 sec)
Now I want to DELETE the last row (biggest value of id) only when the value of fault_code is NULL. If it is not NULL, I want to skip the delete.
To my sql-newbie eyes, it seems like there should be a simple something like:
SELECT * IF err_history_table.id=max(err_history_table) AND fault_code = 'NULL';
I could not find my answer on the mysql docs page. Is there a simple solution for something like this?

You can do it with a subquery that returns the row of the maximum id where you check the value of fault_code:
DELETE FROM err_hist_table
WHERE id = (
SELECT id FROM (
SELECT * FROM err_hist_table
ORDER BY id DESC LIMIT 1
) t
WHERE fault_code IS NULL
);
See the demo.
Results:
| id | cl | usr | fault_code |
| --- | --- | --- | ---------- |
| 1 | 1 | pA | A |
| 2 | 2 | pA | |
| 3 | 3 | pC | B |

I'm not totally sure if this is what you want, but it should work. If you have a recent version of MySQL you should be able to use sub-queries, like this:
DELETE FROM err_hist_table WHERE id = (SELECT MAX(id) FROM err_hist_table) AND fault_code IS NULL
Untested on your specific table of course. I would even suggest performing a SELECT first to verify it does what you expect.

It's worth mentioning that you can acheive the desired result using a single JOIN, without needing a sub-sub-query and in a way which seems to me both concise and readable:
DELETE e1 FROM err_hist_table e1
JOIN (
SELECT MAX(id) AS id
FROM err_hist_table
) e2 USING (id)
WHERE e1.fault_code = 'NULL'
More on the topic of using joins in a DELETE statement here: https://dev.mysql.com/doc/refman/8.0/en/delete.html#idm45306662494864

Related

How can I update a column between two unique values in another column in MySQL?

How can I update a column between two unique values in another column in MySQL or MariaDB?
Consider a table called Example that has three columns:
Id: An auto-increment integer ID
RandomId: A series of random and unique GUIDs
IsUpdated: A column currently only containing NULL values, that needs to be updated
* ----------------------------------------------------- *
| Id | RandomId | IsUpdated |
| ----------------------------------------------------- |
| 1 | c446980b-cf2f-4f2d-a27b-28d6bde6415d | NULL |
| 2 | d6a1a52c-d073-4019-836a-67cf6551d958 | NULL |
| 3 | 7a339a6a-8e57-4373-84fd-1b40ee51c884 | NULL |
| 4 | 56b908a7-fb07-4f4c-a25d-699cf40cf690 | NULL |
| 5 | fac75ce6-a605-453a-958c-74f197e20a11 | NULL |
* ----------------------------------------------------- *
I would like to update IsUpdated between two specific GUIDs, like so:
UPDATE Example
SET IsUpdated = 1
WHERE RandomId >= 'd6a1a52c-d073-4019-836a-67cf6551d958' -- Starting Here
AND RandomId <= '56b908a7-fb07-4f4c-a25d-699cf40cf690' -- Ending Here
The resulting table should look like the following:
* ----------------------------------------------------- *
| Id | RandomId | IsUpdated |
| ----------------------------------------------------- |
| 1 | c446980b-cf2f-4f2d-a27b-28d6bde6415d | NULL |
| 2 | d6a1a52c-d073-4019-836a-67cf6551d958 | 1 |
| 3 | 7a339a6a-8e57-4373-84fd-1b40ee51c884 | 1 |
| 4 | 56b908a7-fb07-4f4c-a25d-699cf40cf690 | 1 |
| 5 | fac75ce6-a605-453a-958c-74f197e20a11 | NULL |
* ----------------------------------------------------- *
But since the Ids are not sequential, this method does not appear to work.
What would be the most efficient way to update a column (IsUpdated) between two unique values in another column (RandomId)?
You need to filter by id instead. I would recommend the update ... join syntax:
update example e
inner join (
select min(Id) minId, max(Id) maxId
from example
where RandomId in (
'd6a1a52c-d073-4019-836a-67cf6551d958',
'56b908a7-fb07-4f4c-a25d-699cf40cf690'
)
) i on e.id between i.minId and i.maxId
set e.IsUpdated = 1
Note that this does not stricly guarantee that guid are matched on the first and last rows (it would also work the other way around). You can be more specific with two joins:
update example e
inner join (
select Id
from example
where RandomId = 'd6a1a52c-d073-4019-836a-67cf6551d958'
) eMin on e.id >= eMin.id
inner join (
select Id
from example
where RandomId = '56b908a7-fb07-4f4c-a25d-699cf40cf690'
) eMax on e.id <= eMax.id
set e.IsUpdated = 1

Can't get expected result using left join and on condition

Running the following scripts, you will get 2 tables with records.
-- ----------------------------
-- Table structure for data
-- ----------------------------
DROP TABLE IF EXISTS `data`;
CREATE TABLE `data` (
`id` int(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of data
-- ----------------------------
INSERT INTO `data` VALUES ('1');
INSERT INTO `data` VALUES ('2');
-- ----------------------------
-- Table structure for status
-- ----------------------------
DROP TABLE IF EXISTS `status`;
CREATE TABLE `status` (
`id` int(255) DEFAULT NULL,
`status` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of status
-- ----------------------------
INSERT INTO `status` VALUES ('1', '0');
INSERT INTO `status` VALUES ('1', '1');
Table data
+--------+--------+
| id | more...|
|--------|--------+
| 1 | |
| 2 | |
+--------+--------+
Table status
+--------+--------+
| id | status |
|--------|--------+
| 1 | 0 |
| 1 | 1 |
+--------+--------+
I need select data of table data excluded items which status is 0 after refer to status.
Wrong try 1
I write the following SQL, and get the following result. It's not the expected result because the item id=2 is also needed.
select * from data d LEFT JOIN status s on d.id=s.id where s.status=1
+--------+--------+--------+
| id | id1 | status |
|--------|--------|--------+
| 1 | 1 | 1 |
+--------+--------+--------+
Wrong try 2
Then I change where to on, and get the following result which looks good.
select * from data d LEFT JOIN status s on d.id=s.id and s.status=1
+--------+--------+--------+
| id | id1 | status |
|--------|--------|--------+
| 1 | 1 | 1 |
+--------+--------+--------+
| 2 | (Null) | (Null) |
+--------+--------+--------+
Try 3
Table status has two items now. If I delete the item (id=1,status=1) of table status , what will be the result ?
select * from data d LEFT JOIN status s on d.id=s.id and s.status=1
+--------+--------+--------+
| id | id1 | status |
|--------|--------|--------+
| 1 | (Null) | (Null) |
+--------+--------+--------+
| 2 | (Null) | (Null) |
+--------+--------+--------+
In this case, I expect the item id=1 should not be here . The expected result is
+--------+--------+--------+
| id | id1 | status |
+--------+--------+--------+
| 2 | (Null) | (Null) |
+--------+--------+--------+
Why does the item id=1 appear in the result ? Can't it resolve my issue using on status=1 ?
A LEFT JOIN returns all the rows of the left table.
Use NOT EXISTS:
select d.* from data d
where not exists (
select 1 from status s
where s.id = d.id and s.status = 0
)
Or with NOT IN:
select * from data
where id not in (select id from status where status = 0)
See the demo.
I've resolved my issue with the following SQL. I have learned my case is not a typical LEFT JOIN scenario, so just putting a condition status=1 to where or on can't resolve it directly.
SELECT
*
FROM
data d
LEFT JOIN status s ON d.id = s.id
WHERE
s.status = 1
OR ISNULL(s.status)
Perhaps post your expected answer, but to return values in data where status is not zero:
Select id
from data d
Left join status s on d.id = s.id
Where ((s.status <> 0) or (s.status isnull))

MySQL: Get minimal set of entries where sum(value) is bigger then given number

I've got a new/special problem where I wasnt able to get a good solution, maybe someone has an idea?
Table as follows:
| id | value |
---------------
| … | … |
| 11 | 500 |
| 12 | 300 |
| 13 | 200 |
| 14 | 400 |
| 15 | 300 |
---------------
Now I need to select (and then delete/update) these entries until my given value (1250) is reached, so after "the magic" I need the table as follows:
| id | value |
---------------
| … | … |
| 11 | 450 |
---------------
Is there any good possibility to do it (performance matters)? (that select query, the other two should be much easier then)
I've found some solutions with subselect (w/o limit…) and "where sum() <= 1250" but they dont work, because they only select the ids 15,14,13&12, not 11. i need something like "give minimal set of entries where sum(value) is bigger then 1250 order by id desc"
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,value INT NOT NULL
);
INSERT INTO my_table VALUES
(10,700),
(11,500),
(12,300),
(13,200),
(14,400),
(15,300);
For simplicity, the following query assumes that ids are contiguous, but it can be easily extended to situations where they are not.
SELECT a.id
, LEAST(a.value,a.value-i) value
FROM my_table a
JOIN
( SELECT *
, #i:=#i-value i
FROM my_table
, ( SELECT #i:=1250) vars
ORDER
BY id DESC
) b
ON b.id = a.id + 1
AND a.value - i >= 0;
+----+-------+
| id | value |
+----+-------+
| 10 | 700 |
| 11 | 450 |
+----+-------+

SQL exclusive or on insert

if've got three tables in my MYSQL Database and want to connect two of this table with one table. The tables look like this
CONNECTTABLE
+----+-----------+---------+
| ID | search_id | room_id |
+----+-----------+---------+
SEARCHTABLE
+----+-----------+-----+
| ID | search_id | ... |
+----+-----------+-----+
SEARCHTABLE
+----+---------+-----+
| ID | room_id | ... |
+----+---------+-----+
Is it possible to ensure via MYSQL that in the CONNECTTABLE only search_id OR room_id is not null per datarow? If I can do so, how can I do so?
Valid rows:
+----+-----------+---------+
| ID | search_id | room_id |
+----+-----------+---------+
| 1 | 42 | NULL |
+----+-----------+---------+
| 2 | NULL | 1337 |
+----+-----------+---------+
Invalid row:
+----+-----------+---------+
| ID | search_id | room_id |
+----+-----------+---------+
| 3 | 42 | 17 |
+----+-----------+---------+
Best regards,
Gerrit
What you want is called a "CHECK constraint". Unfortunately, MySQL does not support them. Well, it will let you define them, but it won't actually check them. This article discusses using triggers to implement the same functionality.
hope this helps
select * from tableName where search_id is null && room_id is not null
union
select * from tableName where search_id is not null && room_id is null
First of all, it is not possible to add two tables with the same name in the database. But i presume that this are only dummy tablenames right? try this one:
SELECT a.ID, b.Search_ID, c.Room_ID
FROM ConnectTable a LEFT JOIN SearchTableA b
ON a.ID = b.ID
LEFT JOIN SearchTableB c
ON a.ID = c.ID
WHERE b.ID IS NULL OR
c.ID IS NULL

How to speed up this slow query?

I'm having some issue with this query that seems to be too slow...
SELECT SUM(c) FROM (
(
SELECT COUNT( id ) AS c
FROM QueueOne
WHERE id = my_id
)
UNION ALL (
SELECT COUNT( id ) AS c
FROM QueueTwo
WHERE id = my_id
)
UNION ALL (
SELECT COUNT( id ) AS c
FROM QueueThree
WHERE id = my_id
)
UNION ALL (
SELECT COUNT( id ) AS c
FROM QueueFour
WHERE id = my_id
)
) AS d
It is actually quite simple :
QueueOne, QueueTwo, QueueThree, QueueFour
Are four queue with different type of column and unfortunately can not be squizzed to One column.
This query give us the number of all waiting queue from every Queue table.
It seems that is too slow for mysql since it log it on the slow-query.log file
Any help would be appreciated.
EDIT
Here's the explain :
+----+--------------+-------------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+-------------------+------+---------------+------+---------+------+------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 4 | |
| 2 | DERIVED | QueueOne | ref | ID | ID | 4 | | 1 | Using index |
| 3 | UNION | QueueTwo | ref | ID | ID | 4 | | 1 | Using index |
| 4 | UNION | QueueThree | ref | ID | ID | 4 | | 1 | Using index |
| 5 | UNION | QueueFour | ref | ID | ID | 4 | | 1 | Using index |
| NULL | UNION RESULT | <union2,3,4,5> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+-------------------+------+---------------+------+---------+------+------+-------------+
6 rows in set (0.82 sec)
EDIT 2:
A little more information, some tables have almost 15 000 000 records
Add an index on id, and rewrite into count(*)
SELECT SUM(c)
FROM ((SELECT COUNT(*) AS c
FROM queueone
WHERE id = my_id)
UNION ALL
(SELECT COUNT(*) AS c
FROM queuetwo
WHERE id = my_id)
UNION ALL
(SELECT COUNT(*) AS c
FROM queuethree
WHERE id = my_id)
UNION ALL
(SELECT COUNT(*) AS c
FROM queuefour
WHERE id = my_id)) AS d
UPDATE
You should also look into parallelization and partitioning
Other benefits usually associated with partitioning include those in the following list. These features are not currently implemented in MySQL Partitioning, but are high on our list of priorities.
Queries involving aggregate functions such as SUM() and COUNT() can easily be parallelized. A simple example of such a query might be SELECT salesperson_id, COUNT(orders) as order_total FROM sales GROUP BY salesperson_id;. By “parallelized,” we mean that the query can be run simultaneously on each partition, and the final result obtained merely by summing the results obtained for all partitions.
Make sure that there is an index for id in each table.
Use count(*) instead of count(id). The result is the same as there are no null-values in the id in the result, but it doesn't have to do the check for null values.