2010-10-06 100 views
3

我有以下形式的Access表(我简化了一点)整蛊的MS Access SQL查询来删除多余的重复记录

ID   AutoNumber  Primary Key 
SchemeName Text (50) 
SchemeNumber Text (15) 

这包含了一些数据,例如...

ID   SchemeName   SchemeNumber 
-------------------------------------------------------------------- 
714   Malcolm    ABC123 
80   Malcolm    ABC123 
96   Malcolms Scheme  ABC123 
101   Malcolms Scheme  ABC123 
98   Malcolms Scheme  DEF888 
654   Another Scheme  BAR876 
543   Whatever Scheme  KJL111 
etc... 

现在。我想在相同的SchemeNumber下删除重复的名称。但是我想离开该Scheme编号的SchemeName最长的记录。 如果有长度相同的重复记录,那么我只想留下一个,即最低的ID(但任何一个都会真的)。从上面的例子中,我想删除ID 714,80和101(仅留下96)。

我认为这样会比较容易实现,但它变成了一个噩梦!感谢您的任何建议。我知道我可以循环编程,但我宁愿有一个DELETE查询。

+0

不住那些有回答 - 请看我的更新! – 2010-10-06 17:36:13

+0

您的数据在Jet/ACE或SQL Server中?如果前者,为什么你用SQL Server术语给你的数据类型?例如,Jet/ACE不支持BIGINT。数据在SQL Server中的事实将是一个重要的细节,因为它意味着SQL方言是不同的。或者您正在通过ODBC访问它,这对选择最佳任务方式具有各种含义。 – 2010-10-08 02:18:42

+0

它在Access中,是的。抱歉。我更习惯于SQL Server,因此以这种形式提供了数据。我会改变它来说清楚。 – 2010-10-08 08:04:53

回答

2

看是否有此查询返回你想保留的行:

SELECT r.SchemeNumber, r.SchemeName, Min(r.ID) AS MinOfID 
FROM 
    (SELECT 
     SchemeNumber, 
     SchemeName, 
     Len(SchemeName) AS name_length, 
     ID 
    FROM tblSchemes 
    ) AS r 
    INNER JOIN 
    (SELECT 
     SchemeNumber, 
     Max(Len(SchemeName)) AS name_length 
    FROM tblSchemes 
    GROUP BY SchemeNumber 
    ) AS w 
    ON 
     (r.SchemeNumber = w.SchemeNumber) 
     AND (r.name_length = w.name_length) 
GROUP BY r.SchemeNumber, r.SchemeName 
ORDER BY r.SchemeName; 

如果是这样,将其保存为qrySchemes2Keep。然后创建一个DELETE查询,以放弃在qrySchemes2Keep中找不到其ID值的tblSchemes中的行。

DELETE 
FROM tblSchemes AS s 
WHERE Not Exists (SELECT * FROM qrySchemes2Keep WHERE MinOfID = s.ID); 

只是要小心,如果以后使用Access'查询设计器进行修改,即删除查询,它可能‘有益’的SQL转换为这样的事情:

DELETE s.*, Exists (SELECT * FROM qrySchemes2Keep WHERE MinOfID = s.ID) 
FROM tblSchemes AS s 
WHERE (((Exists (SELECT * FROM qrySchemes2Keep WHERE MinOfID = s.ID))=False)); 
+0

这*几乎*完美地工作!它给我留下了一个重复的地方,在具有相同长度的不同名称的相同schemeNumber下有两个记录 - 它应该已经删除了具有较低ID的那个记录。 – 2010-10-07 08:24:32

+0

在这种情况下,请从qrySchemes2Keep的第一行以及最后一个GROUP BY行中删除r.SchemeName。还要更改或删除ORDER BY。 – HansUp 2010-10-07 11:41:24

+0

糟糕。删除较低的ID?我以为你想保持最低的身份证。 – HansUp 2010-10-07 13:37:42

2
DELETE FROM Table t1 
WHERE EXISTS (SELECT 1 from Table t2 
      WHERE t1.SchemeNumber = t2.SchemeNumber 
      AND Length(t2.SchemeName) > Length(t1.SchemeName) 
) 

取决于你的RDBMS你可以使用功能不同长度(甲骨文 - 长度,MySQL的 - 长度,SQL服务器 - LEN)

+1

我认为这可能会留下重复的记录,如果他们有相同的长度名称?我已经更新了我的问题示例表... – 2010-10-06 17:32:59

+0

我已经将此作为选择查询运行,并且它不会返回足够的行附近以删除任何地方。 – 2010-10-07 08:07:28

+0

@E Ronnoco,下面的查询是否返回任何东西?选择* FROM表t1 WHERE EXISTS(从表t2选择1) WHERE t1.SchemeNumber = t2.SchemeNumber AND Length(t2.SchemeName)> Length(t1.SchemeName) ) – 2010-10-07 08:28:53

0

试试这个:

Select * From Table t 
    Where Len(SchemeName) < 
     (Select Max(Len(Schemename)) 
     From Table 
     Where SchemeNumber = t.SchemeNumber) 
    And Id > 
     (Select Min (Id) 
     From Table 
     Where SchemeNumber = t.SchemeNumber 
      And SchemeName = t.SchemeName) 

或本:, ...

Select * From Table t 
    Where Id > 
     (Select Min(Id) From Table 
     Where SchemeNumber = t.SchemeNumber 
     And Len(SchemeName) < 
      (Select Max(Len(Schemename)) 
      From Table 
      Where SchemeNumber = t.SchemeNumber)) 

如果其中任一选择应删除的记录,只需将其更改为删除

Delete 
    From Table t 
    Where Len(SchemeName) < 
     (Select Max(Len(Schemename)) 
     From Table 
     Where SchemeNumber = t.SchemeNumber) 
    And Id > 
     (Select Min (Id) 
     From Table 
     Where SchemeNumber = t.SchemeNumber 
      And SchemeName = t.SchemeName) 

或使用第二建筑:

Delete From Table t Where Id > 
    (Select Min(Id) From Table 
    Where SchemeNumber = t.SchemeNumber 
    And Len(SchemeName) < 
     (Select Max(Len(Schemename)) 
     From Table 
     Where SchemeNumber = t.SchemeNumber)) 
+0

我认为这将留下重复,如果他们有相同的长度SchemeName – 2010-10-06 17:29:58

+0

是的,这是真的,但如果有多个具有相同的长度,如何决定哪一个不删除?指定一个规则,我可以修改查询以删除除此之外的所有内容。 – 2010-10-06 17:38:12

+0

嗨看到我更新的问题。对于不遵守规则更具体的道歉。但是每个SchemeNumber只应保留一行。此行应该具有该SchemeNumber最长的原始SchemeNames。如果有两个以上的不同名称,其中最长的是同一个号码,那么保留哪一个并不重要。为了参数,我指定了ID最小的一个。 – 2010-10-06 18:19:18

2
delete ShortScheme 
from Scheme ShortScheme 
join Scheme LongScheme 
    on ShortScheme.SchemeNumber = LongScheme.SchemeNumber 
    and (len(ShortScheme.SchemeName) < len(LongScheme.SchemeName) or (len(ShortScheme.SchemeName) = len(LongScheme.SchemeName) and ShortScheme.ID > LongScheme.ID)) 

(SQL服务器有味)

现在更新,包括指定的领带分辨率。虽然,在两个查询中可以获得更好的性能:首先使用较短的名称删除计划(如我的原始查询),然后返回并删除名称长度相同的较高ID。

+0

比较长度并不是一个真正的准确的方法来做到这一点。如果在SchemeName中有两个长度相同的不同字符串会怎样。 – James 2010-10-06 21:01:19

+0

如果条件是相同的长度,则要求不指定保留哪条记录 - 只是*最长的*中的任何一条。我不确定这个查询是否可以在Access中工作。 – 2010-10-06 22:57:26

+0

我在Access中试过这个,DELETE ... FROM语法无效:( – 2010-10-07 08:10:16

0

如果你的平台支持排名函数和公用表表达式:

with cte as (
    select row_number() 
    over (partition by SchemeNumber order by len(SchemeName) desc) as rn 
    from Table) 
delete from cte where rn > 1; 
+0

我的平台是MSAccess恐怕:) – 2010-10-06 17:27:47

+2

Row_number()和OVER不被支持,但很多人都不知道Jet/ACE/Access支持分区。 – 2010-10-06 20:44:06

+0

@ David-W-Fenton:直到你的评论我是其中之一:) – 2010-10-06 20:53:06

2

我会为此在多个步骤。一步完成的大量删除操作让我感到非常紧张 - 如果您犯了一个错误,该怎么办?没有sql'undo'语句。

-- Setup the data 
DROP Table foo; 
DROP Table bar; 
DROP Table bat; 
DROP Table baz; 
CREATE TABLE foo (
    id int(11) NOT NULL, 
    SchemeName varchar(50), 
    SchemeNumber varchar(15), 
    PRIMARY KEY (id) 
); 

insert into foo values (714, 'Malcolm', 'ABC123'); 
insert into foo values (80, 'Malcolm', 'ABC123'); 
insert into foo values (96, 'Malcolms Scheme', 'ABC123'); 
insert into foo values (101, 'Malcolms Scheme', 'ABC123'); 
insert into foo values (98, 'Malcolms Scheme', 'DEF888'); 
insert into foo values (654, 'Another Scheme ', 'BAR876'); 
insert into foo values (543, 'Whatever Scheme ', 'KJL111'); 

-- Find all the records that have dups, find the longest one 
create table bar as 
    select max(length(SchemeName)) as max_length, SchemeNumber 
    from foo 
    group by SchemeNumber 
    having count(*) > 1; 

-- Find the one we want to keep 
create table bat as 
    select min(a.id) as id, a.SchemeNumber 
    from foo a join bar b on a.SchemeNumber = b.SchemeNumber 
     and length(a.SchemeName) = b.max_length 
    group by SchemeNumber; 

-- Select into this table all the rows to delete 
create table baz as 
    select a.id from foo a join bat b where a.SchemeNumber = b.SchemeNumber 
     and a.id != b.id; 

这会为您提供一个只包含要删除的行的记录的新表。

现在检查这些,并确保它们只包含要删除的行。通过这种方式,您可以确保在执行删除操作时,您确切地知道将会发生什么。它也应该很快。

然后,当您准备好时,使用此命令使用此命令删除行。

delete from foo where id in (select id from baz); 

这似乎是更多的工作,因为不同的表,但它更安全,可能与其他方式一样快。另外,您可以在任何步骤中停止并确保在执行任何实际删除之前数据是您想要的。

+0

这只会删除其中一个副本,并且不会考虑我的最大名称长度要求。 – 2010-10-06 17:42:03

+0

你是对的。我已经更新了有效的答案。 – 2010-10-06 20:08:03

+0

+1由子句组合是正确的技巧。 – James 2010-10-06 20:59:37