2012-05-16 14 views
6

Supongamos que tengo esta tabla:funciones de ventana y más agregación "local"

select * from window_test; 

k | v 
---+--- 
a | 1 
a | 2 
b | 3 
a | 4 

En última instancia quiero llegar:

k | min_v | max_v 
---+-------+------- 
a | 1  | 2 
b | 3  | 3 
a | 4  | 4 

Pero yo sería tan feliz de conseguir esto (ya que yo puede filtrar fácilmente con distinct):

k | min_v | max_v 
---+-------+------- 
a | 1  | 2 
a | 1  | 2 
b | 3  | 3 
a | 4  | 4 

¿es posible lograr esto con PostgreSQL 9.1+ func ventana ciones? Estoy tratando de entender si puedo conseguir que use una partición separada para la primera y última ocurrencia de k=a en esta muestra (ordenada por v).

+0

Primero: ¿Desea "colapsar" rangos completos? Es decir. ¿Qué esperarías si agregas '(a, 5), (a, 6), (a, 7)' a tu conjunto de datos? Segundo: ¿'v' siempre está aumentando perfectamente en 1 o puede haber lagunas? –

+0

@ A.H. 'v' no está aumentando perfectamente (de hecho, es una marca de tiempo). Sí, quiero colapsar rangos completos y esperaría '(a, 4, 5)' como la última tupla después de insertar '(a, 5)'. –

+0

@KonradGarus, ¿alguna de las soluciones proporcionadas funciona para usted? – vyegorov

Respuesta

7

Esto devuelve el resultado deseado con los datos de la muestra. No está seguro de si va a trabajar para los datos del mundo real:

select k, 
     min(v) over (partition by group_nr) as min_v, 
     max(v) over (partition by group_nr) as max_v 
from (
    select *, 
      sum(group_flag) over (order by v,k) as group_nr 
    from (
    select *, 
      case 
       when lag(k) over (order by v) = k then null 
       else 1 
      end as group_flag 
    from window_test 
    ) t1 
) t2 
order by min_v; 

me he dejado la DISTINCT sin embargo.

+0

Muy buen enfoque! – vyegorov

+0

Gracias chicos. Me gustan ambas soluciones, pero encontrar esta más elegante. –

+0

Buen trabajo allí, pero no funcionaba para mí (postgresql 9.3) hasta que agregué la oración 'group by k, group_nr' antes del' order by min_v', y reemplacé el 'min (v) over ...' y 'max (v) over ...' funciones de ventana en la selección principal para funciones agregadas simples 'min (v)' y 'max (v)'. Gracias, esto realmente me ayudó :) – Aleix

1

EDIT: He ocurrió la siguiente consulta - sin funciones de ventana en absoluto:

WITH RECURSIVE tree AS (
    SELECT k, v, ''::text as next_k, 0 as next_v, 0 AS level FROM window_test 
    UNION ALL 
    SELECT c.k, c.v, t.k, t.v + level, t.level + 1 
    FROM tree t JOIN window_test c ON c.k = t.k AND c.v + 1 = t.v), 
partitions AS (
    SELECT t.k, t.v, t.next_k, 
     coalesce(nullif(t.next_v, 0), t.v) AS next_v, t.level 
    FROM tree t 
    WHERE NOT EXISTS (SELECT 1 FROM tree WHERE next_k = t.k AND next_v = t.v)) 
SELECT min(k) AS k, v AS min_v, max(next_v) AS max_v 
    FROM partitions p 
GROUP BY v 
ORDER BY 2; 

que he proporcionado 2 consultas de trabajo ahora, espero que uno de ellos suite de usted.

SQL Fiddle para esta variante.


Otra forma de lograr esto es utilizar una secuencia de soporte.

  1. crear una secuencia de soporte:

    CREATE SEQUENCE wt_rank START WITH 1; 
    
  2. La consulta:

    WITH source AS (
        SELECT k, v, 
         coalesce(lag(k) OVER (ORDER BY v), k) AS prev_k 
        FROM window_test 
        CROSS JOIN (SELECT setval('wt_rank', 1)) AS ri), 
    ranking AS (
        SELECT k, v, prev_k, 
         CASE WHEN k = prev_k THEN currval('wt_rank') 
           ELSE nextval('wt_rank') END AS rank 
        FROM source) 
    SELECT r.k, min(s.v) AS min_v, max(s.v) AS max_v 
        FROM ranking r 
        JOIN source s ON r.v = s.v 
        GROUP BY r.rank, r.k 
        ORDER BY 2; 
    
+0

Gracias. En realidad, 'v' es la columna de clasificación. Esta solución se rompe si agrego (a, 5) tupla al final (en otras palabras, si hay algunos grupos con más de una fila cada uno). –

0

¿No lo haría por usted, sin la necesidad de ventanas, particiones o uniones? Simplemente utiliza un truco de SQL tradicional para la búsqueda de tuplas más cercanos a través de una combinación de autorrestricción, y un mínimo en la diferencia:

SELECT k, min(v), max(v) FROM (
    SELECT k, v, v + min(d) lim FROM (
     SELECT x.*, y.k n, y.v - x.v d FROM window_test x 
     LEFT JOIN window_test y ON x.k <> y.k AND y.v - x.v > 0) 
    z GROUP BY k, v, n) 
w GROUP BY k, lim ORDER BY 2; 

Creo que esto es probablemente una solución más 'relacional', pero no estoy seguro acerca de su eficiencia.

+0

La solución con una función de ventana es típicamente más rápida. Su consulta debe pasar por la tabla 'window_test' dos veces (debido a la auto-unión). Una unión que utiliza '<' or '>' generalmente tampoco se escala. Además: está agrupando el resultado de la unión, lo que significa que el grupo debe procesar más filas para entonces con la función de ventana (que solo necesita tratar las filas en la tabla una vez). Pero, ¿qué pasa con las funciones de ventana? Cada DBMS moderno los admite. –

+0

De acuerdo. Y, desde luego, nada en contra de las funciones de ventana. Ofrezco esta solución al OP, ya que podría ser interesante que solo use operadores del álgebra relacional y, por lo tanto, podría considerarse 'más pura' (personalmente creo que también es más clara desde el punto de vista conceptual). Sin embargo, no lo usaría en una relación de mil millones de filas ... –

Cuestiones relacionadas