23

情况

我的目标是每年进行一次 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 查询中容易做的事情。

任何帮助

非常感谢。

4

6 回答 6

19

首先识别注册的其他客户的注册。这是一个视图:

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         

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

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

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加入它。

于 2013-03-26T05:43:01.787 回答
1

您想知道所有需要保留的注册。因此,您的第一个查询返回前 5 年内的注册:

SELECT
  Id
FROM
  Registration
WHERE
  Registration_date >= '2011-10-08'

然后所有与上一个查询相关的客户注册:

SELECT
  a2.Registration_id as Id
FROM
  AssociatedClient AS a1
  INNER JOIN AssociatedClient AS a2
    ON a1.Client_id = a2.Client_id 
WHERE
  a1.Registration_id IN
  (  
    SELECT
      Id
    FROM
      Registration
    WHERE
      Registration_date >= '2011-10-08'
 )

然后,通过将先前的查询组合在一个中,您将拥有所有不能删除的注册UNION,并且您希望所有不属于此查询的客户端:

SELECT
  Client_id
FROM
  AssociatedClient
WHERE
  Registration_id NOT IN
  (
    SELECT
      Id
    FROM
      Registration
    WHERE
      Registration_date >= '2011-10-08'
    UNION
    SELECT
      a2.Registration_id as Id
    FROM
      AssociatedClient AS a1
      INNER JOIN AssociatedClient AS a2
        ON a1.Client_id = a2.Client_id 
    WHERE
      a1.Registration_id IN
      (  
        SELECT
          Id
        FROM
          Registration
        WHERE
          Registration_date >= '2011-10-08'
      )
  )

你可以在这个SQL fiddle中看到结果

然后,您可以使用以下查询删除未注册的客户行与条件相对应:

DELETE FROM
  AssociatedClient
WHERE
  Client_id IN (<previous query>);

以及 AssociatedClient 中不存在的所有注册:

DELETE FROM
  Registration
WHERE
  Id NOT IN (SELECT Registration_id FROM AssociatedClient)
于 2014-09-25T17:51:46.553 回答
0

使用临时表。

INSERT INTO LockedClient(client_id) --select clients that should not be deleted
SELECT DISTINCT ac.client_id 
FROM AssociatedClient ac
JOIN Registration r ON r.Id = ac.ID
WHERE TIMESTAMPDIFF(YEAR, Reg.`Registration_date`, NOW()) >= 5;

DELETE  * FROM Registration r -- now delete all except locked clients
JOIN AssociatedClient ac ON ac.registration_id = r.id
LEFT JOIN LockedClient lc ON lc.client_id = ac.client_id
WHERE TIMESTAMPDIFF(YEAR, Reg.`Registration_date`, NOW()) >= 5 AND lc.client_id IS NULL
于 2013-02-26T11:09:12.493 回答
0

这应该为您提供正确的客户信息 1 级到链接的客户。我知道这可能无法为您提供所有需要的信息。但是,正如评论中所述,目前 1 级实现应该就足够了。这可能不是最佳的。

SELECT
AC1.Client_id,
MAX(R.Registration_date) AS [LatestRegistration]
FROM
#AssociatedClient AC1
JOIN #AssociatedClient AC2
    ON  AC1.Registration_id = AC2.Registration_id
JOIN #AssociatedClient AC3
    ON  AC2.Client_id = AC3.Client_id
JOIN #Registration R
    ON  AC3.Registration_id = R.Id
GROUP BY
AC1.Client_id

您应该使用循环查看函数。这是我现在唯一能想到的。

于 2013-03-15T13:20:17.870 回答
0

我是一个 SQL Server 人,但我认为这种语法适用于 MySQL。此查询将拉取不应删除的客户端。

SELECT A3.Client_id
FROM AssociatedClient A1
#Get clients with registrations in the last 5 years
JOIN Registration R1 ON A1.Registration_id = R1.Id 
    AND TIMESTAMPDIFFERENCE(YEAR, R1.Registration_Date, Now()) <= 5
#get the rest of the registrations for those clients
JOIN AssociatedClient A2 ON A1.Client_id = A2.Client_id
#get other clients tied to the rest of the registrations
JOIN AssociatedClient A3 ON A2.Registration_id = A3.Registration_id
于 2013-03-20T17:40:36.973 回答
0

您需要两个 sql delete 语句,因为您要从两个表中删除。

两个删除语句都需要区分正在保留的注册和正在删除的注册,因此需要先从注册表中删除。

控制问题是与 id 相关联的最新注册(注册 id 或客户端 id)。因此,您将根据 id 进行聚合并找到最大注册日期。

删除客户端 ID 时,您会删除聚合注册 ID 超过五年的那些。此删除操作将取消之前链接的注册 ID 的关联,但这没关系,因为此操作不会为他们提供更新的关联注册日期。

也就是说,一旦您拥有客户端 ID,您将需要加入注册 ID,以查找相关的注册 ID。您需要加入客户端 ID,然后自行加入回注册 ID,以使该部分正常工作。如果您已删除与注册相关联的所有客户端 ID,您还需要删除这些注册。

我的 sql 有点生锈,我的 mysql 也生锈了,这是未经测试的代码,但这应该与我认为您需要做的相当接近:

delete from associatedclient where client_id in (
  select client_id from (
    select ac.client_id, max(r.registration_date) as dt
      from associatedclient ac
        inner join registration r
          on ac.registration_id = r.id
      group by ac.client_id
  ) d where d.dt < cutoff
)

下一步看起来像这样:

delete from registration where id in (
  select id from (
    select r1.id, max(r2.date) dt
      from registration r1
        inner join associated_client ac1
          on r1.id = ac1.registration_id
        inner join associated_client ac2
          on ac1.client_id = ac2.client_id
        inner join registration r2
          on ac2.registration_id = r2.id
) d
  where d.dt < cutoff
  or d.dt is null

我希望你不介意我提醒你,但你应该先运行不删除的 select 语句,然后检查结果的合理性,然后再继续删除内容。

(如果你有任何限制或索引阻止它工作,你也必须处理这些。)

于 2014-09-19T17:36:07.793 回答