2009-09-23 24 views
63

Encontré una pregunta respondida con la función Row_Number() en la cláusula where. Cuando probé una consulta, recibí el siguiente error:Función SQL Row_Number() en Where Clause

"Msg 4108, Level 15, State 1, Line 1 Windowed functions can only appear in the SELECT or ORDER BY clauses."

Aquí está la consulta que probé. Si alguien sabe cómo resolver esto, házmelo saber.

SELECT employee_id 
FROM V_EMPLOYEE 
WHERE row_number() OVER (ORDER BY employee_id) > 0 
ORDER BY Employee_ID 
+7

'ROW_NUMBER() OVER (ORDER BY employee_id)> 0' siempre evaluará a "TRUE" – Quassnoi

+1

Sí , está bien. No estoy preocupado por la condición, que puedo cambiar en cualquier momento. Quiero que la consulta funcione primero, y luego piense en mantener el número entre 500 y 800 ... gracias –

+1

@Joseph: ¿Por qué está tratando de evitar el uso de un CTE? –

Respuesta

49
SELECT employee_id 
FROM (
     SELECT employee_id, ROW_NUMBER() OVER (ORDER BY employee_id) AS rn 
     FROM V_EMPLOYEE 
     ) q 
WHERE rn > 0 
ORDER BY 
     Employee_ID 

Tenga en cuenta que este filtro es redundante: ROW_NUMBER() comienza desde 1 y es siempre mayor que 0.

+0

Excelente. Esto me ayudó mucho. Gracias. –

+0

@Quassnoi Gracias! Curiosidad: ¿por qué hay 'q'? Qué significa eso? –

+1

@ DavideChicco.it: en SQL Server, las tablas derivadas requieren un alias (debería haber escrito 'AS q' en su lugar, pero esto funcionaría bien). – Quassnoi

69

Para evitar este problema, ajuste su instrucción de selección en un CTE, y luego puede consultar contra el CTE y usar los resultados de la función de ventana en la cláusula where.

WITH MyCte AS 
(
    select employee_id, 
      RowNum = row_number() OVER (order by employee_id) 
    from  V_EMPLOYEE 
    ORDER BY Employee_ID 
) 
SELECT employee_id 
FROM MyCte 
WHERE RowNum > 0 
+4

Estoy tratando de evitar CTE. Ese es el peor caso que estoy buscando. gracias –

+2

Puede funcionar más rápido si utiliza una subconsulta en lugar de un CTE. He visto un mejor rendimiento por un factor de 1.5 en algunos casos –

+0

¿Puedo usar With en la subconsulta – Xulfee

14

Creo que quieres algo como esto:

SELECT employee_id 
FROM (SELECT employee_id, row_number() 
     OVER (order by employee_id) AS 'rownumber' 
     FROM V_EMPLOYEE) TableExpressionsMustHaveAnAliasForDumbReasons 
WHERE rownumber > 0 
+3

Cree un alias para la tabla si la consulta anterior no funciona para usted. Modifique la segunda última línea como 'From V_EMPLOYEE) A' que es agregar A como alias. –

3

Usando CTE (SQL Server 2005 +):

WITH employee_rows AS (
    SELECT t.employee_id, 
     ROW_NUMBER() OVER (ORDER BY t.employee_id) 'rownum' 
    FROM V_EMPLOYEE t) 
SELECT er.employee_id 
    FROM employee_rows er 
WHERE er.rownum > 1 

Usar la vista de línea/No CTE alternativa equivalente:

SELECT er.employee_id 
    FROM (SELECT t.employee_id, 
       ROW_NUMBER() OVER (ORDER BY t.employee_id) 'rownum' 
      FROM V_EMPLOYEE t) er 
WHERE er.rownum > 1 
+0

¿Cuál mejor en rendimiento? ¿Usando CTE o subconsulta? gracias –

+0

Ver la respuesta de Shannon: en su prueba son iguales. –

+5

No, no es más rápido. En 'SQL Server',' CTE' y las vistas en línea son lo mismo y tienen el mismo rendimiento. Cuando se usan funciones no deterministas en un 'CTE', se vuelve a evaluar en cada llamada. Uno tiene que usar trucos sucios para forzar la materialización de un 'CTE'. Vea estos artículos en mi blog: http: // explainextended.com/2009/07/28/sql-server-random-records-avoid-cte-reevaluation/http://explainextended.com/2009/05/28/generating-xml-in-subqueries/ – Quassnoi

5

En respuesta a los comentarios sobre rexem's respuesta, con respecto a si una vista en línea o CTE sería más rápida Reformé las consultas para usar una tabla I, y todos, tenían disponible: sys.objects.

WITH object_rows AS (
    SELECT object_id, 
     ROW_NUMBER() OVER (ORDER BY object_id) RN 
    FROM sys.objects) 
SELECT object_id 
FROM object_rows 
WHERE RN > 1 

SELECT object_id 
FROM (SELECT object_id, 
     ROW_NUMBER() OVER (ORDER BY object_id) RN 
    FROM sys.objects) T 
WHERE RN > 1 

Los planes de consulta producidos eran exactamente lo mismo. Me gustaría esperar que en todos los casos, el optimizador de consultas presente el mismo plan, al menos en el reemplazo simple de CTE con vista en línea o viceversa.

Por supuesto, intente sus propias consultas en su propio sistema para ver si hay alguna diferencia.

También, row_number() en la cláusula where es un error común en las respuestas dadas en Desbordamiento de pila. Logicaly row_number() no está disponible hasta que se procesa la cláusula de selección. La gente lo olvida y cuando responden sin probar la respuesta, a veces la respuesta es incorrecta. (A cargo yo mismo he sido culpable de.)

+0

Thx Shannon. ¿Qué versión de SQL Server usabas? –

+0

¿Eso significa que la respuesta proporcionada en ese enlace es incorrecta? Pero, la persona que publicó la pregunta estuvo de acuerdo en que está funcionando ... Sorprendente ... :-) –

+1

@ Joseph, pero si mira otra respuesta publicada por el OP en la pregunta vinculada, verá que está vinculado a una versión de el código que no es el mismo que en la respuesta aceptada. No sé por qué aceptó la respuesta, a pesar de que no funcionaría como se ingresó. Tal vez fue editado en algún momento después de ser aceptado, tal vez fue suficiente para hacerlo funcionar, incluso sin ser totalmente correcto. –

1

basado en la respuesta de OP a la pregunta:

Please see this link. Its having a different solution, which looks working for the person who asked the question. I'm trying to figure out a solution like this.

Paginated query using sorting on different columns using ROW_NUMBER() OVER() in SQL Server 2005

~Joseph

"método 1" es como la consulta del OP de la cuestión vinculada, y "Método 2" es como la consulta de la respuesta seleccionada. Había que mirar el código vinculado en este answer para ver qué estaba pasando realmente, ya que el código en la respuesta seleccionada se modificó para hacerlo funcionar.Prueba esto:

DECLARE @YourTable table (RowID int not null primary key identity, Value1 int, Value2 int, value3 int) 
SET NOCOUNT ON 
INSERT INTO @YourTable VALUES (1,1,1) 
INSERT INTO @YourTable VALUES (1,1,2) 
INSERT INTO @YourTable VALUES (1,1,3) 
INSERT INTO @YourTable VALUES (1,2,1) 
INSERT INTO @YourTable VALUES (1,2,2) 
INSERT INTO @YourTable VALUES (1,2,3) 
INSERT INTO @YourTable VALUES (1,3,1) 
INSERT INTO @YourTable VALUES (1,3,2) 
INSERT INTO @YourTable VALUES (1,3,3) 
INSERT INTO @YourTable VALUES (2,1,1) 
INSERT INTO @YourTable VALUES (2,1,2) 
INSERT INTO @YourTable VALUES (2,1,3) 
INSERT INTO @YourTable VALUES (2,2,1) 
INSERT INTO @YourTable VALUES (2,2,2) 
INSERT INTO @YourTable VALUES (2,2,3) 
INSERT INTO @YourTable VALUES (2,3,1) 
INSERT INTO @YourTable VALUES (2,3,2) 
INSERT INTO @YourTable VALUES (2,3,3) 
INSERT INTO @YourTable VALUES (3,1,1) 
INSERT INTO @YourTable VALUES (3,1,2) 
INSERT INTO @YourTable VALUES (3,1,3) 
INSERT INTO @YourTable VALUES (3,2,1) 
INSERT INTO @YourTable VALUES (3,2,2) 
INSERT INTO @YourTable VALUES (3,2,3) 
INSERT INTO @YourTable VALUES (3,3,1) 
INSERT INTO @YourTable VALUES (3,3,2) 
INSERT INTO @YourTable VALUES (3,3,3) 
SET NOCOUNT OFF 

DECLARE @PageNumber  int 
DECLARE @PageSize  int 
DECLARE @SortBy   int 

SET @PageNumber=3 
SET @PageSize=5 
SET @SortBy=1 


--SELECT * FROM @YourTable 

--Method 1 
;WITH PaginatedYourTable AS (
SELECT 
    RowID,Value1,Value2,Value3 
     ,CASE @SortBy 
      WHEN 1 THEN ROW_NUMBER() OVER (ORDER BY Value1 ASC) 
      WHEN 2 THEN ROW_NUMBER() OVER (ORDER BY Value2 ASC) 
      WHEN 3 THEN ROW_NUMBER() OVER (ORDER BY Value3 ASC) 
      WHEN -1 THEN ROW_NUMBER() OVER (ORDER BY Value1 DESC) 
      WHEN -2 THEN ROW_NUMBER() OVER (ORDER BY Value2 DESC) 
      WHEN -3 THEN ROW_NUMBER() OVER (ORDER BY Value3 DESC) 
     END AS RowNumber 
    FROM @YourTable 
    --WHERE 
) 
SELECT 
    RowID,Value1,Value2,Value3,RowNumber 
     ,@PageNumber AS PageNumber, @PageSize AS PageSize, @SortBy AS SortBy 
    FROM PaginatedYourTable 
    WHERE RowNumber>=(@PageNumber-1)*@PageSize AND RowNumber<=(@PageNumber*@PageSize)-1 
    ORDER BY RowNumber 



-------------------------------------------- 
--Method 2 
;WITH PaginatedYourTable AS (
SELECT 
    RowID,Value1,Value2,Value3 
     ,ROW_NUMBER() OVER 
     (
      ORDER BY 
       CASE @SortBy 
        WHEN 1 THEN Value1 
        WHEN 2 THEN Value2 
        WHEN 3 THEN Value3 
       END ASC 
       ,CASE @SortBy 
        WHEN -1 THEN Value1 
        WHEN -2 THEN Value2 
        WHEN -3 THEN Value3 
       END DESC 
     ) RowNumber 
    FROM @YourTable 
    --WHERE more conditions here 
) 
SELECT 
    RowID,Value1,Value2,Value3,RowNumber 
     ,@PageNumber AS PageNumber, @PageSize AS PageSize, @SortBy AS SortBy 
    FROM PaginatedYourTable 
    WHERE 
     RowNumber>=(@PageNumber-1)*@PageSize AND RowNumber<=(@PageNumber*@PageSize)-1 
     --AND more conditions here 
    ORDER BY 
     CASE @SortBy 
      WHEN 1 THEN Value1 
      WHEN 2 THEN Value2 
      WHEN 3 THEN Value3 
     END ASC 
     ,CASE @SortBy 
      WHEN -1 THEN Value1 
      WHEN -2 THEN Value2 
      WHEN -3 THEN Value3 
     END DESC 

SALIDA:

RowID Value1 Value2 Value3 RowNumber PageNumber PageSize SortBy 
------ ------ ------ ------ ---------- ----------- ----------- ----------- 
10  2  1  1  10   3   5   1 
11  2  1  2  11   3   5   1 
12  2  1  3  12   3   5   1 
13  2  2  1  13   3   5   1 
14  2  2  2  14   3   5   1 

(5 row(s) affected 

RowID Value1 Value2 Value3 RowNumber PageNumber PageSize SortBy 
------ ------ ------ ------ ---------- ----------- ----------- ----------- 
10  2  1  1  10   3   5   1 
11  2  1  2  11   3   5   1 
12  2  1  3  12   3   5   1 
13  2  2  1  13   3   5   1 
14  2  2  2  14   3   5   1 

(5 row(s) affected) 
+0

fyi, al usar _SET SHOWPLAN_ALL ON_ método 1 tenía un TotalSubtreeCost de 0.08424953, mientras que el método 2 estaba en 0.02627153. el método 2 fue más de tres veces mejor. –

+0

Lo sentimos: el método 2 es la vista en línea o CTE, ¿correcto? –

+0

@rexem, ambos métodos 1 y 2 utilizan CTE, la forma en que paginan y ordenan filas es diferente. No estoy seguro de por qué esta pregunta real es tan diferente de la pregunta a la que se vincula el OP (en la respuesta a esta pregunta del OP), pero mi respuesta crea un código de trabajo basado en el enlace al que hace referencia el OP –

20
Select * from 
(
    Select ROW_NUMBER() OVER (order by Id) as 'Row_Number', * 
    from tbl_Contact_Us 
) as tbl 
Where tbl.Row_Number = 5 
+1

¡buen ejemplo! muy mucho – richin

+1

¡Gracias por el gran ejemplo! Esto realmente me ayudó, ya que necesitaba esta funcionalidad para trabajar en una antigua base de datos de SQL Server 2000 donde los CTE no existen. – StarPilot

-1
WITH MyCte AS 
(
    select 
     employee_id, 
     RowNum = row_number() OVER (order by employee_id) 
    from V_EMPLOYEE 
) 
SELECT employee_id 
FROM MyCte 
WHERE RowNum > 0 
ORDER BY employee_id 
-1

si es necesario añadir la condición dinámica, puede utilizar existe en su búsqueda:

SELECT f0.* 
FROM FOO f0 
WHERE EXISTS 
    (SELECT f2.foo_id 
    FROM 
    (SELECT foo_id , 
     ROW_NUMBER() OVER(PARTITION BY F1.BAR_ID ORDER BY F1.AMOUNT) rk 
    FROM foo f1 
    )f2 
    WHERE f0.foo_id=f2.foo_id 
    AND rk   =2 -- your condition on row_number goes here 
); 
Cuestiones relacionadas