I have problems with this function and couldn't figure out how to fix it.
Create Function Quy(sdate timestamp)
returns integer as $$
declare
numbmonth integer;
quy integer;
Begin
numbmonth := Date_part('month',sdate);
If numbmonth < 4 then
quy := 1;
else if numbmonth < 7 then
quy := 2;
else if numbmonth < 10 then
quy := 3;
else quy := 4;
return quy;
END;
$$
LANGUAGE plpgsql;
This happens when I try to run the code:
ERROR: syntax error at or near ";"
LINE 16: END;
I really don't understand what is wrong with this.
Multiple syntax errors. The function would work like this:
CREATE OR REPLACE FUNCTION quy(sdate timestamp)
RETURNS integer AS
$func$
DECLARE
numbmonth integer := date_part('month', sdate);
quy integer;
BEGIN
IF numbmonth < 4 THEN
quy := 1;
ELSIF numbmonth < 7 THEN
quy := 2;
ELSIF numbmonth < 10 THEN
quy := 3;
ELSE
quy := 4;
END IF;
RETURN quy;
END
$func$ LANGUAGE plpgsql;
Consult the manual for the basic syntax of IF.
But that's much ado about nothing. To get the quarter of the year use the field specifier QUARTER with date_part() or EXTRACT() in a simple expression:
EXTRACT(QUARTER FROM $timestamp)
EXTRACT is the standard SQL equivalent of date_part().
Either returns double precision, so cast to integer if you need that (::int).
If you still need a function:
CREATE OR REPLACE FUNCTION quy(sdate timestamp)
RETURNS int LANGUAGE sql IMMUTABLE AS
'SELECT EXTRACT(QUARTER FROM $1)::int';
$1 is the reference to the 1st function parameter. Equivalent to sdate in the example. $-notation works in any version of Postgres, while named parameter references in SQL functions were only introduced with Postgres 9.2. See:
PLPGSQL Function to Calculate Bearing
dbfiddle here
I am using an Amazon RDS Postgres database (9.4.4), and I would like to calculate the weighted mean of some data.
I have found the following extension which looks perfect for the job.
https://github.com/Kozea/weighted_mean
However I am now unsure of how to install the extension, as my initial research shows that only 'supported' extensions are allowed.
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts.General.FeatureSupport
What options do i have for using this extension. I don't want to have to re-invent the wheel, and I am not familiar with installing any kind of function/extension within Postgres.
Thanks
Why you just don't issue a simple query like this:
select
case when sum(quantity) = 0 then
0
else
sum(unitprice * quantity) / sum(quantity)
end
from sales;
of course you need to change your fields (unitprice, quantity)
The SUM(value*weight)/SUM(weight) answer is fine, but if you need to reuse it constantly it is more convenient to create an aggregate function.
CREATE FUNCTION avg_weighted_step(state numeric[], value numeric, weight numeric)
RETURNS numeric[]
LANGUAGE plpgsql
AS $$
BEGIN
RETURN array[state[1] + value*weight, state[2] + weight];
END;
$$ IMMUTABLE;
CREATE FUNCTION avg_weighted_finalizer(state numeric[])
RETURNS numeric
LANGUAGE plpgsql
AS $$
BEGIN
IF state[2] = 0 THEN
RETURN null;
END IF;
RETURN state[1]/state[2];
END;
$$ IMMUTABLE;
CREATE AGGREGATE avg_weighted(value numeric, weight numeric) (
sfunc = avg_weighted_step,
stype = numeric[],
finalfunc = avg_weighted_finalizer,
initcond = '{0,0}' );
Example Usage:
$ table tmp;
v w
-- -
1 2
10 1
(2 rows)
$ select avg_weighted(v, w) from tmp;
avg_weighted
------------------
4.0000000000000000
(1 row)
$ select avg_weighted(v, w) from tmp where v is null;
avg_weighted
------------
∅
(1 row)
In order to register new custom extension you need to add appropriate scripts to contrib directory of Postgres installation. AWS does NOT allow you to have such a granular control.
Long story short, there is no way you can add any custom extensions (beside the ones specified in the link you provided or from pg_available_extensions view) to Amazon RDS service.
This is one of the drawbacks of using DBaaS (database as a service) solutions.
Is it possible to get the maximum value for BIGINT type without hardcoding it?
I know that the limit is well known, however I wouldn't like to hardcode it.
You can use pg_column_size, it'll give you bytes size of bigint. Using it you can get its max and min sizes:
select (2^(8*pg_column_size(1::bigint)-2))::bigint << 1 as min_bigint_value;
select -(((2^(8*pg_column_size(1::bigint)-2))::bigint << 1)+1) as max_bigint_value;
So if somehow in the future bigint max value will change (very very very unlikely) your code will still works if you will rely on these calculated values.
You can write functions for convenience:
CREATE OR REPLACE FUNCTION "get_max_bigint_value"() RETURNS BIGINT as $$
DECLARE
max_bigint_value BIGINT;
BEGIN
SELECT -(((2^(8*pg_column_size(1::BIGINT)-2))::BIGINT << 1)+1) INTO max_bigint_value;
RETURN max_bigint_value;
END
$$ LANGUAGE "plpgsql";
CREATE OR REPLACE FUNCTION "get_min_bigint_value"() RETURNS BIGINT as $$
DECLARE
min_bigint_value BIGINT;
BEGIN
SELECT (2^(8*pg_column_size(1::bigint)-2))::bigint << 1 INTO min_bigint_value;
RETURN min_bigint_value;
END
$$ LANGUAGE "plpgsql";
and then:
SELECT get_min_bigint_value();
SELECT get_max_bigint_value();
The "ideal" solution would be to create some C functions that just expose the relevant defines (below). But assuming that you'd rather have something in SQL, I think the best you'll be able to do is:
CREATE OR REPLACE FUNCTION bigint_min() RETURNS bigint LANGUAGE sql AS 'SELECT 1::bigint<<63';
CREATE OR REPLACE FUNCTION bigint_max() RETURNS bigint LANGUAGE sql AS 'SELECT ~bigint_min()';
CREATE OR REPLACE FUNCTION int_min() RETURNS int LANGUAGE sql AS 'SELECT 1<<31';
CREATE OR REPLACE FUNCTION int_max() RETURNS int LANGUAGE sql AS 'SELECT ~int_min()';
These are what's defined at the C level. bigint uses PG_INT64_*:
#define PG_INT8_MIN (-0x7F-1)
#define PG_INT8_MAX (0x7F)
#define PG_UINT8_MAX (0xFF)
#define PG_INT16_MIN (-0x7FFF-1)
#define PG_INT16_MAX (0x7FFF)
#define PG_UINT16_MAX (0xFFFF)
#define PG_INT32_MIN (-0x7FFFFFFF-1)
#define PG_INT32_MAX (0x7FFFFFFF)
#define PG_UINT32_MAX (0xFFFFFFFFU)
#define PG_INT64_MIN (-INT64CONST(0x7FFFFFFFFFFFFFFF) - 1)
#define PG_INT64_MAX INT64CONST(0x7FFFFFFFFFFFFFFF)
#define PG_UINT64_MAX UINT64CONST(0xFFFFFFFFFFFFFFFF)
Just for fun: Building on alexpods' answer, we could define a "generic" function (assuming two's complement) for all postgres integers:
create or replace function
minint(a anyelement)
returns anyelement
immutable
language sql
as $$
select ((a - a) - 1) << (8 * pg_column_size(a) - 1);
$$
;
create or replace
function maxint(a anyelement)
returns anyelement
immutable
language sql
as $$
select ~minint(a)
$$
;
Usage:
select
minint(0::smallint)
, maxint(0::smallint)
, minint(0::int)
, maxint(0::int)
, minint(0::bigint)
, maxint(0::bigint)
;
Result:
minint | maxint | minint | maxint | minint | maxint
--------+--------+-------------+------------+----------------------+---------------------
-32768 | 32767 | -2147483648 | 2147483647 | -9223372036854775808 | 9223372036854775807
(1 row)
You can reach it in ~100ms and use in code or save to temporary table - here's example of more or less fast way to get the max value to raise it:
do
$$
declare
i bigint =2;
r record;
begin
begin
for r in 2::int..999::int loop
i=power(2,r);
raise info '%', i::text;
end loop;
exception when others then raise info '%', 'fast roll on (power to 2) ended - switching to slow (multiplying by 2)'||i::text;
end;
begin
for r in 2::int..999::int loop
i=i*2;
raise info '%', i::text;
end loop;
exception when others then raise info '%', 'max reached:'||(1+(i-1)*2)::text;
end;
end;
$$
;
Let's say I've created a composite type in Postgresql:
CREATE TYPE custom_type AS
(x integer
y integer);
I need to use it in a function as an array:
...
DECLARE
customVar custom_type[];
BEGIN
....
My question is: how do I access custom_type's specific components?
For example, I want to (re)assign 'x' for the third element in custom_type array...
postgres=> create type pt as (x int, y int);
CREATE TYPE
postgres=> create or replace function fx()
returns void as $$
declare a pt[] = ARRAY[(10,20),(30,40)]; declare xx pt;
begin
for i in array_lower(a, 1) .. array_upper(a,1)
loop
xx = a[i]; xx.x := xx.x + 1; a[i] := xx; raise notice '%', a[i].x;
end loop;
end;
$$ language plpgsql;
CREATE FUNCTION
postgres=> select fx();
NOTICE: 11
NOTICE: 31
fx
────
(1 row)
Significant limit for target of assign statement is possibility to refer only one level nested properties. This limit can be bypassed by auxiliary variables - it is not too friendly - and internal implementation is too simple, but it is fast and enough for typical stored procedure usage although it is not strong in comparison with generic programming languages.
Given:
SELECT ARRAY[(1,2),(3,4)]::custom_type[];
Use an array subscript and then refer to the field by name.
regress=> SELECT (ARRAY[(1,2),(3,4)]::custom_type[])[1].x;
x
---
1
(1 row)
I have a PostgreSQL table of this form:
base_id int | mods smallint[]
3 | {7,15,48}
I need to populate a table of this form:
combo_id int | base_id int | mods smallint[]
1 | 3 |
2 | 3 | {7}
3 | 3 | {7,15}
4 | 3 | {7,48}
5 | 3 | {7,15,48}
6 | 3 | {15}
7 | 3 | {15,48}
8 | 3 | {48}
I think I could accomplish this using a function that does almost exactly this, iterating over the first table and writing combinations to the second table:
Generate all combinations in SQL
But, I'm a Postgres novice and cannot for the life of me figure out how to do this using plpgsql. It doesn't need to be particularly fast; it will only be run periodically on the backend. The first table has approximately 80 records and a rough calculation suggests we can expect around 2600 records for the second table.
Can anybody at least point me in the right direction?
Edit: Craig: I've got PostgreSQL 9.0. I was successfully able to use UNNEST():
FOR messvar IN SELECT * FROM UNNEST(mods) AS mod WHERE mod BETWEEN 0 AND POWER(2, #n) - 1
LOOP
RAISE NOTICE '%', messvar;
END LOOP;
but then didn't know where to go next.
Edit: For reference, I ended up using Erwin's solution, with a single line added to add a null result ('{}') to each set and the special case Erwin refers to removed:
CREATE OR REPLACE FUNCTION f_combos(_arr integer[], _a integer[] DEFAULT '{}'::integer[], _z integer[] DEFAULT '{}'::integer[])
RETURNS SETOF integer[] LANGUAGE plpgsql AS
$BODY$
DECLARE
i int;
j int;
_up int;
BEGIN
IF array_length(_arr,1) > 0 THEN
_up := array_upper(_arr, 1);
IF _a = '{}' AND _z = '{}' THEN RETURN QUERY SELECT '{}'::int[]; END IF;
FOR i IN array_lower(_arr, 1) .. _up LOOP
FOR j IN i .. _up LOOP
CASE j-i
WHEN 0,1 THEN
RETURN NEXT _a || _arr[i:j] || _z;
ELSE
RETURN NEXT _a || _arr[i:i] || _arr[j:j] || _z;
RETURN QUERY SELECT *
FROM f_combos(_arr[i+1:j-1], _a || _arr[i], _arr[j] || _z);
END CASE;
END LOOP;
END LOOP;
ELSE
RETURN NEXT _arr;
END IF;
END;
$BODY$
Then, I used that function to populate my table:
INSERT INTO e_ecosystem_modified (ide_ecosystem, modifiers)
(SELECT ide_ecosystem, f_combos(modifiers) AS modifiers FROM e_ecosystem WHERE ecosystemgroup <> 'modifier' ORDER BY ide_ecosystem, modifiers);
From 79 rows in my source table with a maximum of 7 items in the modifiers array, the query took 250ms to populate 2630 rows in my output table. Fantastic.
After I slept over it I had a completely new, simpler, faster idea:
CREATE OR REPLACE FUNCTION f_combos(_arr anyarray)
RETURNS TABLE (combo anyarray) LANGUAGE plpgsql AS
$BODY$
BEGIN
IF array_upper(_arr, 1) IS NULL THEN
combo := _arr; RETURN NEXT; RETURN;
END IF;
CASE array_upper(_arr, 1)
-- WHEN 0 THEN -- does not exist
WHEN 1 THEN
RETURN QUERY VALUES ('{}'), (_arr);
WHEN 2 THEN
RETURN QUERY VALUES ('{}'), (_arr[1:1]), (_arr), (_arr[2:2]);
ELSE
RETURN QUERY
WITH x AS (
SELECT f.combo FROM f_combos(_arr[1:array_upper(_arr, 1)-1]) f
)
SELECT x.combo FROM x
UNION ALL
SELECT x.combo || _arr[array_upper(_arr, 1)] FROM x;
END CASE;
END
$BODY$;
Call:
SELECT * FROM f_combos('{1,2,3,4,5,6,7,8,9}'::int[]) ORDER BY 1;
512 rows, total runtime: 2.899 ms
Explain
Treat special cases with NULL and empty array.
Build combinations for a primitive array of two.
Any longer array is broken down into:
the combinations for same array of length n-1
plus all of those combined with element n .. recursively.
Really simple, once you got it.
Works for 1-dimensional integer arrays starting with subscript 1 (see below).
2-3 times as fast as old solution, scales better.
Works for any element type again (using polymorphic types).
Includes the empty array in the result as is displayed in the question (and as #Craig pointed out to me in the comments).
Shorter, more elegant.
This assumes array subscripts starting at 1 (Default). If you are not sure about your values, call the function like this to normalize:
SELECT * FROM f_combos(_arr[array_lower(_arr, 1):array_upper(_arr, 1)]);
Not sure if there is a more elegant way to normalize array subscripts. I posted a question about that:
Normalize array subscripts for 1-dimensional array so they start with 1
Old solution (slower)
CREATE OR REPLACE FUNCTION f_combos2(_arr int[], _a int[] = '{}', _z int[] = '{}')
RETURNS SETOF int[] LANGUAGE plpgsql AS
$BODY$
DECLARE
i int;
j int;
_up int;
BEGIN
IF array_length(_arr,1) > 0 THEN
_up := array_upper(_arr, 1);
FOR i IN array_lower(_arr, 1) .. _up LOOP
FOR j IN i .. _up LOOP
CASE j-i
WHEN 0,1 THEN
RETURN NEXT _a || _arr[i:j] || _z;
WHEN 2 THEN
RETURN NEXT _a || _arr[i:i] || _arr[j:j] || _z;
RETURN NEXT _a || _arr[i:j] || _z;
ELSE
RETURN NEXT _a || _arr[i:i] || _arr[j:j] || _z;
RETURN QUERY SELECT *
FROM f_combos2(_arr[i+1:j-1], _a || _arr[i], _arr[j] || _z);
END CASE;
END LOOP;
END LOOP;
ELSE
RETURN NEXT _arr;
END IF;
END;
$BODY$;
Call:
SELECT * FROM f_combos2('{7,15,48}'::int[]) ORDER BY 1;
Works for 1-dimensional integer arrays.
This could be further optimized, but that's certainly not needed for the scope of this question.
ORDER BY to impose the order displayed in the question.
Provide for NULL or empty array, as NULL is mentioned in the comments.
Tested with PostgreSQL 9.1, but should work with any halfway modern version.
array_lower() and array_upper() have been around for at least since PostgreSQL 7.4. Only parameter defaults are new in version 8.4. Could easily be replaced.
Performance is decent.
SELECT DISTINCT * FROM f_combos('{1,2,3,4,5,6,7,8,9}'::int[]) ORDER BY 1;
511 rows, total runtime: 7.729 ms
Explanation
It builds on this simple form that only creates all combinations of neighboring elements:
CREATE FUNCTION f_combos(_arr int[])
RETURNS SETOF int[] LANGUAGE plpgsql AS
$BODY$
DECLARE
i int;
j int;
_up int;
BEGIN
_up := array_upper(_arr, 1);
FOR i in array_lower(_arr, 1) .. _up LOOP
FOR j in i .. _up LOOP
RETURN NEXT _arr[i:j];
END LOOP;
END LOOP;
END;
$BODY$;
But this will fail for sub-arrays with more than two elements. So:
For any sub-array with 3 elements one array with just the outer two elements is added. this is a shortcut for this special case that improves performance and is not strictly needed.
For any sub-array with more than 3 elements I take the outer two elements and fill in with all combinations of inner elements built by the same function recursively.
One approach is with a recursive CTE. Erwin's updated recursive function is significantly faster and scales better, though, so this is really useful as an interesting different approach. Erwin's updated version is much more practical.
I tried a bit counting approach (see the end) but without a fast way to pluck arbitrary elements from an array it proved slower then either recursive approach.
Recursive CTE combinations function
CREATE OR REPLACE FUNCTION combinations(anyarray) RETURNS SETOF anyarray AS $$
WITH RECURSIVE
items AS (
SELECT row_number() OVER (ORDER BY item) AS rownum, item
FROM (SELECT unnest($1) AS item) unnested
),
q AS (
SELECT 1 AS i, $1[1:0] arr
UNION ALL
SELECT (i+1), CASE x
WHEN 1 THEN array_append(q.arr,(SELECT item FROM items WHERE rownum = i))
ELSE q.arr END
FROM generate_series(0,1) x CROSS JOIN q WHERE i <= array_upper($1,1)
)
SELECT q.arr AS mods
FROM q WHERE i = array_upper($1,1)+1;
$$ LANGUAGE 'sql';
It's a polymorphic function, so it'll work on arrays of any type.
The logic is to iterate over each item in the unnested input set, using a working table. Start with an empty array in the working table, with a generation number of 1. For each entry in the input set insert two new arrays into the working table with an incremented generation number. One of the two is a copy of the input array from the previous generation and the other is the input array with the (generation-number)'th item from the input set appended to it. When the generation number exceeds the number of items in the input set, return the last generation.
Usage
You can use the combinations(smallint[]) function to produce the results you desire, using it as a set-returning function in combinatin with the row_number window function.
-- assuming table structure
regress=# \d comb
Table "public.comb"
Column | Type | Modifiers
---------+------------+-----------
base_id | integer |
mods | smallint[] |
SELECT base_id, row_number() OVER (ORDER BY mod) AS mod_id, mod
FROM (SELECT base_id, combinations(mods) AS mod FROM comb WHERE base_id = 3) x
ORDER BY mod;
Results
regress=# SELECT base_id, row_number() OVER (ORDER BY mod) AS mod_id, mod
regress-# FROM (SELECT base_id, combinations(mods) AS mod FROM comb WHERE base_id = 3) x
regress-# ORDER BY mod;
base_id | mod_id | mod
---------+--------+-----------
3 | 1 | {}
3 | 2 | {7}
3 | 3 | {7,15}
3 | 4 | {7,15,48}
3 | 5 | {7,48}
3 | 6 | {15}
3 | 7 | {15,48}
3 | 8 | {48}
(8 rows)
Time: 2.121 ms
Zero element arrays produce a null result. If you want combinations({}) to return one row {} then a UNION ALL with {} will do the job.
Theory
It appears you want the k-combinations for all k in a k-multicombination, rather than simple combinations. See number of combinations with repetition.
In other words, you want all k-combinations of elements from your set, for all k from 0 to n where n is the set size.
Related SO question: SQL - Find all possible combination, which has the really interesting answer about bit counting.
Bit operations exist in Pg, so a bit counting approach should be possible. You'd expect it to be more efficient, but because it's so slow to select a scattered subset of elements from an array it actually works out slower.
CREATE OR REPLACE FUNCTION bitwise_subarray(arr anyarray, elements integer)
RETURNS anyarray AS $$
SELECT array_agg($1[n+1])
FROM generate_series(0,array_upper($1,1)-1) n WHERE ($2>>n) & 1 = 1;
$$ LANGUAGE sql;
COMMENT ON FUNCTION bitwise_subarray(anyarray,integer) IS 'Return the elements from $1 where the corresponding bit in $2 is set';
CREATE OR REPLACE FUNCTION comb_bits(anyarray) RETURNS SETOF anyarray AS $$
SELECT bitwise_subarray($1, x)
FROM generate_series(0,pow(2,array_upper($1,1))::integer-1) x;
$$ LANGUAGE 'sql';
If you could find a faster way to write bitwise_subarray then comb_bits would be very fast. Like, say, a small C extension function, but I'm only crazy enough to write one of those for an SO answer.