2010-08-18 55 views
2

我正试图解决我们在上次迭代测试中发现的错误。它涉及一个使用公共表格表达式的查询。查询的主题是它模拟'第一'聚合操作(获取此分组的第一行)。该公共表格表达式的结果有多少次被评估?

问题是,在某些情况下,查询似乎完全随意选择行 - 返回同一组中的多行,有些组完全被删除。但是,它总是选择正确的行数。

我已经创建了一个最小的例子在这里发布。有客户和地址,以及定义它们之间关系的表格。这是一个我正在查看的实际查询的简化版本,但我相信它应该具有相同的特征,并且它是一个很好的示例,可用于解释我认为出错的内容。

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_NUMBER()和order by子句,可能会在多次运行时导致不同的排序(我们按地址排序,客户端可以按任何顺序该查询被执行)。

因为这可能是CTE和CTE2可以包含不同的结果吗?或者是CTE只执行一次,我是否需要在别处寻找问题?

回答

5

不以任何方式保证。根据计划,每次访问或缓存结果时可自由评估CTE

您可能需要阅读这篇文章:

如果您CTE不确定性,则必须将其结果存储在临时表或表变量,并用它而不是CTE

PostgreSQL另一方面,总是只评估CTE s一次,缓存其结果。