52

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

消息 102,级别 15,状态 1,过程 TopUsers,第 11 行
“偏移”附近的语法不正确。

创建视图的代码是

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排序这个结果的顺序,所以结果最大的用户排在最前面。

4

9 回答 9

88

我不确定你认为这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 完全忽略并优化掉:

在此处输入图像描述

根本没有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,但是添加了一个排序(请注意,代价不小)以显示按以下顺序排序的结果CustomerID

在此处输入图像描述

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

于 2013-03-03T17:27:56.723 回答
40

我已经成功强制使用订购视图

SELECT TOP 9999999 ... ORDER BY something

不幸的是,由于这里SELECT TOP 100 PERCENT的问题,使用不起作用。

于 2013-12-16T06:26:00.767 回答
8

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

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

警告:这应该只用于小列表,因为即使视图上的进一步连接或过滤器减小了它的大小,OFFSET 也会强制评估整个视图!

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

于 2017-07-27T09:17:33.590 回答
2

正如这篇文章中的一条评论建议使用存储过程来返回数据......我认为这是最好的答案。就我而言,我所做的是编写了一个View封装查询逻辑和连接的函数,然后我编写了一个Stored Proc返回排序后的数据,并且 proc 还包括其他增强功能,例如用于过滤数据的参数。

现在您必须选择查询视图,这允许您进一步操作数据。或者您可以选择执行存储过程,这样可以更快、更精确地输出。

STORED PROC 执行查询数据

执行 [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
于 2019-02-19T16:14:04.660 回答
-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

于 2013-12-11T07:10:35.447 回答
-1

请尝试以下逻辑。

SELECT TOP(SELECT COUNT(SNO) From MyTable) * FROM bar ORDER BY SNO
于 2015-03-26T10:52:36.950 回答
-2

只需在 Select 中使用 TOP 100 Percent:

     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
于 2014-11-21T14:37:33.853 回答
-2

为了向视图添加 ORDER BY,请执行以下操作

CREATE VIEW [dbo].[SQLSTANDARDS_PSHH]
AS


SELECT TOP 99999999999999
Column1,
Column2
FROM
dbo.Table
Order by
Column1
于 2015-10-02T18:07:31.170 回答
-2

使用程序

Create proc MyView as begin SELECT TOP 99999999999999 Column1, Column2 FROM dbo.Table Order by Column1 end

执行程序

执行我的视图

于 2017-04-19T08:58:41.100 回答