Reputation: 180
I need to create a SINGLE function that looks two different tables (created from joining other tables) BRANDS_TYPES and PRODUCTS_LAUNCH. They have the following Structure.
| id | brand_name | type |
| 1 | porsche | sports |
| 1 | porsche | suvs |
| id | brand_name | prod | prod_date | sales | group | prod_remain |
| 1 | porsche | carrera | 1991 | 500000 | 1 | 5 |
I need to return a table that contains the brands in the top x sales for a chosen type of a single group. Brands must have at least 1 product launch since a chosen date. Returning table should sum sales grouped by brand and ordered by sales AND the brands with least remaining products. I cannot figure out how to join these various queries into a single function.
I've managed to split the "procedure" into three different parts, as they are not overly complicated for a beginner (me). The first function gets all the brands that have a specific type. The second looks at PRODUCTS_LAUNCH and returns brands that have launched at least 1 product after a specific date and belong to a certain group. The third function looks at PRODUCTS_LAUNCH and returns a table with a ranked order of brands with the least amount of remaining products and most sales.
CREATE OR REPLACE FUNCTION get_type (type_choice VARCHAR)
RETURNS TABLE (
g_type_name brands_types.type%TYPE,
g_brand_id brands_types.brand_id%TYPE,
g_brand_name brands_types.brand_name%TYPE)
AS $$
BEGIN
RETURN QUERY SELECT
type_name,
brand_id,
brand_name
FROM brands_types WHERE type ILIKE type_choice;
END; $$
CREATE OR REPLACE FUNCTION get_group_date (cutoff_date DATE, group_choice INT)
RETURNS TABLE (
r_brand_name products_launch.brand_name%TYPE,
r_date_l products_launch.prod_date%TYPE,
r_sales products_launch.sales%TYPE,
r_group products_launch.group%TYPE,
r_remaining products_launch.prod_remain%TYPE)
AS $$
DECLARE
i_row record;
BEGIN
FOR i_row IN
(SELECT
brand_name,
prod_date,
sales,
group,
prod_remain
FROM
products_launch
WHERE prod_date >= cutoff_date AND group = group_choice)
LOOP
r_brand_name := upper(i_row.brand_name);
r_date_l := i_row.prod_date;
r_sales := i_row.sales;
r_group := i_row.group;
r_remaining := i_row.prod_remain;
RETURN NEXT;
END LOOP;
END;
$$
CREATE OR REPLACE FUNCTION get_topsales_prodleft (top_t1 INT)
RETURNS TABLE (
tp_brand_name products_launch.brand_name%TYPE,
tp_remaining products_launch.prod_remain%TYPE,
tp_sales products_launch.sales%TYPE
)
AS $$
BEGIN
RETURN QUERY
SELECT
brand_name,
prod_remain,
SUM (sales) AS total_sales
FROM
products_launch
GROUP BY
brand_name,
prod_remain
ORDER BY prod_remain DESC, total_sales DESC
LIMIT top_t1;
END;
$$
Each function works as intended (i think) however i need to have a single function. How do i call one inside another and have its return be queried?
Upvotes: 0
Views: 42
Reputation: 664196
First I'd suggest to simplify a bit:
CREATE OR REPLACE FUNCTION get_type (type_choice VARCHAR)
RETURNS SETOF brands_types
LANGUAGE SQL
AS $$
SELECT *
FROM brands_types
WHERE type ILIKE type_choice;
$$
CREATE OR REPLACE FUNCTION get_group_date (cutoff_date DATE, group_choice INT)
RETURNS SETOF products_launch
LANGUAGE SQL
AS $$
SELECT
upper(brand_name) AS brand_name,
prod_date,
sales,
group,
prod_remain
FROM
products_launch
WHERE prod_date >= cutoff_date AND group = group_choice;
$$
CREATE OR REPLACE FUNCTION get_topsales_prodleft (top_t1 INT)
RETURNS SETOF products_launch
LANGUAGE SQL
AS $$
SELECT
brand_name,
prod_remain,
SUM (sales) AS total_sales
FROM
products_launch
GROUP BY
brand_name,
prod_remain
ORDER BY prod_remain DESC, total_sales DESC
LIMIT top_t1;
$$
Now to join them into a single query, all you need to do is join the type with with launches by brand name, and filter your summation by that:
SELECT *
FROM get_topsales_prodleft($1)
WHERE brand_name IN (SELECT brand_name
FROM get_type($2)
JOIN get_group_date($3, $4) USING (brand_name))
For this to work as expected, I would think that you a) will need to put the LIMIT on the outer, filtered query not inside the get_topsales_prodleft
function, and that you will need to remove the upper()
call on the brand_name
in get_group_date
, or otherwise the join wouldn't work.
Also I believe that it would have been simpler - or at least, a lot less verbose - to write the query in a single statement, instead of using any functions. It would arrive at
SELECT *
brand_name,
prod_remain,
SUM (sales) AS total_sales
FROM
products_launch
WHERE
brand_name IN (SELECT brand_name
FROM brands_types
JOIN products_launch USING (brand_name)
WHERE type ILIKE :type_choice
AND prod_date >= :cutoff_date AND group = :group_choice)
GROUP BY
brand_name,
prod_remain
ORDER BY prod_remain DESC, total_sales DESC
LIMIT :top_t1;
Upvotes: 1