Crear una vista con la cláusula ORDER BY

Estoy intentando crear una vista con una cláusula ORDER BY. He creado con éxito en SQL Server 2012 SP1, pero cuando trato de volver a crear en SQL Server 2008 R2, me sale este error:

Msg 102, Level 15, State 1, Procedure TopUsers, Line 11
Incorrect sintaxis cerca de 'OFFSET'.

El código para crear la vista es

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

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

Esta es una captura de pantalla del diagrama

Deseo devolver el DisplayName de los usuarios y el UserTotalMarks y ordenar este resultado de forma descendente, para que el usuario con el mayor resultado esté en la parte superior.

Solución

No estoy seguro de qué crees que está consiguiendo este ORDER BY. Incluso si usted hace poner ORDER BY en la vista de una manera legal (por ejemplo, mediante la adición de una cláusula TOP), si sólo selecciona de la vista, por ejemplo, SELECT * FROM dbo.TopUsersTest; sin una cláusula ORDER BY, SQL Server es libre de devolver las filas de la manera más eficiente, que no coincidirá necesariamente con el orden que usted espera. Esto se debe a que la cláusula ORDER BY está sobrecargada, ya que intenta servir a dos propósitos: ordenar los resultados y dictar qué filas incluir en TOP. En este caso, TOP siempre gana (aunque dependiendo del índice elegido para escanear los datos, podría observar que su orden funciona como se esperaba - pero esto es sólo una coincidencia).

**Para conseguir lo que quieres, necesitas añadir tu cláusula ORDER BY a las consultas que extraen los datos de la vista, no al código de la propia vista.

Así que el código de la vista debería ser:

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];

El ORDER BY no tiene sentido, por lo que ni siquiera debería incluirse.


Para ilustrar, usando AdventureWorks2012, aquí está un ejemplo:

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;

Resultados:

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

Y se puede ver en el plan de ejecución que el TOP y ORDER BY han sido absolutamente ignorados y optimizados por SQL Server:

No hay operador TOP en absoluto, y no hay ordenación. SQL Server los ha optimizado completamente.

Ahora, si cambias la vista a ORDER BY SalesID, obtendrás el orden que la vista indica, pero sólo - como se mencionó antes - por coincidencia.

Pero si cambias tu consulta externa para realizar el ORDER BY que querías:

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

Obtendrá los resultados ordenados de la forma que desee:

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

Y el plan aún ha optimizado el TOP/ORDER BY en la vista, pero se añade una ordenación (con un coste no pequeño, eso sí) para presentar los resultados ordenados por CustomerID:

Así que, moraleja de la historia, no pongas ORDER BY en las vistas. Pon ORDER BY en las consultas que hacen referencia a ellas. Y si la ordenación es costosa, podrías considerar añadir/cambiar un índice para soportarla.

Comentarios (10)

He tenido éxito forzando la vista a ser ordenada usando

SELECT TOP 9999999 ... ORDER BY something

Desafortunadamente usando SELECT TOP 100 PERCENT no funciona debido al problema aquí.

Comentarios (2)

El error es: FROM (SELECT empno,name FROM table1 where location = 'A' ORDER BY emp_no)

La solución es: FROM (SELECT empno,name FROM table1 where location = 'A') ORDER BY emp_no.

Comentarios (0)