繁体   English   中英

大表上的SQL计数和多个子查询

[英]SQL counting and multiple subqueries on huge tables

我有一些SQL表,分别命名为FOS,关键字和PRef。 它们的结构和关系如下:

+------------------+      +------------------+      +-----------------+
|       FOS        |      |     keywords     |      |       PRef      |
+------------------+      +------------------+      +-----------------+
|fosID (PK)        |--+   |pkID (PK)         |  +---|pID1 (PK)        |
|fosName           |  +---|fosID(FK)         |  +---|pID2 (PK)        |
+------------------+      |paperID (FK)      |--+   +-----------------+
 ( 53k+ rows)             +------------------+       ( 952M+ rows)
                           ( 157M+ rows)

目前,我可以通过向查询提供单个fosID来做到这一点,但是由于fos表包含超过1k条记录,因此我没有足够的人力来手动提供每个fosID并获取其对应的rowCount然后合并所有结果

declare @fosID varchar(10)='1234567890';--my fosID

select fos.fosID,fos.fosName,count(*) as rowCount 
from  PRef pr left join FOS fos on fos.fosID=@fosID 
where 
     pr.pID1 in(SELECT paperID FROM keywords k where k.fosID=@fosID)
  OR pr.pID2 in(SELECT paperID FROM keywords k where k.fosID=@fosID)
group by fos.fosID,fos.fosName

然后给出正确的结果为:

+----------+--------+----------+
|fosID     |fosName |rowCount  |
+----------+--------+----------+
|1234567890|name1   |34        |
+----------+--------+----------+

现在,我想获取所有fos项目的列表以及PRef中53k + fos项目的每个记录的数量。

我试图将where k.fosID=@fieldID的部分修改为where k.fosID=@fieldID的部分where k.fosID in (select fosID from FOS)但产生的计数较少。

关于如何解决这个问题有什么建议吗?

PS我现在正在看游标,但是性能确实...真的很慢

编辑1:预期结果:

+----------+--------+----------+
|fosID     |fosName |rowCount  |
+----------+--------+----------+
|1234567890|name1   |34        |
|1234567891|name2   |3         |
|1234567892|name3   |23        |
|.....     |....    |...       | 
+----------+--------+----------+
 (exact same number of rows as table FOS)

您可以只修改子查询以使用关联的子查询

select fos.fosID, fos.fosName, count(*) as rowCount 
from  PRef pr cross join
      FOS fos
where pr.pID1 in (SELECT paperID FROM keywords k where k.fosID = fos.fosID) OR
      pr.pID2 in (SELECT paperID FROM keywords k where k.fosID = fos.fosID)
group by fos.fosID, fos.fosName;

我的猜测是性能会很差。

这是一种选择:

select fos.*, kp.cnt
from fos outer apply
     (select count(*) as cnt
      from keywords k join
           pref pr
           on k.paperID in (pr.pID1, pf.pID2) and
              k.fosID = fos.fosID
     ) kp;

我认为这也将具有非常差的性能特征。

如果可以分别设置每个ID,则SQL Server应该能够提出更好的执行计划:

select fos.*, (kp1.cnt + kp2.cnt)
from fos outer apply
     (select count(*) as cnt
      from keywords k join
           pref pr
           on k.paperID = pr.pID1 and
              k.fosID = fos.fosID
     ) kp1 outer apply
     (select count(*) as cnt
      from keywords k join
           pref pr
           on k.paperID = pr.pID2 and
              k.fosID = fos.fosID
     ) kp2;

首先,我怀疑您可以通过检查表中的数据类型来获得重大改进。 看来您只使用数字的varchar(10)吗?
在小型表上不会注意到这种荒谬,但是在900M行上可能浪费超过5GB,从而影响存储,内存和性能。

第二个FOS仅真正用于查找fosName并且在53k行中是工作的较小部分。 因此,首先要正确获取每个fosID的计数; 然后加入名字。

;with CountPerFos as (
    SELECT  k.fosID, COUNT(*) AS fosCount
    FROM    PRef r
            INNER JOIN keywords k ON
                r.PID1 = k.paperID
             OR r.PID2 = k.paperID
    GROUP BY k.fosID
)
SELECT  c.fosID, f.fosName,
        --Need to handle fosIDs missing from CTE above
        COALESCE(c.fosCount, 0)
FROM    FOS f
        LEFT OUTER JOIN CountPerFos c
            f.fosID = c.fosID

暂无
暂无

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

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