我正在尝试使用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
=====================
这是该图的屏幕截图
我希望返回用户“DisplayName
和UserTotalMarks
并订购此结果desc,以便将结果最大的用户放在首位。
答案 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
您可以从执行计划中看到TOP
和ORDER 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
,但是添加了一种排序(以不小的代价,请您注意)以显示{{1}排序的结果}:
所以,故事的道德,不要把ORDER BY放在视图中。将ORDER BY放在引用它们的查询中。如果排序很昂贵,您可以考虑添加/更改索引以支持它。
答案 1 :(得分:28)
答案 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,这样可以更快,更精确地输出。
存储过程执行以查询数据
查看定义
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