2010-06-09 17 views
5

tengo estas filas de una tablacómo filtrar filas en un filtro complejo

ID Name Price Delivery 
== ==== ===== ======== 
1 apple 1  1 
2 apple 3  2 
3 apple 6  3 
4 apple 9  4 
5 orange 4  6 
6 orange 5  7 

Quiero tener el precio en la tercera entrega (Delivery = 3) o el último precio si no hay tercera entrega.

Me daría esto:

ID Name Price Delivery 
== ==== ===== ======== 
3 apple 6  3 
6 orange 5  7 

yo no necesaria quieren una solución completa, pero una idea de lo que debe buscar sería muy apreciada.

Respuesta

3

Use ROW_NUMBER dos veces: una para filtrar las filas que están después de la tercera entrega, y la segunda para encontrar la última fila restante (es decir, una consulta máxima típica por grupo).

Lo he implementado usando CTE. Lo probé en SQL Server, pero creo que Oracle admite la misma sintaxis.

WITH T1 AS (
    SELECT 
     ID, Name, Price, Delivery, 
     ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Delivery) AS rn 
    FROM Table1 
), T2 AS (
    SELECT 
     t1.*, 
     ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Delivery DESC) AS rn2 
    FROM T1 
    WHERE rn <= 3 
) 
SELECT ID, Name, Price, Delivery 
FROM T2 
WHERE rn2 = 1 

Resultado:

ID Name Price Delivery 
3 apple 6  3  
6 orange 5  7  
+0

Esta respuesta me ayudó más. – dsimard

2
select t3.ID, t3.Name, t3.Price, t3.Delivery 
from (
    select Name, max(Delivery) as MaxDelivery 
    from MyTable 
    group by Name 
) t1 
left outer join MyTable t2 on t1.Name = t2.Name and Delivery = 3 
inner join MyTable t3 on t1.Name = t3.name 
    and t3.Delivery = coalesce(t2.Delivery, t1.MaxDelivery) 
+0

Este es simple y elegante. Simplemente no me gusta la unión externa izquierda, pero tal vez hay una forma de evitarlo. – dsimard

+0

Esta accede a la mesa tres veces, donde un acceso es suficiente, como lo indican las otras soluciones aquí. –

2

respuestas APC de Mark y de trabajo si nos referimos la tercera entrega, sin importar el número de entrega. Aquí hay una solución con funciones analíticas que busca específicamente para un registro con entrega = 3.

CREATE TABLE FRUITS (
    ID NUMBER, 
    Name VARCHAR2(10), 
    Price INTEGER, 
    Delivery INTEGER); 

INSERT INTO FRUITS VALUES (1, 'apple', 1, 1); 
INSERT INTO FRUITS VALUES (2, 'apple', 3, 2); 
INSERT INTO FRUITS VALUES (3, 'apple', 6, 3); 
INSERT INTO FRUITS VALUES (4, 'apple', 9, 4); 
INSERT INTO FRUITS VALUES (5, 'orange', 4, 6); 
INSERT INTO FRUITS VALUES (6, 'orange', 5, 7); 
INSERT INTO FRUITS VALUES (7, 'pear', 2, 5); 
INSERT INTO FRUITS VALUES (8, 'pear', 4, 6); 
INSERT INTO FRUITS VALUES (9, 'pear', 6, 7); 
INSERT INTO FRUITS VALUES (10, 'pear', 8, 8); 

SELECT ID, 
     Name, 
     Price, 
     Delivery 
    FROM (SELECT ID, 
       Name, 
       Price, 
       Delivery, 
       SUM(CASE WHEN Delivery = 3 THEN 1 ELSE 0 END) 
        OVER (PARTITION BY Name) AS ThreeCount, 
       ROW_NUMBER() 
        OVER (PARTITION BY Name ORDER BY Delivery DESC) AS rn 
      FROM FRUITS) 
WHERE (ThreeCount <> 0 AND Delivery = 3) OR 
     (ThreeCount = 0 AND rn = 1) 
ORDER BY ID; 

DROP TABLE FRUITS; 

Y los resultados de Oracle XE 10g:

ID Name  Price Delivery 
---- ---------- ------- ---------- 
3 apple  6  3   
6 orange  5  7   
10 pear  8  8   

que incluyó un tercer fruto en los datos de ejemplo para ilustrar el efecto de diferentes interpretaciones de la pregunta. Las otras soluciones elegirían ID = 9 para la pera.

4
SQL> create table t (id,name,price,delivery) 
    2 as 
    3 select 1, 'apple', 1, 1 from dual union all 
    4 select 2, 'apple', 3, 2 from dual union all 
    5 select 3, 'apple', 6, 3 from dual union all 
    6 select 4, 'apple', 9, 4 from dual union all 
    7 select 5, 'orange', 4, 6 from dual union all 
    8 select 6, 'orange', 5, 7 from dual 
    9/

Table created. 

SQL> select max(id) keep (dense_rank last order by nullif(delivery,3) nulls last) id 
    2  , name 
    3  , max(price) keep (dense_rank last order by nullif(delivery,3) nulls last) price 
    4  , max(delivery) keep (dense_rank last order by nullif(delivery,3) nulls last) delivery 
    5 from t 
    6 group by name 
    7/

     ID NAME  PRICE DELIVERY 
---------- ------ ---------- ---------- 
     3 apple   6   3 
     6 orange   5   7 

2 rows selected. 

EDITAR: Puesto que usted quiere "una idea de qué buscar", aquí es una descripción de por qué creo que esta solución es la mejor, además de ser la consulta con la menor cantidad de líneas. El conjunto de resultados esperados indica que desea agrupar sus datos por nombre de fruta ("grupo por nombre"). Y de cada grupo desea mantener los valores de los registros con delivery = 3 o cuando ese número no existe, el último ("keep (dense_rank último pedido por nullif (delivery, 3) nulls last"). En mi opinión, la consulta anterior simplemente lee el estilo. Y utiliza un único acceso a la tabla para obtener el resultado, aunque mi consulta no es único en eso.

Saludos, Rob.

+0

+1 La solución KEEP es mucho más ordenada que la mía, así que la borro. – APC

+0

Me gusta esta respuesta porque es concisa y tiene razón, sí lee lógicamente como la pregunta original. Sin embargo, no me gusta la repetición de la cláusula 'dense_rank'. Para este pequeño ejemplo, no es gran cosa, pero si hubiera más columnas, sería difícil de leer y mantener (¿y si en lugar de eso quisiéramos la cuarta entrega o necesitáramos ordenar por más columnas?) –

+0

Acepto que la repetición de esta sintaxis no es ideal para el mantenimiento, especialmente cuando tienes muchas columnas.Si espera que sus requisitos cambien mucho, la solución analítica podría valer la pena. –

Cuestiones relacionadas