如何加快按计算字段排序的查询?

Vic*_*ria 4 sql-server

我会试着举一个例子 - 这不是我的表格结构 - 我只是想概述这个问题以找到解决方案......

人员 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,但可能与平台无关...

Qua*_*noi 5

如果您真的不需要零秒现实,您可以不时运行查询并缓存结果。

如果您仍然需要有关此的实时数据(牺牲插入性能),我会这样做:

由于索引视图中不允许自联接,因此您需要为每个表创建两个副本:

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)

如果普通兄弟姐妹的数量是相关的,这个查询会非常快。

该解决方案有两个缺点:

  1. DML性能:如果为百万兄弟共享的名字插入或删除一条记录,索引视图将获得2M插入或删除。这就是您为实时查询付出的代价:您所要求的数据类型无法轻松编入索引。

  2. 有 0 个兄弟或 0 个姐妹的人不会被索引。如果顶对有可能没有兄弟姐妹,你应该稍微修改最后一个查询。