2013-01-06 38 views
5

这是从我的存储过程嵌套选择还是连接?

SELECT NULL AS StoryID 
     , AlbumID 
     , CAST(NULL as varchar) AS StoryTitle 
     , AlbumName 
     , (SELECT URL FROM AlbumPictures AS AlbumPictures_3 WHERE (AlbumID = Albums.AlbumID) AND (AlbumCover = 'True')) AS AlbumCover 
     , Votes 
     , CAST(NULL as Int) AS PictureId 
     , 'albums' AS tableName 
     , (SELECT NestedAlbums.AlbumID FROM NestedAlbums WHERE (AlbumID = Albums.AlbumID)) AS Flag 
INTO #Results2 
FROM Albums WHERE AlbumID IN (SELECT StringVal FROM funcListToTableInt(@whereAlbumID)) 

片段我在我的查询中使用的上述嵌套选择。我很想知道Nested Selects是否优于LEFT/Right JOINS或者我应该使用JOINS

表相册:

enter image description here

表NestedAlbums:

enter image description here

+3

更好?为了什么?可读性?性能?还有别的吗? – Oded

+3

无论如何,查询计划器可能会将您的嵌套选择重写为连接。使用SQL服务器,通过使用客户端来查看查询计划应该很容易。 – Pointy

+0

@Oded表演。 – user1593175

回答

8

一般写这篇明确OUTER JOIN会更好。

的SQL Server可能会需要一个断言添加到计划与验证子查询只返回最多一行(除非这是由唯一索引保证)的子查询的版本。这可以限制可用的转换。有关更多信息,请参见Scalar Subqueries

而且(虽然不是在你的问题是两个子查询不同的实例相关)写作作为一项明确JOIN允许您从连接表使用多个列的一个查询,而使用单独的类似的子查询就不会(SQL服务器没有逻辑来检测常见的子表达式)。

编辑:

继喜欢

SELECT NULL      AS StoryID, 
     A.AlbumID, 
     CAST(NULL AS VARCHAR(30)) AS StoryTitle, 
     A.AlbumName, 
     AP.URL     AS AlbumCover, 
     A.Votes, 
     CAST(NULL AS INT)   AS PictureId, 
     'albums'     AS tableName, 
     CASE 
     WHEN EXISTS (SELECT * 
         FROM NestedAlbums NA 
         WHERE NA.AlbumID = A.AlbumID 
          AND (AccountId = @AccountId)) THEN 1 
     ELSE 0 
     END      AS Flag 
INTO #Results2 
FROM Albums A 
     LEFT OUTER JOIN AlbumPictures AP 
     ON (AP.AlbumID = A.AlbumID) 
      AND (AP.AlbumCover = 'True') 
WHERE A.AlbumID IN (SELECT StringVal 
        FROM funcListToTableInt(@whereAlbumID)) 

评论中讨论的东西你可能会注意到这仍然有在SELECT列表中的子查询,但CASE ... EXISTSwill be implemented efficiently as a semi join

目前查询假设每本相簿最多一个匹配行将从AlbumPictures返回,并会报错了,如果这个假设是不正确的。这改变了语义,因为不会返回任何错误,并且您将得到具有各种URL的多行。如果您不希望发生这种情况,您需要定义使用哪个URL并添加GROUP BY

+0

对不起。我对此很陌生,谨慎解释。如何做EXPLICIT'OUTER JOIN' – user1593175

+0

@ user1593175 - 请为您的问题添加表格定义,包括任何唯一约束。 –

+1

@ user1593175 **明确连接**使用'ON'子句的连接条件**联系起来,而**隐式连接使用一个'WHERE'条款。显式连接更清楚地标识表格之间的关系。 – BellevueBob

0

在您的情况下,如果考虑性能,两种方法都将几乎相同。 (考虑到你在你的数据库以及索引的表)

JOIN是更容易使用和有效的,如果太多的JOIN是必需的。 JOIN可能在加入时创建重复数据,但嵌套查询不会。

但最后的表现将基于数量和你拥有的数据的组织。您可以使用SQL性能监视器工具根据您的数据进行验证。

1

对于我的钱,使用写入连接的查询,而不是嵌套选择是“更好”,因为它们更容易为人们阅读和理解(想维护,支持,未来的修改等)所有的表连接的逻辑是在一个地方(from条款),并恢复所有列在另外一个地方(select条款),它只是更容易弄清楚发生了什么事情。

其他人指出,执行查询可能会或可能不会执行命令(与往常一样,@Martin Smith的+1,用于指出难懂但非常相关的细节),这些答案就是你在这里寻找......但花一两分钟的时间来考虑那些未来的可怜的开发者,他们必须弄清楚被遗弃的代码实际上是在做什么。毕竟,它可能是你...

2

这是一个基于马丁建议使用外部连接的猜测查询。我相信当他说“明确”加入时,他指的是选择这种技术,而不是技术类型的加入。下面的查询假设你将只返回一行从NestedAlbums和AlbumCover表搜索一个单一的相册ID - 如果这不是真的,你会得到“重复行”,并将不得不添加一些条件的连接条款删除它们。此查询还假定AlbumCover位于“相册”表中。如果没有,你将不得不文本a.AlbumCover更改为ac.AlbumCover

SELECT NULL AS StoryID 
     , a.AlbumID 
     , CAST(NULL as varchar) AS StoryTitle 
     , a.AlbumName 
     , ac.URL AS AlbumCover 
     , a.Votes 
     , CAST(NULL as Int) AS PictureId 
     , 'albums' AS tableName 
     , na.AlbumID AS Flag 
INTO #Results2 
FROM Albums a 
LEFT OUTER JOIN NestedAlbums na ON a.AlbumID = na.AlbumID 
LEFT OUTER JOIN AlbumCover ac ON a.AlbumID = ac.AlbumID AND a.AlbumCover = 'True' 
WHERE a.AlbumID IN (SELECT StringVal FROM funcListToTableInt(@whereAlbumID)) 

此查询需要从相册中的结果,包括在具有匹配ALBUMID NestedAlbums任何行,并且还要有一个匹配的ALBUMID在任何行AlbumCover表(但前提是相册中的AlbumCover字段对于该行为“True”)。因为我们选择了LEFT OUTER JOIN作为我们的操作员,所以如果NestedAlbums或AlbumCover中没有匹配的行,SQL Server将为这些字段返回NULL,但行将由查询返回。如果连接是INNER JOIN,那么如果连接表中没有匹配的行,主表中的行也会被过滤掉。

+0

通过显式我的意思是子查询版本可能会显示在执行计划中作为外连接。 –

+0

Thanks.Appretiate它。 +1 – user1593175

3

第一个区别是,FROM子句管辖结果的初始基数。

就你的情况而言,结果将在“相册”中每行有一行。 SELECT子句中的标量子查询不能更改此项。如果子查询恰好返回多行,SQL Server将引发异常。它永远不会增加结果。

当您将此逻辑连接移动到FROM子句时,您可以重新定义的初始基数。它不再是每个行的相册中的一行,而是每行中的一行Album LEFT OUTER JOIN AblumPictures ON...等。如果在相册中每行产生多行,则SQL Server将而不是为子查询抛出异常。相反,它会将行添加到结果中。

因此,在这方面,子查询表达意图的工作做得更好,并且主动保护您免于违反该意图的数据:“为每个专辑提供一行,并为每个专辑包含此处的URL ,从那里嵌套的ID“等。

但是从功能上讲,有一个巨大的缺点:标量子查询不能返回整个元组。你已经完成了所有这些工作来编写子查询,SQL Server已经完成了所有这些工作来执行它,现在你仅限于这个单一的标量返回值!有时候这很好,但有时你需要更多。当你需要更多的时候,你需要FROM条款。

最接近FROM -clause相当于一个标量子查询是不是OUTER JOIN,而是奇妙OUTER APPLYOUTER APPLY不是一个标量表达式:它返回整个元组和任意数量的行。

第一近似:

SELECT Albums.*, AlbumPictures.URL, NestedAlbums.AlbumID 
FROM Albums 
OUTER APPLY (
    SELECT TOP (1) * FROM AlbumPictures 
    WHERE (AlbumID = Albums.AlbumID) AND (AlbumCover = 'True') 
) AlbumPictures 
OUTER APPLY (
    SELECT TOP (1) * FROM NestedAlbums 
    WHERE (AlbumID = Albums.AlbumID) 
) NestedAlbums 
WHERE Albums.AlbumID IN (SELECT StringVal FROM funcListToTableInt(@whereAlbumID)) 

因此,凭借TOP (1),专辑仍然在调整结果的初始基数。然而,我们现在有机会获得所有列从相关的表,这是真棒。

然后,如果我们有信心TOP (1)不necessary-- 凭借键和索引的子查询永远只能返回一行 --then我们可以使用一个简单的形式改写:

SELECT Albums.*, AlbumPictures.URL, NestedAlbums.AlbumID 
FROM Albums 
OUTER APPLY (
    SELECT * FROM AlbumPictures 
    WHERE (AlbumID = Albums.AlbumID) AND (AlbumCover = 'True') 
) AlbumPictures 
OUTER APPLY (
    SELECT * FROM NestedAlbums 
    WHERE (AlbumID = Albums.AlbumID) 
) NestedAlbums 
WHERE Albums.AlbumID IN (SELECT StringVal FROM funcListToTableInt(@whereAlbumID)) 

现在是逻辑上等同于OUTER JOIN

SELECT Albums.*, AlbumPictures.URL, NestedAlbums.AlbumID 
FROM Albums 
LEFT OUTER JOIN AlbumPictures 
    ON AlbumPictures.AlbumID = Albums.AlbumID 
AND AlbumPictures.AlbumCover = 'True' 
LEFT OUTER JOIN NestedAlbums 
    ON NestedAlbums.AlbumID = Albums.AlbumID 
WHERE Albums.AlbumID IN (SELECT StringVal FROM funcListToTableInt(@whereAlbumID)) 

有你有它。哪个更好?那么,无论你做什么,请保持简单。

表现智慧,一般来说,形式之间没有很大的差异。您可以并行比较您的特定表和索引的执行计划。了解SQL Server如何重写逻辑上等效的查询是一次很好的学习体验。我希望看到相同的计划OUTER APPLY(W/O TOP (1))和LEFT OUTER JOIN

+0

感谢您的详细解释。非常感谢。+ 1.我接受了@Martin Smith的回答,否则会接受你的。 – user1593175

+0

np,只是在随机问题上练习我的博览会。 –