6

我正在尝试优化需要很长时间的查询。查询的目标是获得最相似的 F2 。(特别是相似性度量)这是我所拥有的一个示例:

 CREATE TABLE Test
(
   F1 varchar(124),
   F2 varchar(124),
   F3 varchar(124)
)
INSERT INTO TEST ( F1, F2, F3 ) VALUES ( 'A', 'B', 'C' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'D', 'B', 'E' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'F', 'I', 'G' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'F', 'I', 'G' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'D', 'B', 'C' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'F', 'B', 'G' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'D', 'I', 'C' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'A', 'B', 'C' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'A', 'B', 'K' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'A', 'K', 'K' )

现在如果我运行这个查询:

SELECT B.f2,COUNT(*) AS CNT  
FROM 
(
select F1,F3 from Test
where F2='B'
 )AS A
    INNER JOIN  Test AS B
   ON A.F1 = B.F1 AND  A.F3 = B.F3
GROUP BY B.F2 
ORDER BY CNT DESC 

该表有 1m+ 行。有什么更好的方法来做到这一点?

4

5 回答 5

3

你也可以用这种形式写你的查询,因为你有一个选择,所以你的检索时间会减少

SELECT  Test_1.F2, COUNT(Test_1.F1) AS Cnt 
FROM    Test 
INNER JOIN Test AS Test_1 ON Test.F1 = Test_1.F1 AND Test.F3 = Test_1.F3 
WHERE   (Test.F2 = 'B') 
GROUP BY Test_1.F2
于 2012-09-16T04:58:40.227 回答
3

这是编写查询的另一种方法。接近 guido 在 MS SQL 中可运行的答案。

WITH Filtered AS (SELECT DISTINCT F1,F3 FROM Test WHERE F2='B')
SELECT B.f2,COUNT(*) AS CNT
  FROM Test B
       INNER JOIN Filtered
           ON B.F1 = Filtered.F1 AND B.F3 = Filtered.F3
 GROUP BY B.F2
 ORDER BY CNT DESC

我认为您的原始查询可能有错误,就像 Fred 提到的那样。在您的示例中,F2="B" 的计数应该是 6,而不是 8,对吗?如果打算使用 8,则取出DISTINCT.

您可能会尝试的另一件事是将 TEST 表的聚集索引设为 (F2, F1, F3),并在 (F1, F3) 上创建另一个非聚集索引。

示例代码也可以在SqlFiddle上找到。

于 2012-09-16T08:25:13.373 回答
2

对所有行的过滤搜索WHERE F2 = 'B'将导致全表扫描,除非您创建的索引将 F2 作为其第一列或唯一列。再往下,连接条件涉及列 F1 和 F3,您提到的列已经是以 F1 开头的索引的一部分。

我还注意到,查询的第一部分并没有消除 T2 = 'B' 的 (T1, T3) 集合的重复项,正如人们在将该集合与同一个表的另一个子集相交时所期望的那样。您可能有这样做的理由,但在您提供有关您尝试实现的相似性测量算法的一些详细信息之前,我们无法确定。

您的ORDER BY子句还通过对最终结果集进行潜在的大型内部排序来影响查询运行时间。

于 2012-09-16T06:34:44.073 回答
1

如果您的 Test 表有 1m+ 行,那么您分组的连接临时表很容易有数亿行。

这将在 mysql 中有效,但在 sql-server afaik 上无效:

SELECT F2,COUNT(*)
FROM Test AS B 
WHERE (B.F1,B.F3) IN (
  SELECT F1,F3 FROM Test
  WHERE F2='B') 
GROUP BY F2
于 2012-09-16T06:01:25.087 回答
1

我意识到这已经得到了回答,但我认为这种方法可能会快得多,特别是如果 F1 和 F3 有很多重复值:

SELECT B.f2, sum(A.cnt) AS CNT  
FROM (select F1, F3, count(*) as cnt
      from Test
      where F2='B'
      group by f1, f3
     ) A INNER JOIN
     Test B
     ON A.F1 = B.F1 AND A.F3 = B.F3
GROUP BY B.F2 
ORDER BY CNT DESC

如果 F1 和 F3 没有很多组合,那么第一个子查询应该减少到几百或几千行。(您的示例数据只有一个大写字母,因此如果使用所有字母,则组合数将为 576。)SQL Server 可能会对结果进行合并或散列连接,这应该会很好地执行。

您也可以使用 windows 函数在没有 join 和 group by 的情况下执行此操作:

select t.f2, sum(nummatches) as cnt
from (select t.*,
             sum(isB) over (partition by f1, f3) as nummatches
      from (select t.*,
                   (case when F2 = 'B' then 1 else 0 end) as IsB
            from test
           ) t
     ) t
group by t.f2
order by 2 desc

窗口函数通常表现更好,因为它们适用于较小的数据块。

于 2012-09-16T17:46:32.757 回答