Douglas Henrique
Douglas Henrique

Reputation: 329

PostgreSQL - Return result from query and distinct possible values

Say I have a table like this:

CREATE TABLE item (
    id text NOT NULL,
    price integer NOT NULL,
    colors text[] DEFAULT ARRAY[]::text[],
    sizes text[] DEFAULT ARRAY[]::text[]
);

And data like this:

INSERT INTO item VALUES ('1', 100, array['red', 'blue', 'green'], array['s', 'l']);
INSERT INTO item VALUES ('2', 5000, array['yellow', 'green'], array['s']);
INSERT INTO item VALUES ('3', 300, array['red'], array['s', 'l', 'xl']);
INSERT INTO item VALUES ('4', 150, array['white'], array['xxl']);

Now suppose I have this query:

SELECT * from item WHERE price > 50 AND price < 400 AND colors&&ARRAY['red'] AND sizes&&ARRAY['s', 'l', 'xxl'] ORDER BY price OFFSET 0 LIMIT 10;

The result set is

 | id | price | colors             | sizes
1| 1  | 100   | {red, blue, green} | {s, l}
1| 3  | 300   | {red}              | {s, l, xl}

My question is: How can I change the query to return not only this result set, but also all possible sizes of this query (excluding any size condition, i.e. excluding sizes&&ARRAY['s', 'l', 'xxl']), all possible colors of this query (excluding only color conditions, i.e. excluding colors&&ARRAY['red']) and the price range ignoring only the price conditions in the query (i.e. excluding price > 50 AND price < 400)? Something like:

 | id | price | colors             | sizes      | p_sizes    | p_colors                          | min_price | max_price |
1| 1  | 100   | {red, blue, green} | {s, l}     | {s, l, xl} | {'red', 'blue', 'green', 'white'} | 100       | 300       |
1| 3  | 300   | {red}              | {s, l, xl} | {s, l, xl} | {'red', 'blue', 'green', 'white'} | 100       | 300       |

Where p_colors is the list of distinct colors for the query:

SELECT * from item WHERE price > 50 AND price < 400 AND sizes&&ARRAY['s', 'l', 'xxl'] ORDER BY price OFFSET 0 LIMIT 10;

 | id | price | colors             | sizes
1| 1  | 100   | {red, blue, green} | {s, l}
1| 4  | 150   | {white}            | {xxl}
1| 3  | 300   | {red}              | {s, l, xl}

p_sizes is the list of distinct sizes for the query:

SELECT * from item WHERE price > 50 AND price < 400 AND colors&&ARRAY['red'] ORDER BY price OFFSET 0 LIMIT 10;

 | id | price | colors             | sizes
1| 1  | 100   | {red, blue, green} | {s, l}
1| 3  | 300   | {red}              | {s, l, xl}

And min_price and max_price are the price range for the query:

SELECT * from item WHERE colors&&ARRAY['red'] AND sizes&&ARRAY['s', 'l', 'xxl'] ORDER BY price OFFSET 0 LIMIT 10;
 | id | price | colors             | sizes
1| 1  | 100   | {red, blue, green} | {s, l}
1| 3  | 300   | {red}              | {s, l, xl}

----- Edit -----

Added a size condition and changed the price of the item with id 2 to 5000 to clarify the problem.

Upvotes: 1

Views: 55

Answers (1)

klin
klin

Reputation: 121604

This query selects expected values:

select 
    array(select distinct unnest(colors) from item) all_colors,
    array(select distinct unnest(sizes) from item) all_sizes,
    min(price) min_price,
    max(price) max_price
from item;

          all_colors           |  all_sizes   | min_price | max_price 
-------------------------------+--------------+-----------+-----------
 {yellow,blue,white,green,red} | {xl,s,l,xxl} |       100 |       300
(1 row)

You can join the result of the query with your original query in lateral join:

select * 
from item,
lateral (
    select 
        array(select distinct unnest(colors) from item) all_colors,
        array(select distinct unnest(sizes) from item) all_sizes,
        min(price) min_price,
        max(price) max_price
    from item
) sub
where price > 50 and price < 400 AND colors&&ARRAY['red'] order by price;

 id | price |      colors      |  sizes   |          all_colors           |  all_sizes   | min_price | max_price 
----+-------+------------------+----------+-------------------------------+--------------+-----------+-----------
 1  |   100 | {red,blue,green} | {s,l}    | {yellow,blue,white,green,red} | {xl,s,l,xxl} |       100 |       300
 3  |   300 | {red}            | {s,l,xl} | {yellow,blue,white,green,red} | {xl,s,l,xxl} |       100 |       300
(2 rows)    

You can split the aggregate query to three parts to have different where clauses, e.g.:

select * 
from item,
lateral (
    select array(
        select distinct unnest(colors) 
        from item 
        where price > 50 and price < 400) all_colors) q1,
lateral (
    select array(
        select distinct unnest(sizes) 
        from item
        where price > 50 and price < 400 and colors && ARRAY['red']) all_sizes) q2,
lateral (
    select
        min(price) min_price,
        max(price) max_price
    from item
    where colors&&ARRAY['red']
) q3
where price > 50 and price < 400 and colors&&ARRAY['red'] order by price;

 id | price |      colors      |  sizes   |          all_colors           | all_sizes | min_price | max_price 
----+-------+------------------+----------+-------------------------------+-----------+-----------+-----------
 1  |   100 | {red,blue,green} | {s,l}    | {yellow,white,green,red,blue} | {l,s,xl}  |       100 |       300
 3  |   300 | {red}            | {s,l,xl} | {yellow,white,green,red,blue} | {l,s,xl}  |       100 |       300
(2 rows)    

Upvotes: 2

Related Questions