我会试着举一个例子 - 这不是我的表格结构 - 我只是想概述这个问题以找到解决方案......
人员 ID、姓名
BrothersNames Id, 名称
SistersNames Id, 名字
PersonBrothers(连接表)PersonId、BrotherNameId
PersonSisters(连接表)PersonId、SisterNameId
好的 - 想象一下这个数据库包含来自一个小国家的每个人。数据库记录了每个人的兄弟姐妹的名字(它不会将一个人映射到他们的兄弟姐妹——只是他们的名字),以便我们可以找到关于名字的统计数据。
显然很多名字是共享的,所以连接表为我们规范了这一点。
我想要做的是取一个用户,找出系统中每个其他用户的兄弟姓名匹配数和姐妹姓名匹配数,然后将这两个匹配项加在一起并按降序排列。所以这会给我们一个拥有最多兄弟姐妹名字的用户列表。
我真的只对前十场比赛感兴趣,但我认为我必须得到整个结果集才能计算出前十场比赛。
请注意,在我的实际数据中,一个人可以有一百万个兄弟或一百万个姐妹。这是我遇到性能问题的地方。
这就是我计算兄弟匹配的方式,我为姐妹计算匹配
select p.id, matches
FROM Person p
LEFT JOIN
(
SELECT
COUNT(*) AS Matches,
pbn.PersonId
FROM PersonBrothersNames pbn
INNER JOIN Brothersnames bn on pbn.BrothernameId =bn.Id
inner join PersonBrothersName otherpbn on otherpbn.BrothernameId = bn.Id
WHERE pbn.PersonId= @PersonId and pbn.PersonId <> otherpbn.personid
GROUP BY pbn.PersonId
) As BrothersNamesJoin ON BrothersNamesJoin.Person = p.Id
Run Code Online (Sandbox Code Playgroud)
请让我知道我是否应该指定更多信息...我使用的是 SQL Server 2008,但可能与平台无关...
如果您真的不需要零秒现实,您可以不时运行查询并缓存结果。
如果您仍然需要有关此的实时数据(牺牲插入性能),我会这样做:
由于索引视图中不允许自联接,因此您需要为每个表创建两个副本:
CREATE TABLE personBrother
(
personId INT NOT NULL,
brotherName INT NOT NULL
)
CREATE TABLE personBrother2
(
personId INT NOT NULL,
brotherName INT NOT NULL
)
Run Code Online (Sandbox Code Playgroud)
在他们的连接上创建一个索引视图:
CREATE VIEW
commonBrothers
WITH SCHEMABINDING
AS
SELECT p1.personId AS p1,
p2.personId AS p2,
COUNT_BIG(*) AS cnt
FROM dbo.personBrother p1
JOIN dbo.personBrother2 p2
ON p1.brotherName = p2.brotherName
WHERE p1.personId < p2.personId
GROUP BY
p1.personId, p2.personId
CREATE UNIQUE CLUSTERED INDEX
ux_commonBrothers_p1_p2
ON commonBrothers (p1, p2)
CREATE INDEX
ix_commonBrothers_cnt
ON commonBrothers (cnt)
Run Code Online (Sandbox Code Playgroud)
对姐妹也一样。
您应该手动维护这些表以具有相同的数据(编写触发器、插入/更新/删除两者等)。
现在我们可以轻松获得最多兄弟最多姐妹的配对:
SELECT TOP 1 WITH TIES
*
FROM commonBrothers
ORDER BY
cnt DESC
Run Code Online (Sandbox Code Playgroud)
我们现在需要的只是获取最大的总和。不幸的是,我们无法索引这些视图的连接(这是一个纯粹的实现缺陷,对此没有理论上的限制)。
所以我们需要做到以下几点:顶级对的兄弟不能少于顶级sis对。对姐妹来说也是一样。所以我们有这个查询:
SELECT TOP 1 WITH TIES
cb.p1, cb.p2, cb.cnt + cs.cnt AS totalCnt
FROM commonBrothers cb
JOIN commonSisters cs
ON cs.p1 = cb.p1
AND cs.p2 = cb.p2
WHERE cs.cnt >=
(
SELECT MAX(cst.cnt)
FROM (
SELECT TOP 1 WITH TIES
p1, p2
FROM commonBrothers
ORDER BY
cnt DESC
) cbt
JOIN commonSisters cst
ON cst.p1 = cbt.p1
AND cst.p2 = cbt.p2
)
AND cb.cnt >=
(
SELECT MAX(cbt.cnt)
FROM (
SELECT TOP 1 WITH TIES
p1, p2
FROM commonSisters
ORDER BY
cnt DESC
) cst
JOIN commonBrothers cbt
ON cbt.p1 = cst.p1
AND cbt.p2 = cst.p2
)
ORDER BY
totalCnt DESC
Run Code Online (Sandbox Code Playgroud)
如果普通兄弟姐妹的数量是相关的,这个查询会非常快。
该解决方案有两个缺点:
DML性能:如果为百万兄弟共享的名字插入或删除一条记录,索引视图将获得2M插入或删除。这就是您为实时查询付出的代价:您所要求的数据类型无法轻松编入索引。
有 0 个兄弟或 0 个姐妹的人不会被索引。如果顶对有可能没有兄弟姐妹,你应该稍微修改最后一个查询。
| 归档时间: |
|
| 查看次数: |
3488 次 |
| 最近记录: |