使用ORDER BY子句创建视图

时间:2013-03-03 16:16:12

标签: sql-server database tsql view sql-server-2008-r2

我正在尝试使用ORDER BY子句创建视图。我已经在SQL Server 2012 SP1上成功创建它,但是当我尝试在SQL Server 2008 R2上重新创建它时,我收到此错误:

  

消息102,级别15,状态1,程序TopUsers,第11行
不正确   'OFFSET'附近的语法。

创建视图的代码是

CREATE View [dbo].[TopUsersTest] 
as 
select 
u.[DisplayName]  , sum(a.AnswerMark) as Marks
From Users_Questions us inner join [dbo].[Users] u
on u.[UserID] = us.[UserID] 
inner join [dbo].[Answers] a
on a.[AnswerID] = us.[AnswerID]
group by [DisplayName] 
order by Marks desc
OFFSET 0 ROWS

=====================

这是该图的屏幕截图

我希望返回用户“DisplayNameUserTotalMarks并订购此结果desc,以便将结果最大的用户放在首位。

9 个答案:

答案 0 :(得分:70)

我不确定你认为这个ORDER BY正在完成什么?即使您 以合法的方式将ORDER BY放入视图中(例如,通过添加TOP子句),如果您只是从视图中选择,例如SELECT * FROM dbo.TopUsersTest;没有ORDER BY子句,SQL Server可以以最有效的方式自由返回行,这不一定与您期望的顺序相匹配。这是因为ORDER BY被重载,因为它尝试用于两个目的:对结果进行排序并指定要包含在TOP中的行。在这种情况下,TOP总是获胜(尽管取决于选择扫描数据的索引,您可能会发现订单按预期工作 - 但这只是巧合)。

为了实现您的目标,您需要将ORDER BY子句添加到从视图中提取数据的查询中,而不是视图本身的代码。

所以你的观点代码应该是:

CREATE VIEW [dbo].[TopUsersTest] 
AS 
  SELECT 
    u.[DisplayName], SUM(a.AnswerMark) AS Marks
  FROM
    dbo.Users_Questions AS uq
    INNER JOIN [dbo].[Users] AS u
      ON u.[UserID] = us.[UserID] 
    INNER JOIN [dbo].[Answers] AS a
      ON a.[AnswerID] = uq.[AnswerID]
    GROUP BY u.[DisplayName];

ORDER BY没有意义,所以甚至不应该包括在内。


为了说明,使用AdventureWorks2012,这是一个例子:

CREATE VIEW dbo.SillyView
AS
  SELECT TOP 100 PERCENT 
    SalesOrderID, OrderDate, CustomerID , AccountNumber, TotalDue
  FROM Sales.SalesOrderHeader
  ORDER BY CustomerID;
GO

SELECT SalesOrderID, OrderDate, CustomerID, AccountNumber, TotalDue
FROM dbo.SillyView;

结果:

SalesOrderID   OrderDate   CustomerID   AccountNumber   TotalDue
------------   ----------  ----------   --------------  ----------
43659          2005-07-01  29825        10-4020-000676  23153.2339
43660          2005-07-01  29672        10-4020-000117  1457.3288
43661          2005-07-01  29734        10-4020-000442  36865.8012
43662          2005-07-01  29994        10-4020-000227  32474.9324
43663          2005-07-01  29565        10-4020-000510  472.3108

您可以从执行计划中看到TOPORDER BY已被SQL Server完全忽略和优化:

enter image description here

根本没有TOP运算符,也没有排序。 SQL Server已完全优化它们。

现在,如果您将视图更改为ORDER BY SalesID,那么您将恰好获得视图所述的排序,但仅限于 - 如前所述 - 巧合。

但是,如果您更改外部查询以执行所需的ORDER BY

SELECT SalesOrderID, OrderDate, CustomerID, AccountNumber, TotalDue
FROM dbo.SillyView
ORDER BY CustomerID;

您可以按照自己的方式订购结果:

SalesOrderID   OrderDate   CustomerID   AccountNumber   TotalDue
------------   ----------  ----------   --------------  ----------
43793          2005-07-22  11000        10-4030-011000  3756.989
51522          2007-07-22  11000        10-4030-011000  2587.8769
57418          2007-11-04  11000        10-4030-011000  2770.2682
51493          2007-07-20  11001        10-4030-011001  2674.0227
43767          2005-07-18  11001        10-4030-011001  3729.364

该计划仍然优化了视图中的TOP / ORDER BY,但是添加了一种排序(以不小的代价,请您注意)以显示{{1}排序的结果}:

enter image description here

所以,故事的道德,不要把ORDER BY放在视图中。将ORDER BY放在引用它们的查询中。如果排序很昂贵,您可以考虑添加/更改索引以支持它。

答案 1 :(得分:28)

我已成功强制使用

订购视图
SELECT TOP 9999999 ... ORDER BY something

由于问题here,使用SELECT TOP 100 PERCENT无法使用。

答案 2 :(得分:3)

从Sql 2012开始,您可以使用OFFSET强制在视图和子查询中进行排序

SELECT      C.CustomerID,
            C.CustomerName,
            C.CustomerAge
FROM        dbo.Customer C
ORDER BY    CustomerAge OFFSET 0 ROWS;

警告:这应仅用于小型列表,因为即使视图上的其他连接或过滤器减小了其大小,OFFSET也会强制评估完整视图!

没有好的方法可以在视图中强制进行排序而没有副作用,这是有充分理由的。

答案 3 :(得分:1)

正如本文中的评论之一建议使用存储过程来返回数据...我认为这是最佳答案。在我的情况下,我写了一个View来封装查询逻辑并进行联接,然后我写了一个Stored Proc以返回排序后的数据,并且proc还包括其他增强功能,例如用于过滤数据。

现在,您必须选择查询视图,这使您可以进一步处理数据。或者,您可以选择执行存储的proc,这样可以更快,更精确地输出。

存储过程执行以查询数据

exec [olap].[uspUsageStatsLogSessionsRollup]

查看定义

USE [DBA]
GO

/****** Object:  View [olap].[vwUsageStatsLogSessionsRollup]    Script Date: 2/19/2019 10:10:06 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


--USE DBA
-- select * from olap.UsageStatsLog_GCOP039 where CubeCommand='[ORDER_HISTORY]'
;

ALTER VIEW [olap].[vwUsageStatsLogSessionsRollup] as
(
    SELECT --*
        t1.UsageStatsLogDate
        , COALESCE(CAST(t1.UsageStatsLogDate AS nvarchar(100)), 'TOTAL- DATES:') AS UsageStatsLogDate_Totals
        , t1.ADUserNameDisplayNEW
        , COALESCE(t1.ADUserNameDisplayNEW, 'TOTAL- USERS:') AS ADUserNameDisplay_Totals
        , t1.CubeCommandNEW
        , COALESCE(t1.CubeCommandNEW, 'TOTAL- CUBES:') AS CubeCommand_Totals
        , t1.SessionsCount
        , t1.UsersCount
        , t1.CubesCount
    FROM
    (
        select 
            CAST(olapUSL.UsageStatsLogTime as date) as UsageStatsLogDate
            , olapUSL.ADUserNameDisplayNEW
            , olapUSL.CubeCommandNEW
            , count(*) SessionsCount
            , count(distinct olapUSL.ADUserNameDisplayNEW) UsersCount
            , count(distinct olapUSL.CubeCommandNEW) CubesCount
        from 
            olap.vwUsageStatsLog olapUSL
        where CubeCommandNEW != '[]'
        GROUP BY CUBE(CAST(olapUSL.UsageStatsLogTime as date), olapUSL.ADUserNameDisplayNEW, olapUSL.CubeCommandNEW )
            ----GROUP BY 
            ------GROUP BY GROUPING SETS
            --------GROUP BY ROLLUP
    ) t1

    --ORDER BY
    --  t1.UsageStatsLogDate DESC
    --  , t1.ADUserNameDisplayNEW
    --  , t1.CubeCommandNEW
)
;


GO

存储过程定义

USE [DBA]
GO

/****** Object:  StoredProcedure [olap].[uspUsageStatsLogSessionsRollup]    Script Date: 2/19/2019 9:39:31 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


-- =============================================
-- Author:      BRIAN LOFTON
-- Create date: 2/19/2019
-- Description: This proceedured returns data from a view with sorted results and an optional date range filter.
-- =============================================
ALTER PROCEDURE [olap].[uspUsageStatsLogSessionsRollup]
    -- Add the parameters for the stored procedure here
    @paramStartDate date = NULL,
    @paramEndDate date = NULL,
    @paramDateTotalExcluded as int = 0,
    @paramUserTotalExcluded as int = 0,
    @paramCubeTotalExcluded as int = 0
AS

BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @varStartDate as date 
        = CASE  
            WHEN @paramStartDate IS NULL THEN '1900-01-01' 
            ELSE @paramStartDate 
        END
    DECLARE @varEndDate as date 
        = CASE  
            WHEN @paramEndDate IS NULL THEN '2100-01-01' 
            ELSE @paramStartDate 
        END

    -- Return Data from this statement
    SELECT 
        t1.UsageStatsLogDate_Totals
        , t1.ADUserNameDisplay_Totals
        , t1.CubeCommand_Totals
        , t1.SessionsCount
        , t1.UsersCount
        , t1.CubesCount
        -- Fields with NULL in the totals
            --  , t1.CubeCommandNEW
            --  , t1.ADUserNameDisplayNEW
            --  , t1.UsageStatsLogDate
    FROM 
        olap.vwUsageStatsLogSessionsRollup t1
    WHERE

        (
            --t1.UsageStatsLogDate BETWEEN @varStartDate AND @varEndDate
            t1.UsageStatsLogDate BETWEEN '1900-01-01' AND '2100-01-01'
            OR t1.UsageStatsLogDate IS NULL
        )
        AND
        (
            @paramDateTotalExcluded=0
            OR (@paramDateTotalExcluded=1 AND UsageStatsLogDate_Totals NOT LIKE '%TOTAL-%')
        )
        AND
        (
            @paramDateTotalExcluded=0
            OR (@paramUserTotalExcluded=1 AND ADUserNameDisplay_Totals NOT LIKE '%TOTAL-%')
        )
        AND
        (
            @paramCubeTotalExcluded=0
            OR (@paramCubeTotalExcluded=1 AND CubeCommand_Totals NOT LIKE '%TOTAL-%')
        )
    ORDER BY
            t1.UsageStatsLogDate DESC
            , t1.ADUserNameDisplayNEW
            , t1.CubeCommandNEW

END


GO

答案 4 :(得分:0)

只需在选择中使用TOP 100%:

     CREATE VIEW [schema].[VIEWNAME] (
         [COLUMN1],
         [COLUMN2],
         [COLUMN3],
         [COLUMN4])
     AS 
        SELECT TOP 100 PERCENT 
         alias.[COLUMN1],
         alias.[COLUMN2],
         alias.[COLUMN3],
         alias.[COLUMN4]
        FROM 
           [schema].[TABLENAME] AS alias
          ORDER BY alias.COLUMN1
     GO

答案 5 :(得分:-1)

错误是:FROM (SELECT empno,name FROM table1 where location = 'A' ORDER BY emp_no)

解决方案是:FROM (SELECT empno,name FROM table1 where location = 'A') ORDER BY emp_no

答案 6 :(得分:-1)

请尝试以下逻辑。

SELECT TOP(SELECT COUNT(SNO) From MyTable) * FROM bar WITH(NOLOCK) ORDER BY SNO

答案 7 :(得分:-2)

为了向视图添加ORDER BY执行以下

CREATE VIEW [dbo].[SQLSTANDARDS_PSHH]
AS


SELECT TOP 99999999999999
Column1,
Column2
FROM
dbo.Table
Order by
Column1

答案 8 :(得分:-2)

使用程序

创建proc MyView 如  开始   选择TOP 99999999999999   列1,   列2   从   dbo.Table   订购   列1 端

执行程序

exec MyView