5

我正在尝试清理存储在 MySQL 表中的记录。如果一行包含%X%,无论内容如何,​​我都需要删除该行及其下方的行。例如(对不起,如果这张桌子侮辱了任何人的智力):

| 1 | leave alone 
| 2 | Contains %X% - Delete
| 3 | This row should also be deleted
| 4 | leave alone
| 5 | Contains %X% - Delete
| 6 | This row should also be deleted
| 7 | leave alone

有没有办法只使用几个查询来做到这一点?还是我必须先执行一个 SELECT 查询(使用%x%search 参数),然后循环遍历这些结果并为每个返回的索引执行 DELETE...WHERE + 1

4

4 回答 4

2

这应该可以工作,虽然它有点笨重(可能需要检查 LIKE 参数,因为它使用模式匹配(见评论) DELETE FROM table. db WHERE idcolIN ( SELECT idcolFROM db. tableWHERE colLIKE '%X%') OR idcol IN ( SELECTidcol +1 FROMdb .table WHEREcol` LIKE '%X%')

于 2013-03-17T04:40:14.243 回答
1

假设该表已命名test并包含在名为 和 的列iddata

我们从一个 SELECT 开始,它为我们提供具有前一行的所有行的 id(所有 id 中的最高 id 低于当前行的 id):

SELECT t1.id FROM test t1
  JOIN test t2 ON
    ( t2.id, true )
      =
    ( SELECT t3.id, t3.data LIKE '%X%' FROM test t3
        WHERE t3.id < t1.id ORDER BY id DESC LIMIT 1 )

这给了我们 ID 3 和 6。它们前面的第 2 行和第 5 行包含%X%,所以这很好。

现在让我们通过 UNION 获取包含行的 id%X%并将它们与之前的行组合:

(SELECT t1.id FROM test t1
  JOIN test t2 ON
    ( t2.id, true )
      =
    ( SELECT t3.id, t3.data LIKE '%X%' FROM test t3
        WHERE t3.id < t1.id ORDER BY id DESC LIMIT 1 )
)
UNION
(
  SELECT id FROM test WHERE data LIKE '%X%'
)

这给了我们 3、6、2、5 - 很好!

现在,我们不能从一个表中删除并从 MySQL 中的同一个表中选择 - 所以让我们使用一个临时表,将要删除的 id 存储在其中,然后从该临时表中读取以从我们的原始表中删除:

CREATE TEMPORARY TABLE deleteids (id INT);

INSERT INTO deleteids
  (SELECT t1.id FROM test t1
    JOIN test t2 ON
      ( t2.id, true )
        =
      ( SELECT t3.id, t3.data LIKE '%X%' FROM test t3
          WHERE t3.id < t1.id ORDER BY id DESC LIMIT 1 )
  )
  UNION
  (
    SELECT id FROM test WHERE data LIKE '%X%'
  );

  DELETE FROM test WHERE id in (SELECT * FROM deleteids);

...我们的表中只剩下 ids 1、4 和 7 了test

(并且由于之前的行是使用 <、ORDER BY 和 LIMIT 选择的,因此如果 id 不连续,这也适用。)

于 2013-03-17T05:47:26.563 回答
1

您可以在一个DELETE语句中完成所有操作:

假设“紧随其后的行”是基于您的基于 INT 的 ID 列的顺序,您可以使用 MySQL 变量来分配行号,从而解决您的 ID 中的空白:

DELETE a FROM tbl a
JOIN (
    SELECT a.id, b.id AS nextid
    FROM (
        SELECT     a.id, a.text, @rn:=@rn+1 AS rownum 
        FROM       tbl a
        CROSS JOIN (SELECT @rn:=1) rn_init
        ORDER BY   a.id
    ) a
    LEFT JOIN (
        SELECT     a.id, @rn2:=@rn2+1 AS rownum 
        FROM       tbl a
        CROSS JOIN (SELECT @rn2:=0) rn_init
        ORDER BY   a.id
    ) b ON a.rownum = b.rownum
    WHERE a.text LIKE '%X%'
) b ON a.id IN (b.id, b.nextid)

SQL Fiddle Demo(例如添加了附加数据)


它的作用是首先获取您的数据并根据您的 ID 列对其进行排名,然后我们LEFT JOIN对几乎相同的结果集进行偏移,除了排名列落后于1. 这将并排获取行及其直接“下一个”行,以便我们可以在父DELETE语句中同时提取它们的两个 id:

SELECT a.id, a.text, b.id AS nextid, b.text AS nexttext
FROM (
    SELECT     a.id, a.text, @rn:=@rn+1 AS rownum 
    FROM       tbl a
    CROSS JOIN (SELECT @rn:=1) rn_init
    ORDER BY   a.id
) a
LEFT JOIN (
    SELECT     a.id, a.text, @rn2:=@rn2+1 AS rownum 
    FROM       tbl a
    CROSS JOIN (SELECT @rn2:=0) rn_init
    ORDER BY   a.id
) b ON a.rownum = b.rownum
WHERE a.text LIKE '%X%'

产量:

ID     | TEXT                   | NEXTID   | NEXTTEXT
2      | Contains %X% - Delete  | 3        | This row should also be deleted
5      | Contains %X% - Delete  | 6        | This row should also be deleted
257    | Contains %X% - Delete  | 3434     | This row should also be deleted
4000   | Contains %X% - Delete  | 4005     | Contains %X% - Delete
4005   | Contains %X% - Delete  | 6000     | Contains %X% - Delete
6000   | Contains %X% - Delete  | 6534     | This row should also be deleted

然后,我们JOIN-DELETE整个语句的条件是它删除 ID 为“子选择”IDNEXTID.

于 2013-03-17T05:47:38.583 回答
0

没有合理的方法可以在单个查询中执行此操作。(这可能是可能的,但是您最终不得不使用的查询将非常复杂,并且几乎肯定不能移植到其他 SQL 引擎。)

使用您在问题中描述的 SELECT-then-DELETE 方法。

于 2013-03-17T04:37:15.353 回答