function array_agg(text) is not unique when connecting with pgAdmin III - postgresql

When I connect to PostgreSQL database with pgAdmin III (1.22.2), I get this error:
ERROR: function array_agg(text) is not unique
LINE 5: (SELECT array_agg(label) FROM pg_shseclabel sl1 WHERE sl1.ob...
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
The database server is PostgreSQL 9.6.
Same instance of pgAdmin works fine with databases running PostgreSQL 9.0 versions.
How can I fix this problem?

I found that problem is caused by migration from 9.0 to 9.6.
Log in to the database using psql and run \df array_agg. This is how it should look in 9.0:
db=# \df array_agg
List of functions
Schema | Name | Result data type | Argument data types | Type
------------+-----------+------------------+---------------------+------
pg_catalog | array_agg | anyarray | anyelement | agg
(1 row)
and in 9.6:
db=# \df array_agg
List of functions
Schema | Name | Result data type | Argument data types | Type
------------+-----------+------------------+---------------------+------
pg_catalog | array_agg | anyarray | anyarray | agg
pg_catalog | array_agg | anyarray | anynonarray | agg
(2 rows)
However, when I ran \df array_agg on my server, I get:
db=> \df array_agg
Lista funkcji
Schemat | Nazwa | Typ danych wyniku | Typy danych argumentów | Typ
------------+-----------+-------------------+------------------------+-------
pg_catalog | array_agg | anyarray | anyarray | agreg
pg_catalog | array_agg | anyarray | anynonarray | agreg
public | array_agg | anyarray | anyelement | agreg
(3 rows)
As mentioned here, it can be fixed by:
DROP AGGREGATE public.array_agg(anyelement);

In my case, explicitly typecasting the argument to either TEXT or VARCHAR works.
E.g.,
SELECT array_agg('Group1');
Results in:
ERROR: function array_agg(unknown) is not unique
LINE 1: SELECT array_agg('Group1');
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
SQL state: 42725
Character: 8
However, both of
SELECT array_agg('Group1'::VARCHAR);
and
SELECT array_agg('Group1'::TEXT);
work as expected and return the respective TEXT or VARCHAR array.

Related

How to change column type in PostgreSQL from text to array and cast only non-null values?

I have a table in PostgreSQL:
| id | country| type |
| 1 | USA | FOO |
| 2 | null | BAR |
I want to change the column type for the country column from text to array and cast to the new type only non-null values to have the table look as follows:
| id | country | type |
| 1 | {USA} | FOO |
| 2 | null | BAR |
So far, I have come up with this expression that casts any value to the array. So for the 2nd row, I have an array with a null value.
ALTER TABLE my_table
ALTER COLUMN country TYPE TEXT[]
USING ARRAY[country];
How can I use the USING expression to cast only not null values?
Simply do
ALTER TABLE my_table
ALTER COLUMN country TYPE TEXT[]
USING string_to_array(country,'');
You can use a CASE expression
ALTER TABLE my_table
ALTER COLUMN country TYPE TEXT[]
USING case
when country is null then null
else ARRAY[country]
end;

Translating SUBSTRING_INDEX() in MySQL to PostgreSQL

There are a number of question related to this and the answer is to use split_part(). For example:
emulating MySQL's substring_index() in PGSQL
Mysql`s SUBSTRING_INDEX equivalent in postgresql
I'm not getting the same behavior, however. I'm trying to figure out how to get the following functionality in Postgres.
If you have a string that looks like:
+------------------------------------------+
| string |
+------------------------------------------+
| A_123, B_123, C_123, D_123, E_123, F_123 |
+------------------------------------------+
MySQL will return the following with the given statement:
mysql> select SUBSTRING_INDEX(string, ',', 4) AS test FROM tbl;
+----------------------------+
| test |
+----------------------------+
| A_123, B_123, C_123, D_123 |
+----------------------------+
PostgreSQL will return the following with the given statement:
mysql> select split_part(string, ',', 4) AS test FROM tbl;
+-------+
| test |
+-------+
| D_123 |
+-------+
Is there a similar function or just implementing a function like this?
As a_horse_with_no_name suggested in the comments, this had the desired result:
array_to_string((regexp_split_to_array(string, '\s*,\s'))[:4], ', ')

Update jsonb column in CASE statement: "column is of type jsonb but expression is of type text"

I am working in Postgres 11.4. I have a table with a jsonb column:
Table "public.feature_bundle_plan_feature"
Column | Type | Collation | Nullable | Default
-------------------+-----------------------------+-----------+----------+---------------
plan_feature_id | integer | | not null |
feature_bundle_id | integer | | not null |
value | jsonb | | | 'true'::jsonb
I can store an object directly in the jsonb value column like this:
insert into feature_bundle_plan_feature(plan_feature_id, feature_bundle_id, value)
values(1, 1, '{"foo":"bar"}');
However, I don't seem to be able to do the same thing inside a CASE statement:
update feature_bundle_plan_feature set value = case
when feature_bundle_id=1 then '{"foo":"bar"}'
end;
This fails with:
ERROR: column "value" is of type jsonb but expression is of type text
LINE 1: update feature_bundle_plan_feature set value = case
HINT: You will need to rewrite or cast the expression.
What am I doing wrong?

Select rows where column matches any IP address in inet[] array

I'm trying to display rows that have at least one value in the inet[] type column.
I really don't know any better, so it seems it would be easiest to use something like this, but it returns results with {} which I guess is null according to the inet[] type, but not from the perspective of the is not null query?
peering_manager=# select asn,name,potential_internet_exchange_peering_sessions from peering_autonomoussystem where potential_internet_exchange_peering_sessions is not null order by potential_internet_exchange_peering_sessions limit 1;
asn | name | potential_internet_exchange_peering_sessions
------+---------------------------------+----------------------------------------------
6128 | Cablevision Systems Corporation | {}
(1 row)
peering_manager=#
So trying to dig a little more into it, I think maybe if I can try to match the existence of any valid IP address in the inet[] column, that would work, however I'm getting an error and I don't understand what it's referring to or how to resolve it to achieve the desired results:
peering_manager=# select asn,name,potential_internet_exchange_peering_sessions from peering_autonomoussystem where potential_internet_exchange_peering_sessions << inet '0.0.0.0/0';
ERROR: operator does not exist: inet[] << inet
LINE 1: ...here potential_internet_exchange_peering_sessions << inet '0...
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
peering_manager=#
Maybe it's saying that the << operator is invalid for the inet[] type or that the << operator is an invalid operation when trying to query an inet type from a value stored as an inet[] type? Or something else?
In any event, I'm kind of lost. Maybe there's a better way to do this?
Here's the table, and a sample of the data set I'm working with.
peering_manager=# \d peering_autonomoussystem;
Table "public.peering_autonomoussystem"
Column | Type | Modifiers
----------------------------------------------+--------------------------+-----------------------------------------------------------------------
id | integer | not null default nextval('peering_autonomoussystem_id_seq'::regclass)
asn | bigint | not null
name | character varying(128) | not null
comment | text | not null
ipv6_max_prefixes | integer | not null
ipv4_max_prefixes | integer | not null
updated | timestamp with time zone |
irr_as_set | character varying(255) |
ipv4_max_prefixes_peeringdb_sync | boolean | not null
ipv6_max_prefixes_peeringdb_sync | boolean | not null
irr_as_set_peeringdb_sync | boolean | not null
created | timestamp with time zone |
potential_internet_exchange_peering_sessions | inet[] | not null
contact_email | character varying(254) | not null
contact_name | character varying(50) | not null
contact_phone | character varying(20) | not null
Indexes:
"peering_autonomoussystem_pkey" PRIMARY KEY, btree (id)
"peering_autonomoussystem_asn_ec0373c4_uniq" UNIQUE CONSTRAINT, btree (asn)
Check constraints:
"peering_autonomoussystem_ipv4_max_prefixes_check" CHECK (ipv4_max_prefixes >= 0)
"peering_autonomoussystem_ipv6_max_prefixes_check" CHECK (ipv6_max_prefixes >= 0)
Referenced by:
TABLE "peering_directpeeringsession" CONSTRAINT "peering_directpeerin_autonomous_system_id_691dbc97_fk_peering_a" FOREIGN KEY (autonomous_system_id) REFERENCES peering_autonomoussystem(id) DEFERRABLE INITIALLY DEFERRED
TABLE "peering_internetexchangepeeringsession" CONSTRAINT "peering_peeringsessi_autonomous_system_id_9ffc404f_fk_peering_a" FOREIGN KEY (autonomous_system_id) REFERENCES peering_autonomoussystem(id) DEFERRABLE INITIALLY DEFERRED
peering_manager=#
peering_manager=# select asn,name,potential_internet_exchange_peering_sessions from peering_autonomoussystem limit 7;
asn | name | potential_internet_exchange_peering_sessions
-------+---------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
37662 | WIOCC | {2001:504:1::a503:7662:1,198.32.160.70}
38001 | NewMedia Express Pte Ltd | {2001:504:16::9471,206.81.81.204}
46562 | Total Server Solutions | {2001:504:1::a504:6562:1,198.32.160.12,2001:504:16::b5e2,206.81.81.81,2001:504:1a::35:21,206.108.35.21,2001:504:2d::18:80,198.179.18.80,2001:504:36::b5e2:0:1,206.82.104.156}
55081 | 24Shells Inc | {2001:504:1::a505:5081:1,198.32.160.135}
62887 | Whitesky Communications | {2001:504:16::f5a7,206.81.81.209}
2603 | NORDUnet | {2001:504:1::a500:2603:1,198.32.160.21}
6128 | Cablevision Systems Corporation | {}
(7 rows)
You can use array_length(). On empty arrays or nulls this returns NULL.
...
WHERE array_length(potential_internet_exchange_peering_sessions, 1) IS NOT NULL
...
better to compare array length with integer number
...
WHERE array_length(potential_internet_exchange_peering_sessions, 1) > 0
...

Update intermediate result

EDIT
As requested a little background of what I want to achieve. I have a table that I want to query but I don't want to change the table itself. Next the result of the SELECT query (what I called the 'intermediate table') needs to be cleaned a bit. For example certain cells of certain rows need to be swapped and some strings need to be trimmed. Of course this could all be done as postprocessing in, e.g., Python, but I was hoping to do all of this with one query statement.
Being new to Postgresql I want to update the intermediate table that results from a SELECT statement. So I basically want to edit the resulting table from a SELECT statement in one query. I'd like to prevent having to store the intermediate result.
I've tried the following 'with clause':
with result as (
select
a
from
b
)
update result as r
set
a = 'd'
...but that results in ERROR: relation "result" does not exist, while the following does work:
with result as (
select
a
from
b
)
select
*
from
result
As I said, I'm new to Postgresql so it is entirely possible that I'm using the wrong approach.
Depending on the complexity of the transformations you want to perform, you might be able to munge it into the SELECT, which would let you get away with a single query:
WITH foo AS (SELECT lower(name), freq, cumfreq, rank, vec FROM names WHERE name LIKE 'G%')
SELECT ... FROM foo WHERE ...
Or, for more or less unlimited manipulation options, you could create a temp table that will disappear at the end of the current transaction. That doesn't get the job done in a single query, but it does get it all done on the SQL server, which might still be worthwhile.
db=# BEGIN;
BEGIN
db=# CREATE TEMP TABLE foo ON COMMIT DROP AS SELECT * FROM names WHERE name LIKE 'G%';
SELECT 4677
db=# SELECT * FROM foo LIMIT 5;
name | freq | cumfreq | rank | vec
----------+-------+---------+------+-----------------------
GREEN | 0.183 | 11.403 | 35 | 'KRN':1 'green':1
GONZALEZ | 0.166 | 11.915 | 38 | 'KNSL':1 'gonzalez':1
GRAY | 0.106 | 15.921 | 69 | 'KR':1 'gray':1
GONZALES | 0.087 | 18.318 | 94 | 'KNSL':1 'gonzales':1
GRIFFIN | 0.084 | 18.659 | 98 | 'KRFN':1 'griffin':1
(5 rows)
db=# UPDATE foo SET name = lower(name);
UPDATE 4677
db=# SELECT * FROM foo LIMIT 5;
name | freq | cumfreq | rank | vec
--------+-------+---------+-------+---------------------
grube | 0.002 | 67.691 | 7333 | 'KRP':1 'grube':1
gasper | 0.001 | 69.999 | 9027 | 'KSPR':1 'gasper':1
gori | 0.000 | 81.360 | 28946 | 'KR':1 'gori':1
goeltz | 0.000 | 85.471 | 47269 | 'KLTS':1 'goeltz':1
gani | 0.000 | 86.202 | 51743 | 'KN':1 'gani':1
(5 rows)
db=# COMMIT;
COMMIT
db=# SELECT * FROM foo;
ERROR: relation "foo" does not exist