SQL: Counting and Numbering Duplicates - Optimising Correlated Subquery

旧时模样 提交于 2019-12-24 14:32:04

问题


In an SQLite database I have one table where I need to count the duplicates across certain columns (i.e. rows where 3 particular columns are the same) and then also number each of these cases (i.e. if there are 2 occurrences of a particular duplicate, they need to be numbered as 1 and 2). I'm finding it a bit difficult to explain in words so I'll use a simplified example below.

The data I have is similar to the following (first line is header row, table is referenced in following as "idcountdata"):

id  match1  match2  match3  data
1   AbCde   BC      0       data01
2   AbCde   BC      0       data02
3   AbCde   BC      1       data03
4   AbCde   AB      0       data04
5   FGhiJ   BC      0       data05
6   FGhiJ   AB      0       data06
7   FGhiJ   BC      1       data07
8   FGhiJ   BC      1       data08
9   FGhiJ   BC      2       data09
10  HkLMop  BC      1       data10
11  HkLMop  BC      1       data11
12  HkLMop  BC      1       data12
13  HkLMop  DE      1       data13
14  HkLMop  DE      2       data14
15  HkLMop  DE      2       data15
16  HkLMop  DE      2       data16
17  HkLMop  DE      2       data17

And the output I need to generate for the above would be:

id  match1  match2  match3  data    matchid  matchcount
1   AbCde   BC      0       data01  1        2
2   AbCde   BC      0       data02  2        2
3   AbCde   BC      1       data03  1        1
4   AbCde   AB      0       data04  1        1
5   FGhiJ   BC      0       data05  1        1
6   FGhiJ   AB      0       data06  1        1
7   FGhiJ   BC      1       data07  1        2
8   FGhiJ   BC      1       data08  2        2
9   FGhiJ   BC      2       data09  1        1
10  HkLMop  BC      1       data10  1        3
11  HkLMop  BC      1       data11  2        3
12  HkLMop  BC      1       data12  3        3
13  HkLMop  DE      1       data13  1        1
14  HkLMop  DE      2       data14  1        4
15  HkLMop  DE      2       data15  2        4
16  HkLMop  DE      2       data16  3        4
17  HkLMop  DE      2       data17  4        4

Previously I was using a couple of correlated subqueries to achieve this as follows:

SELECT id, match1, match2, match3, data,
  (SELECT count(*) FROM idcountdata d2 
    WHERE d1.match1=d2.match1 AND d1.match2=d2.match2 AND d1.match3=d2.match3
      AND d2.id<=d1.id)
  AS matchid,
  (SELECT count(*) FROM idcountdata d2 
    WHERE d1.match1=d2.match1 AND d1.match2=d2.match2 AND d1.match3=d2.match3)
  AS matchcount
FROM idcountdata d1;

But the table has over 200,000 rows (and the data can be variable in length/content) and hence this takes hours to run. (Strangely, when I first used the same query on the same data back in mid-to-late 2013 it took minutes rather than hours, but that is beside the point - even back then I thought it was inelegant and inefficient.)

I've already converted the correlated subquery for "matchcount" in the above to an uncorrelated subquery with a JOIN as follows:

SELECT d1.id, d1.match1, d1.match2, d1.match3, d1.data,
  matchcount
FROM idcountdata d1
JOIN
  (SELECT id,match1,match2,match3,count(*) matchcount 
    FROM idcountdata
    GROUP BY match1,match2,match3) d2
  ON (d1.match1=d2.match1 and d1.match2=d2.match2 and d1.match3=d2.match3);

So it's just the subquery for "matchid" that I would like some help to optimise.
In short, the following query runs too slowly for larger datasets:

SELECT id, match1, match2, match3, data,
  (SELECT count(*) FROM idcountdata d2 
    WHERE d1.match1=d2.match1 AND d1.match2=d2.match2 AND d1.match3=d2.match3
      AND d2.id<=d1.id)
  matchid
FROM idcountdata d1;

How can I improve the performance of the above query?
It doesn't have to run in seconds, but it needs to be minutes rather than hours (for around 200,000 rows).


回答1:


A self join may be faster than a correlated subquery

SELECT d1.id, d1.match1, d1.match2, d1.match3, d1.data, count(*) matchid
FROM idcountdata d1
JOIN idcountdata d2 on d1.match1 = d2.match1 
  and d1.match2 = d2.match2 
  and d1.match3 = d2.match3
  and d1.id >= d2.id
GROUP BY d1.id, d1.match1, d1.match2, d1.match3, d1.data

This query can take advantage of a composite index on (match1,match2,match3,id)



来源:https://stackoverflow.com/questions/28096910/sql-counting-and-numbering-duplicates-optimising-correlated-subquery

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!