2011-07-05 6 views
5

Tengo una tabla de URL y un atributo es el dominio.¿Cómo selecciono un número limitado de filas con el mismo atributo?

Supongamos que tengo 100 URL de Google, 100 de Facebook, 100 de Ebay y lo mismo para otros dominios, pero quiero recuperar las primeras 30 URL de Google, 30 de Facebook, 30 de Ebay y 30 de las otras limitando a un máximo de 500 URL.

¿Cómo hago esto?

+0

¿Cuántos dominios tiene en esa tabla? Si hay más de 16, ¿cómo debería comportarse la consulta? –

+0

Sí, mucho más que 16. Si al seleccionar 30 por dominio excede el límite máximo, deje de obtener más URL. Ejemplo: si el límite fue 70, obtenga 30 de Google, 30 de Facebook, 10 de Ebay y nadie de los demás. –

+1

¿Y qué dominios deberían tener preferencia? ¿Cómo se determina eso? –

Respuesta

2

el siguiente código SQL resuelve mi caso, pero las URL son fuera de servicio debido a que el row_number no siguen las órdenes. Creo que este SQL necesita alguna mejora.

SELECT url,row_number FROM(
    SELECT url,row_number() OVER (PARTITION BY domain) FROM website 
    WHERE domain IN 
    (SELECT DISTINCT domain FROM link) 
) AS links 
WHERE row_number <= 10 
LIMIT 25 
+0

Puede escribir 'PARTITION BY domain ORDER BY ctid' o especificar otra columna para ordenar. De todos modos, buena respuesta. –

1

¿Qué tal algo como esto:

SELECT url FROM link WHERE domain='Google' LIMIT 30 
UNION 
SELECT url FROM link WHERE domain='Facebook' LIMIT 30 
UNION 
SELECT ... 

etc.

+0

Sugeriría agregar 'SELECT 'Google', url .... SELECCIONE 'Facebook', url ...' para que pueda decir cuál fue de dónde. – JNK

+0

Pero entonces ¿puedes poner 'LIMIT 70' al final de todo eso? –

+1

Puede envolver las uniones en otro SELECT y luego limitarlo, si eso es lo que se necesita – duduamar

1

Mi solución en PL/pgSQL basada. He encontrado una mejor manera, que no es necesario tabla temporal adicional (usando INSERT a la mesa en el cuerpo de la función), es decir que puede utilizar esta función para obtener el resultado directamente:

CREATE OR REPLACE FUNCTION getURLs(singleLimit integer, totalLimit integer) 
RETURNS SETOF RECORD AS $$ 
DECLARE 
    dom text; 
    nrOfDomains integer; 
    i integer; 
    lim integer; 
    remainder integer; 
BEGIN 
    nrOfDomains := totalLimit/singleLimit; -- integer division (truncate) 
    remainder := totalLimit%singleLimit; 

    IF remainder <> 0 THEN 
     nrOfDomains := nrOfDomains + 1; 
    END IF; 

    i := 1; 
    FOR dom IN SELECT DISTINCT domain FROM website LIMIT nrOfDomains 
    LOOP 
     IF i = nrOfDomains AND remainder <> 0 THEN 
      lim := remainder; 
     ELSE 
      lim := singleLimit; 
     END IF; 

     RETURN QUERY SELECT * FROM website WHERE domain = dom LIMIT lim; 

     i := i + 1; 
    END LOOP; 
    RETURN; 
END $$ 
LANGUAGE 'plpgsql'; 

Aquí es un poco de los Ensayos conducir:

postgres=> CREATE TABLE website(url text, domain text); 
CREATE TABLE 

postgres=> INSERT INTO website 
    SELECT 'http://' || d.column1 ||'/' || n, d.column1 
    FROM generate_series(1, 100) n CROSS JOIN 
    (VALUES ('google'), ('facebook'), ('ebay')) d; 
INSERT 0 300 

postgres=> SELECT * FROM getURLs(10, 25) website(url text, domain text); 

Resultado:

 url   | domain 
--------------------+---------- 
http://google/1 | google 
http://google/2 | google 
http://google/3 | google 
http://google/4 | google 
http://google/5 | google 
http://google/6 | google 
http://google/7 | google 
http://google/8 | google 
http://google/9 | google 
http://google/10 | google 
http://facebook/1 | facebook 
http://facebook/2 | facebook 
http://facebook/3 | facebook 
http://facebook/4 | facebook 
http://facebook/5 | facebook 
http://facebook/6 | facebook 
http://facebook/7 | facebook 
http://facebook/8 | facebook 
http://facebook/9 | facebook 
http://facebook/10 | facebook 
http://ebay/1  | ebay 
http://ebay/2  | ebay 
http://ebay/3  | ebay 
http://ebay/4  | ebay 
http://ebay/5  | ebay 
(25 rows) 
+1

Entiendo por qué escribió 'IF 500% 30 <> 0' pero me hizo reír de todos modos. :) –

+1

+1. Vale la pena señalar que si desea que los dominios se ordenen aleatoriamente, puede usar 'FOR d IN SELECT DISTINCT domain FROM website LIMIT nrOfDomains ORDER BY RAND()', o si desea que se ordenen de alguna otra manera, ese es el lugar para hacerlo. –

+0

@Chris Cunningham: Sí, cambié ese '500% 30' para que sea más general' totalLimit% singleLimit'. –

Cuestiones relacionadas