Question

Web page should show one product image for specific product category from PostgreSql database. This image should changed automatically to other image after every 25 seconds. Returned product may be random or in some sequence. Some product may be missing and some repeated but most of the products in criteria should returned. Total available image count may change slightly between sample retrieval

Currently code below is used which is executed after every 25 seconds. This requires two queries to database: one for count which may be slwo and second for single image retrieval. In both cases where clauses are duplicated, in real application where clause is very big and changing it requires changes in two places.

How to improve this so that single query returns sample ? Column types cannot changed, natural primary keys are used. Additional columns, triggers, indexes, sequences can added if this helps.

ASP.NET/Mono MVC3 , npgsql are used.

$count = select count(*)
         from products
         where prodtype=$sometype and productid in (select productid from images);

$random = next random integer between 0 .. $count-1;

--  $productsample  is result: desired sample product
$productsample = select product
          from products
          where prodtype=$sometype and productid in (select productid from images)
          offset $random
          limit 1;


create table products ( productid char(20) primary key,
   prodtype char(10) references producttype 
);

create table images(
id serial primary key, 
productid char(20) references products,
mainimage bool
);
Was it helpful?

Solution

An order by will always be expensive specially if the expression in the order by is not indexed. So don't order. In instead do a random offset in the count() as in your queries, but do it all at once.

with t as (
    select *
    from
        products p
        inner join
        images i using (productid)
    where
        prodtype = $sometype
)
select *
from t
offset floor(random() * (select count(*) from t))
limit 1

This version might be faster

with t as (
    select *, count(*) over() total
    from
        products p
        inner join
        images i using (productid)
    where
        prodtype = $sometype
)
select *
from t
offset floor(random() * (select total from t limit 1))
limit 1

OTHER TIPS

PosgreSQL:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

This gives you one, random row. You can of course add back in your WHERE filter to make sure it is the right category.

This removes your requirement to do a count first; and also has the advantage of letting the database engine do the selection, reducing round trips.

Note: For people looking at ways to do this in other SQL engines: http://www.petefreitag.com/item/466.cfm

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top