2010-10-06 46 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