代码之家  ›  专栏  ›  技术社区  ›  Alex Humphrey

这个公共表表达式的结果要计算多少次?

  •  2
  • Alex Humphrey  · 技术社区  · 14 年前

    我试图找出我们在上一次测试迭代中发现的一个bug。它涉及一个使用公共表表达式的查询。查询的主要主题是模拟“first”聚合操作(获取此分组的第一行)。

    问题是,在某些情况下,查询似乎完全随意地选择行—返回同一组中的多个行,一些组则完全被消除。然而,它 总是 选择正确的行数。

    我已经创建了一个最小的例子张贴在这里。有客户机和地址,还有一个定义它们之间关系的表。这是一个 许多的 我正在查看的实际查询的简化版本,但我相信它应该具有相同的特性,这是一个很好的示例,可以用来解释我认为的问题所在。

    CREATE TABLE [Client] (ClientID int, Name varchar(20))
    CREATE TABLE [Address] (AddressID int, Street varchar(20))
    CREATE TABLE [ClientAddress] (ClientID int, AddressID int)
    
    INSERT [Client] VALUES (1, 'Adam')
    INSERT [Client] VALUES (2, 'Brian')
    INSERT [Client] VALUES (3, 'Charles')
    INSERT [Client] VALUES (4, 'Dean')
    INSERT [Client] VALUES (5, 'Edward')
    INSERT [Client] VALUES (6, 'Frank')
    INSERT [Client] VALUES (7, 'Gene')
    INSERT [Client] VALUES (8, 'Harry')
    
    INSERT [Address] VALUES (1, 'Acorn Street')
    INSERT [Address] VALUES (2, 'Birch Road')
    INSERT [Address] VALUES (3, 'Cork Avenue')
    INSERT [Address] VALUES (4, 'Derby Grove')
    INSERT [Address] VALUES (5, 'Evergreen Drive')
    INSERT [Address] VALUES (6, 'Fern Close')
    
    INSERT [ClientAddress] VALUES (1, 1)
    INSERT [ClientAddress] VALUES (1, 3)
    INSERT [ClientAddress] VALUES (2, 2)
    INSERT [ClientAddress] VALUES (2, 4)
    INSERT [ClientAddress] VALUES (2, 6)
    INSERT [ClientAddress] VALUES (3, 3)
    INSERT [ClientAddress] VALUES (3, 5)
    INSERT [ClientAddress] VALUES (3, 1)
    INSERT [ClientAddress] VALUES (4, 4)
    INSERT [ClientAddress] VALUES (4, 6)
    INSERT [ClientAddress] VALUES (5, 1)
    INSERT [ClientAddress] VALUES (6, 3)
    INSERT [ClientAddress] VALUES (7, 2)
    INSERT [ClientAddress] VALUES (8, 4)
    INSERT [ClientAddress] VALUES (5, 6)
    INSERT [ClientAddress] VALUES (6, 3)
    INSERT [ClientAddress] VALUES (7, 5)
    INSERT [ClientAddress] VALUES (8, 1)
    INSERT [ClientAddress] VALUES (5, 4)
    INSERT [ClientAddress] VALUES (6, 6)
    
    ;WITH [Stuff] ([ClientID], [Name], [Street], [RowNo]) AS
    (
        SELECT
            [C].[ClientID],
            [C].[Name],
            [A].[Street],
            ROW_NUMBER() OVER (ORDER BY [A].[AddressID]) AS [RowNo]
        FROM
            [Client] [C] INNER JOIN
            [ClientAddress] [CA] ON
                [C].[ClientID] = [CA].[ClientID] INNER JOIN
            [Address] [A] ON
                [CA].[AddressID] = [A].[AddressID]
    )
    SELECT
        [CTE].[ClientID],
        [CTE].[Name],
        [CTE].[Street],
        [CTE].[RowNo]
    FROM
        [Stuff] [CTE]
    WHERE
        [CTE].[RowNo] IN (SELECT MIN([CTE2].[RowNo]) FROM [Stuff] [CTE2] GROUP BY [CTE2].[ClientID])
    ORDER BY
        [CTE].[Name] ASC,
        [CTE].[Street] ASC
    
    DROP TABLE [ClientAddress]
    DROP TABLE [Address]
    DROP TABLE [Client]
    

    查询的目的是获取所有客户机及其第一个地址(ID最低的地址)。在我看来这应该行得通。

    我有一个理论,为什么有时它不起作用。CTE后面的语句在两个地方提到了CTE。如果CTE是不确定的,并且它运行了不止一次,那么CTE的结果在它引用的两个地方可能是不同的。

    在我的示例中,CTE的RowNo列使用ROW\u NUMBER()和order by子句,当多次运行时可能会导致不同的排序(我们按地址排序,客户机可以按任何顺序,具体取决于查询的执行方式)。

    因此,CTE和CTE2是否可能包含不同的结果?或者CTE只执行一次,我是否需要在其他地方查找问题?

    1 回复  |  直到 14 年前
        1
  •  5
  •   Quassnoi    14 年前

    这是没有任何保证。

    SQL Server CTE 每次访问或缓存结果时,根据计划而定。

    如果你的 CTE公司 如果不确定,则必须将其结果存储在临时表或表变量中,并使用它而不是 .

    PostgreSQL 另一方面,总是 CTE公司