mysql - 解决复杂的 SQL 删除查询问题

标签 mysql sql bash

情况

我的目标是有一个年度 cronjob,根据年龄从数据库中删除某些数据。我可以随意使用 Bash 和 MySQL。我从编写一个 bash 脚本开始,但后来我突然意识到,也许我可以只用一个 SQL 查询来完成所有事情。

我天生就是一名程序员,我在数据结构方面没有太多经验,所以我需要一些帮助。

表/数据结构

本次查询的相关表和列如下:

注册:

+-----+-------------------+
| Id  | Registration_date |
+-----+-------------------+
|   2 | 2011-10-03        | 
|   3 | 2011-10-06        | 
|   4 | 2011-10-07        | 
|   5 | 2011-10-07        | 
|   6 | 2011-10-10        | 
|   7 | 2011-10-13        | 
|   8 | 2011-10-14        | 
|   9 | 2011-10-14        | 
|  10 | 2011-10-17        |
+-------------------------+ 

关联客户端:

+-----------+-----------------+
| Client_id | Registration_id |
+-----------+-----------------+
|         2 |               2 | 
|         3 |               2 | 
|         3 |               4 | 
|         4 |               5 | 
|         3 |               6 | 
|         5 |               6 | 
|         3 |               8 | 
|         8 |               9 | 
|         7 |              10 | 
+-----------------------------+

客户:这里只有 Id 是相关的。

如您所见,这是一个简单的多对多关系。一个客户的名字可以有多个注册,一个注册可以有多个客户。

目标

我需要删除 5 年内没有新注册的客户的所有注册和客户数据。听起来很简单,对吧?

棘手的部分

如果来自特定客户的任何其他客户在 5 年内有新的注册,则应保留数据。

假设客户 A 有 4 个注册,其中只有他自己,还有 1 个是他自己和客户 B。所有 5 个注册都超过 5 年。如果客户 B 在 5 年内没有新注册,则应删除所有内容:客户 A 注册和记录。如果 B 确实在 5 年内进行了新注册,则应保留所有客户 A 数据,包括他自己的旧注册。

我尝试过的

构建我的查询,我得到了这么远:

DELETE * FROM `Registration` AS Reg
WHERE TIMESTAMPDIFF(YEAR, Reg.`Registration_date`, NOW()) >= 5
AND 
    (COUNT(`Id`) FROM `Registration` AS Reg2
     WHERE Reg2.`Id` IN (SELECT `Registration_id` FROM `AssociatedClient` AS Clients
                         WHERE Clients.`Client_id` IN (SELECT `Client_id` FROM `AssociatedClient` AS Clients2
                                                       WHERE Clients2.`Registration_id` IN -- stuck
               #I need all the registrations from the clients associated with the first
               # (outer) registration here, that are newer than 5 years.

    ) = 0 -- No newer registrations from any associated clients

请理解我对 SQL 的经验非常有限。我意识到,即使我到目前为止得到的东西也可以进行大量优化(使用连接等),甚至可能不正确。

我被卡住的原因是,如果我可以使用某种循环,我想到的解决方案就会奏效,而且我只是意识到,在这种 SQL 查询中,这不是一件容易的事。

任何帮助

非常感谢。

最佳答案

首先确定注册的其他客户的注册。这是一个 View :

create view groups as 
select   a.Client_id
       , c.Registration_id
from AssociatedClient as a 
join AssociatedClient as b on a.Registration_id = b.Registration_id 
join AssociatedClient as c on b.Client_id = c.Client_id;

这给了我们:

select Client_id
    , min(Registration_id) as first
    , max(Registration_id) as last
    , count(distinct Registration_id) as regs
    , count(*) as pals
from  groups 
group by Client_id;
Client_id   first       last        regs        pals      
----------  ----------  ----------  ----------  ----------
2           2           8           4           5         
3           2           8           4           18        
4           5           5           1           1         
5           2           8           4           5         
7           10          10          1           1         
8           9           9           1           1         

当然,您不需要 View ;这只是为了方便。你可以只使用一个虚拟表。但是请仔细检查以说服自己它会为每个客户生成正确范围的“好友注册”。请注意,该 View 引用Registration。这很重要,因为即使我们使用它从 Registration 中删除,它也会产生相同的结果,因此我们可以将它用于第二个 delete 语句。

现在我们有一个客户列表和他们的“好友注册”。每个 friend 最后一次注册的日期是什么时候?

select g.Client_id, max(Registration_date) as last_reg
from groups as g join Registration as r
on g.Registration_id = r.Id
group by g.Client_id;
g.Client_id  last_reg  
-----------  ----------
2            2011-10-14
3            2011-10-14
4            2011-10-07
5            2011-10-14
7            2011-10-17
8            2011-10-14

哪些有一个特定时间之前的最新日期?

select g.Client_id, max(Registration_date) as last_reg
from groups as g join Registration as r
on g.Registration_id = r.Id
group by g.Client_id
having max(Registration_date) < '2011-10-08';
g.Client_id  last_reg  
-----------  ----------
4            2011-10-07

IIUC 这意味着应该删除客户端 #4,并且应该删除他注册的任何内容。注册将是

select * from Registration
where Id in (
      select Registration_id from groups as g
      where Client_id in ( 
            select g.Client_id
            from groups as g join Registration as r
            on g.Registration_id = r.Id
            group by g.Client_id
            having max(Registration_date) < '2011-10-08'
      )
);
Id          Registration_date
----------  -----------------
5           2011-10-07       

果然,客户端#4 在注册#5 中,并且是本次测试中唯一要删除的客户端。

从那里您可以计算出delete 语句。我认为规则是“删除客户和他注册的任何东西”。如果是这样,我可能会将注册 ID 写入一个临时表,并通过加入它来为 RegistrationAssociatedClient 写入删除。

关于mysql - 解决复杂的 SQL 删除查询问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15086335/

相关文章:

php - 计算不同表的平均值

linux - 我的 $PATH 和 bash 命令遇到问题

linux - 如何对 find 的输出执行 "for each"?

linux - 如何创建一个正常运行的程序?因为我的程序永远运行,不会停止

php - iOS 开发 - 如何将嵌套值发布到 php 服务器端

mysql - UNION 查询不起作用

mysql - 我的 mysql 查询中存在 DISTINCT 问题

sql - 重新格式化列中的数据

php - Mysql数据转PHP数组,然后显示?

mysql NOT IN 不显示结果