5

这是UNION ALL存储过程的正确方法吗?

ALTER PROCEDURE [GetHomePageObjectPageWise]
      @PageIndex INT = 1
      ,@PageSize INT = 10
      ,@PageCount INT OUTPUT
      ,@whereStoryID varchar(2000)
      ,@whereAlbumID varchar(2000)
      ,@wherePictureID varchar(2000)
AS
BEGIN
      SET NOCOUNT ON;

      SELECT StoryID
      , AlbumID
      , StoryTitle
      , NULL AS AlbumName
      , (SELECT URL FROM AlbumPictures WHERE (AlbumID = dbo.Stories.AlbumID) AND (AlbumCover = 'True')) AS AlbumCover
      , Votes
      , NULL AS PictureId
      , 'stories' AS tableName
      , NEWID() AS Sort 

INTO #Results1
FROM Stories WHERE StoryID IN (SELECT StringVal FROM funcListToTableInt(@whereStoryID))

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

        SELECT NULL AS StoryID
        , NULL AS AlbumID
        , NULL AS StoryTitle
        , NULL AS AlbumName
        , URL
        , Votes
        , PictureID
        , 'pictures' AS tableName
        , NEWID() AS Sort
        INTO #Results3
FROM AlbumPictures AS AlbumPictures_1
WHERE PictureID IN (SELECT StringVal FROM funcListToTableInt(@wherePictureID))

SELECT * INTO #Results4 FROM #Results1
UNION ALL
SELECT * FROM #Results2
UNION ALL
SELECT * FROM #Results3

SELECT ROW_NUMBER() OVER
            (
                  ORDER BY [Sort] DESC
            )AS RowNumber
            , * INTO #Results
            FROM #Results4


      DECLARE @RecordCount INT
      SELECT @RecordCount = COUNT(*) FROM #Results

      SET @PageCount = CEILING(CAST(@RecordCount AS DECIMAL(10, 2)) / CAST(@PageSize AS DECIMAL(10, 2)))

      SELECT * FROM #Results
      WHERE RowNumber BETWEEN(@PageIndex -1) * @PageSize + 1 AND(((@PageIndex -1) * @PageSize + 1) + @PageSize) - 1

      DROP TABLE #Results
      DROP TABLE #Results1
      DROP TABLE #Results2
      DROP TABLE #Results3
      DROP TABLE #Results4
END
4

2 回答 2

3

以下是一些评论:

(1) 我更喜欢表值变量 ( declare @Results as table . . .) 而不是临时表。

(2) 通常,编写单个查询可能比单独的查询更好。因此,无论如何,您都可以消除中间结果表。SQL 引擎旨在优化执行路径。给他们一个工作的机会。也就是说,有时他们会出错,中间表是可取的/必要的。

(3) 你的分类没问题,但你确实需要小心。如果 Sort 具有重复的值,您将面临获得重复值的风险,并且不同的迭代会导致问题。

(4) 既然您实际上只是从查询中返回结果,为什么不直接定义查询(可能作为视图)并完全消除存储过程?存储过程使 SQL Server 不太可能出于分页目的缓存结果。

(5) 我还想知道您是否可以删除from子句中的函数调用,因为它们也会对性能产生负面影响。

于 2012-12-30T21:02:30.593 回答
3

这些天我喜欢使用非物化CTEs 而不是临时表——尽管在某些情况下(比如数据需要索引)我会使用临时表。

主要是很多化妆品的东西,我真的会改变所有的方式,希望将来让它更具可读性(这没有经过测试,因为我没有你的数据副本)

ALTER PROCEDURE [GetHomePageObjectPageWise]
      @PageIndex        INT = 1
      ,@PageSize        INT = 10
      ,@PageCount       INT OUTPUT
      ,@whereStoryID    VARCHAR(2000)
      ,@whereAlbumID    VARCHAR(2000)
      ,@wherePictureID VARCHAR(2000)
AS
BEGIN
     SET NOCOUNT ON;

    WITH Results1 AS
        (
        SELECT 
            StoryID,
            AlbumID,
            StoryTitle,
            [AlbumName] = NULL,
            [AlbumCover] = 
                (
                SELECT URL 
                FROM AlbumPictures 
                WHERE (AlbumID = dbo.Stories.AlbumID) AND (AlbumCover = 'True')
                ),
            Votes,
            [PictureId] = NULL,
            [tableName] = 'stories',
            [Sort] = NEWID()
        FROM Stories 
        WHERE 
                StoryID IN 
                (
                SELECT StringVal 
                FROM funcListToTableInt(@whereStoryID)
                )
        )
    , Results2 AS
        (
        SELECT    
            [StoryID] = NULL ,
            AlbumID,
            [StoryTitle] NULL,
            AlbumName,
            [AlbumCover] = 
                (
                SELECT URL 
                FROM AlbumPictures AS AlbumPictures_3 --<<<DO YOU NEED THIS ALIAS?
                WHERE (AlbumID = Albums.AlbumID) AND (AlbumCover = 'True')
                ),
            Votes,
            [PictureId] = NULL,
            [tableName] = 'albums',
            [Sort] = NEWID()
        FROM Albums 
        WHERE 
            AlbumID IN 
                (
                SELECT StringVal 
                FROM funcListToTableInt(@whereAlbumID)
                )
        )       
    , Result3 AS  
        (
        SELECT 
            [StoryID] = NULL, 
            [AlbumID] = NULL,
            [StoryTitle] = NULL,
            [AlbumName] = NULL,
            URL,
            Votes,
            PictureID,
            [tableName] = 'pictures',
            [Sort] = NEWID()
        FROM AlbumPictures --AS AlbumPictures_1 <<<DO YOU NEED THIS ALIAS?
        WHERE 
            PictureID IN 
                (
                SELECT StringVal 
                FROM funcListToTableInt(@wherePictureID)
                )
        )
    , Result4 AS  
        (
        SELECT * FROM Results1 UNION ALL
        SELECT * FROM Results2 UNION ALL
        SELECT * FROM Results3
        )
    , Results AS
        (
        SELECT 
                [RowNumber] = ROW_NUMBER() OVER (ORDER BY [Sort] DESC),
            x.* 
        FROM Results4   x
        )
 SELECT * 
 FROM Results
 WHERE RowNumber BETWEEN(@PageIndex -1) * @PageSize + 1 AND(((@PageIndex -1) * @PageSize + 1) + @PageSize) - 1;
 DECLARE @RecordCount INT = @@RowCount; 

 SET @PageCount = CEILING(CAST(@RecordCount AS DECIMAL(10, 2)) / CAST(@PageSize AS DECIMAL(10, 2)));

END

我通常使用 Aaron Bertrand 的建议来编写存储过程 这篇博文是我的清单,也是我用来尝试统一我使用的所有 Sproc 的样式的模板:

https://sqlblog.org/2008/10/30/my-stored-procedure-best-practices-checklist


我认为正如 Gordon 建议的那样,您可以将大量逻辑从存储过程中移出并创建一个VIEW这样的:

CREATE VIEW [console].[vw_mySimpleView]
AS

BEGIN
    SET NOCOUNT ON;

    WITH Results1 AS
        (
        SELECT 
            StoryID,
            AlbumID,
            StoryTitle,
            [AlbumName] = NULL,
            [AlbumCover] = 
                (
                SELECT URL 
                FROM AlbumPictures 
                WHERE (AlbumID = dbo.Stories.AlbumID) AND (AlbumCover = 'True')
                ),
            Votes,
            [PictureId] = NULL,
            [tableName] = 'stories',
            [Sort] = NEWID()
        FROM Stories 
        )
    , Results2 AS
        (
        SELECT    
            [StoryID] = NULL ,
            AlbumID,
            [StoryTitle] NULL,
            AlbumName,
            [AlbumCover] = 
                (
                SELECT URL 
                FROM AlbumPictures 
                WHERE (AlbumID = Albums.AlbumID) AND (AlbumCover = 'True')
                ),
            Votes,
            [PictureId] = NULL,
            [tableName] = 'albums',
            [Sort] = NEWID()
        FROM Albums 
        )       
    , Result3 AS  
        (
        SELECT 
            [StoryID] = NULL, 
            [AlbumID] = NULL,
            [StoryTitle] = NULL,
            [AlbumName] = NULL,
            URL,
            Votes,
            PictureID,
            [tableName] = 'pictures',
            [Sort] = NEWID()
        FROM AlbumPictures
        )
    , Result4 AS  
        (
        SELECT * FROM Results1 UNION ALL
        SELECT * FROM Results2 UNION ALL
        SELECT * FROM Results3
        )
SELECT *
FROM Results4;

GO 

那么 Sproc 会短很多:

ALTER PROCEDURE [GetHomePageObjectPageWise]
      @PageIndex        INT = 1
      ,@PageSize        INT = 10
      ,@PageCount       INT OUTPUT
      ,@whereStoryID    VARCHAR(2000)
      ,@whereAlbumID    VARCHAR(2000)
      ,@wherePictureID VARCHAR(2000)
AS
BEGIN
     SET NOCOUNT ON;

    SELECT * 
    FROM 
        (
        SELECT 
            [RowNumber] = ROW_NUMBER() OVER (ORDER BY [Sort] DESC),
            x.* 
        FROM 
            (
            SELECT *
            FROM [dbo].[vw_mySimpleView] 
            WHERE 
                StoryID IN 
                    (
                    SELECT StringVal 
                    FROM funcListToTableInt(@whereStoryID)
                    )
                            OR  
                            AlbumID IN 
                                   (
                                   SELECT StringVal 
                                   FROM funcListToTableInt(@whereAlbumID)
                                   )
            )   x
        )
    WHERE RowNumber BETWEEN(@PageIndex -1) * @PageSize + 1 AND(((@PageIndex -1) * @PageSize + 1) + @PageSize) - 1;
    DECLARE @RecordCount INT = @@RowCount; 

    SET @PageCount = CEILING(CAST(@RecordCount AS DECIMAL(10, 2)) / CAST(@PageSize AS DECIMAL(10, 2)));


END
于 2012-12-30T22:08:58.323 回答