情况
我的目标是有一个年度 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 写入一个临时表,并通过加入它来为 Registration
和 AssociatedClient
写入删除。
关于mysql - 解决复杂的 SQL 删除查询问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15086335/