我有几个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 |
+----------+--------+----------+
现在我想得到一份53k + fos项目的所有fos项目和PRef中的记录数量。
我试图将where k.fosID=@fieldID
中的部分修改为where k.fosID in (select fosID from FOS)
,但产生的次数减少了。
有关如何解决此问题的任何建议?
P.S。我现在正在看游标,但表现真的很......很慢
编辑1:预期结果:
+----------+--------+----------+
|fosID |fosName |rowCount |
+----------+--------+----------+
|1234567890|name1 |34 |
|1234567891|name2 |3 |
|1234567892|name3 |23 |
|..... |.... |... |
+----------+--------+----------+
(exact same number of rows as table FOS)
答案 0 :(得分:2)
您可以修改子查询以使用相关子查询
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;
答案 1 :(得分:2)
首先,我怀疑通过检查表中的数据类型可以获得显着的改进。看起来你只使用数字数字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