繁体   English   中英

复杂SQL连接与group by

[英]Complex SQL join with group by

我正在尝试优化需要很长时间的查询。 查询的目标是获得最佳类似的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 

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

您也可以在此表单中编写查询,并且因为您有一个选择,所以您的检索时间将减少

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

这是编写查询的另一种方法。 接近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

我认为你的原始查询可能有一个bug,就像Fred提到的那样。 在你的例子中,F2 =“B”的计数应该是6而不是8,是吗? 如果打算8,请取出DISTINCT

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

SqlFiddle也提供示例代码。

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

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

您的ORDER BY子句也会通过在最终结果集上产生可能较大的内部排序来影响查询运行时。

如果您的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

我意识到这已经得到了回答,但我认为这种方法可能要快得多,特别是如果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

窗口函数通常表现更好,因为它们可以处理较小的数据块。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM