postgresql procedure for fetching top 10%,20% and 30% values of the total values - postgresql

i have a table named Scoreboard which contains a field named as score which is an array containing values 27,56,78,12,89,77,34,23,90,87,33,55,30,67,76,87,56and i want to write a PostgreSQL procedure to fetch three categories
category 1 = top 10% values of the total no of values in array
category 2 = top 20% values of the total no of values in array
category 3 = top 30% values of the total no of values in array
and put it in an array in the same format i.e
[category 1 values,category 2 values,category 3 values]

smth like this should do:
t=# with p as (
with ntile as (
with v as (
select unnest('{27,56,78,12,89,77,34,23,90,87,33,55,30,67,76,87,56}'::int[]) a
)
select a,ntile(10) over(order by a desc)
from v
)
select distinct string_agg(a::text,',') over (partition by ntile),ntile
from ntile
where ntile <=3 order by ntile
)
select array_agg(concat('category ',ntile,' ',string_agg))
from p;
array_agg
------------------------------------------------------------
{"category 1 90,89","category 2 87,87","category 3 78,77"}
(1 row)
Time: 0.493 ms

I am assuming , you have a table with one column as id and another one is an array type. Based on assumption
I have created table as below and inserted two values to it.
create table test_array (id int, values int[]);
insert into test_array values(1 ,'{27,56,78,12,89,77,34,23,90,87,33,55,30,67,76,87,56}' );
insert into test_array values(2 ,'{72,65,84,21,98,77,43,32,9,78,41,66,3,76,67,88,56}' );
Below is function which is used to find category as mentioned by you. If you do not have any id column in your table
then you can add number by using window function hint: row_number().
create or replace function find_category() returns table(category text[]) as
$$
BEGIN
return query with unnestColumn as (
select id, unnest(values) as values, ntile(10) over(partition by id order by unnest(values) desc) as ntilenumber
from test_array
) ,groupedCategory as ( select id, ntilenumber, string_agg(values::text,',') as combinedvalues from unnestColumn
where
ntilenumber <= 3
group by id, ntilenumber )
select array_agg(concat('Categoty',ntilenumber, ' ', combinedvalues ))
from groupedCategory
group by id;
END;
$$
language 'plpgsql';
Execute below function to check output.
select * from find_category();

Related

Postgres unnest with points

I'm trying to write a PL/PGSQL function that takes delimited text as input and inserts rows into a table that includes points. I've got a test function that works OK:
rowsaz := string_to_array(input,'?');
INSERT INTO test (num1, num2, my_name)
SELECT * FROM unnest(string_to_array(rowsaz[1],',')::integer[],string_to_array(rowsaz[2],',')::integer[],string_to_array(rowsaz[3],',')::varchar[]);
return true;
So if you call the function with
SELECT myfunction('1,2,3?4,5,6?a,b,c')
Then you get a table like
1 4 a
2 5 b
3 6 c
But how do you do this when you have a table like
CREATE TABLE public.gps_points
(
id integer NOT NULL DEFAULT nextval('gps_id_seq'::regclass),
location geometry(Point,4326),
created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
user_id integer
)
An insert would look like
INSERT INTO gps_points (location, user_id) VALUES (ST_GeomFromText('POINT(-71.060316 48.432044)', 4326),2);
But this gets tricky with the unnest because you have to pass the coordinates into the ST_POINT function. Then I would call the function with:
SELECT myfunction('36.98,36.99,36.97?45.85,45.86,45.87?1,2,3')
I'm trying to do the following, but it won't work
insert into gps_points( geom, user_id)
select unnest( (ST_GeomFromText('POINT(string_to_array(rowsaz[1],',')::double precision[] string_to_array(rowsaz[2],',')::double precision[])', 4326),string_to_array(rowsaz[3],',')::double precision[]));
Somebody helped me get it:
insert into gps_points( geom, user_id)
select ST_SetSRID(ST_MakePoint(lng, lat), 4326), uid from unnest(string_to_array(rowsaz[1],',')::double precision[],string_to_array(rowsaz[2],',')::double precision[],string_to_array(rowsaz[3],',')::integer[]) as u(lng,lat,uid);

How to repeat a query in postgreSQL, and add the results to a table

I need to run a query 1000 times, and add the results to a table. I have the following code which is what I would like to be repeated:
Select max_gridco, count(max_gridco) as TaxLots, sum(population) as pop_sum
from
(
Select tlid, max_gridco, population
from (
select tlid, max_gridco, population, st_intersects(tle.geom, acres025.geom)
from tle, acres025
where max_gridco not in (0, 1)
order by RANDOM()
limit 1000
) as count
where st_intersects = 't'
order by max_gridco
) as gridcode_count
group by max_gridco;
Is there a way that I can run this 1000 times automatically, and in the output table have a column that includes the run number? So my table would look like the following:
Run number | max_gridco | TaxLots | pop_sum
I am trying to do a Loop command in PgAdmin3, but cannot seem to get the syntax correct.
use DO block or create a function, eg:
DO
$$
begin
for i in 1..1000 loop
insert into save_to_table (Run number,max_gridco,TaxLots,pop_sum)
Select i, max_gridco, count(max_gridco) as TaxLots, sum(population) as pop_sum
from
(
Select tlid, max_gridco, population
from (
select tlid, max_gridco, population, st_intersects(tle.geom, acres025.geom)
from tle, acres025
where max_gridco not in (0, 1)
order by RANDOM()
limit 1000
) as count
where st_intersects = 't'
order by max_gridco
) as gridcode_count
group by max_gridco;
end loop;
end;
$$
;
If you need to insert values from a specific select you can do it by using:
INSERT INTO table2 (column1, column2, column3, ...)
SELECT column1, column2, column3, ...
FROM table1
WHERE condition;
(https://www.w3schools.com/sql/sql_insert_into_select.asp)
Is that what you're looking for?

PostgreSql: how to update table from array?

How to update table fields from array to avoid temp table using?
User passes array to reorder table.
create TABLE items
(
id serial primary key,
name text,
select_order int
)
insert into items(name, select_order)
values
('cucumber',0),
('milk',1),
('coffee',2),
('water',3)
select * from items order by select_order;
DO
$body$
DECLARE var_array int[]:='{3,0,2,1}';
BEGIN
update items ??
END;
$body$
The final result in this example should be
select * from items order by select_order;
name select_order
------------------------
water 0
cucumber 1
coffee 2
milk 3
Assuming the index in the array corresponds to the value in select_order the following query returns the new and old value:
select i.id, i.select_order, t.new_sort
from items i
join lateral unnest(array[3,0,2,1]) with ordinality as t(new_sort, idx) on t.idx - 1 = i.select_order
Now this can be used to update the target table:
update items
set select_order = r.new_sort
from (
select i.id, i.select_order, t.new_sort
from items i
join lateral unnest(array[3,0,2,1]) with ordinality as t(new_sort, idx) on t.idx - 1 = i.select_order
) r
where r.id = items.id;
This also assumes that select_order starts a 0 and has no gaps.

PostgreSQL join to denormalize a table with generate_series

I've this table:
CREATE TABLE "mytable"
( name text, count integer );
INSERT INTO mytable VALUES ('john', 4),('mark',2),('albert',3);
and I would like "denormlize" the rows in this way:
SELECT name FROM mytable JOIN generate_series(1,4) tmp(a) ON (a<=count)
so I've a number of rows for each name equals to the count column: I've 4 rows with john, 2 with mark and 3 with albert.
But i can't use the generate_series() function if I don't know the highest count (in this case 4). There is a way to do this without knowing the MAX(count) ?
select name,
generate_series(1,count)
from mytable;
Set returning functions can be used in the select list and will do a cross join with the row retrieved from the base table.
I think this is an undocumented behaviour that might go away in the future, but I'm not sure about that (I recall some discussion regarding this on the mailing list)
SQLFiddle example
DROP TABLE ztable ;
CREATE TABLE ztable (zname varchar, zvalue INTEGER NOT NULL);
INSERT INTO ztable(zname, zvalue) VALUES( 'one', 1), ( 'two', 2 ), ( 'three', 3) , ( 'four', 4 );
WITH expand AS (
WITH RECURSIVE zzz AS (
SELECT 1::integer AS rnk , t0.zname
FROM ztable t0
UNION
SELECT 1+rr.rnk , t1.zname
FROM ztable t1
JOIN zzz rr ON rr.rnk < t1.zvalue
)
SELECT zzz.zname
FROM zzz
)
SELECT x.*
FROM expand x
;

tsql - using internal stored procedure as parameter is where clause

I'm trying to build a stored procedure that makes use of another stored procedure. Taking its result and using it as part of its where clause, from some reason I receive an error:
Invalid object name 'dbo.GetSuitableCategories'.
Here is a copy of the code:
select distinct top 6 * from
(
SELECT TOP 100 *
FROM [dbo].[products] products
where products.categoryId in
(select top 10 categories.categoryid from
[dbo].[GetSuitableCategories]
(
-- #Age
-- ,#Sex
-- ,#Event
1,
1,
1
) categories
ORDER BY NEWID()
)
--and products.Price <=#priceRange
ORDER BY NEWID()
)as d
union
select * from
(
select TOP 1 * FROM [dbo].[products] competingproducts
where competingproducts.categoryId =-2
--and competingproducts.Price <=#priceRange
ORDER BY NEWID()
) as d
and here is [dbo].[GetSuitableCategories] :
if (#gender =0)
begin
select * from categoryTable categories
where categories.gender =3
end
else
begin
select * from categoryTable categories
where categories.gender = #gender
or categories.gender =3
end
I would use an inline table valued user defined function. Or simply code it inline is no re-use is required
CREATE dbo.GetSuitableCategories
(
--parameters
)
RETURNS TABLE
AS
RETURN (
select * from categoryTable categories
where categories.gender IN (3, #gender)
)
Some points though:
I assume categoryTable has no gender = 0
Do you have 3 genders in your categoryTable? :-)
Why do pass in 3 parameters but only use 1? See below please
Does #sex map to #gender?
If you have extra processing on the 3 parameters, then you'll need a multi statement table valued functions but beware these can be slow
You can't use the results of a stored procedure directly in a select statement
You'll either have to output the results into a temp table, or make the sproc into a table valued function to do what you doing.
I think this is valid, but I'm doing this from memory
create table #tmp (blah, blah)
Insert into #tmp
exec dbo.sprocName