Postgres: jsonb to jsonb[]? - postgresql

Let's say I have this table:
ams=# \d player
Table "public.player"
Column | Type | Collation | Nullable | Default
-------------+--------------------------+-----------+----------+-------------------
id | integer | | not null |
created | timestamp with time zone | | not null | CURRENT_TIMESTAMP
player_info | jsonb | | not null |
And then I have this:
ams=# \d report
Table "public.report"
Column | Type | Collation | Nullable | Default
---------+--------------------------+-----------+----------+---------
id | integer | | not null |
created | timestamp with time zone | | not null |
data | jsonb[] | | not null |
How can I take the player_info from all the rows in the player table and insert that into a single row in the report table (into the data jsonb[] field)? My attempts with jsonb_agg() return a jsonb, and I can't for the life of me figure out how to go from jsonb to jsonb[]. Any pointers would be very much appreciated! Thanks in advance.

If you plainly want to copy the values, just treat it like any other data type, and use ARRAY_AGG.
SELECT ARRAY_AGG(player_info)
FROM player
WHERE id IN (...)
should return something of type json[].

Since jsonb[] is an array at the type level in PostgreSQL vs. a json array, use array_agg() instead of jsonb_agg().
insert into report
select 1 as id, now() as created, array_agg(player_info)
from player
;

Related

Bulk update datatype of a column in all relevant tables

An example of some tables with the column I want to change.
+--------------------------------------+------------------+------+
| ?column? | column_name | data_type |
|--------------------------------------+------------------+------|
| x.articles | article_id | bigint |
| x.supplier_articles | article_id | bigint |
| x.purchase_order_details | article_id | bigint |
| y.scheme_articles | article_id | integer |
....
There are some 50 tables that have the column.
I want to change the article_id column from a numeric data type to a textual data type. It is found across several tables. Is there anyway to update them all at once ? Information schema is readonly so I cannot do an update on it. Other than writing inidividual alter statements for all the tables, is there a better way to do it ?

Postgres Alter Column Datatype & Update Values

I am new to writing postgres queries, I am stuck at a problem where the price is a string having $ prefix, I want to change the data type of column price to float and update the values by removing the $ prefix. Can someone help me do that?
bootcamp=# SELECT * FROM car;
id | make | model | price
----+---------+---------------------+-----------
1 | Ford | Explorer Sport Trac | $92075.96
2 | GMC | Safari | $81521.80
3 | Mercury | Grand Marquis | $64391.84
(3 rows)
bootcamp=# \d car
Table "public.car"
Column | Type | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------------------------------
id | bigint | | not null | nextval('car_id_seq'::regclass)
make | character varying(50) | | not null |
model | character varying(50) | | not null |
price | character varying(50) | | not null |
Thanks
You can cleanup the string while altering the table:
alter table car
alter column price type numeric using substr(price, 2)::numeric;
First you have to disable safe update mode to update without WHERE clause:
SET SQL_SAFE_UPDATES=0;
Then remove '$' from all rows:
UPDATE car SET price = replace(price, '$', '');
Then change the column type:
ALTER TABLE car ALTER COLUMN price TYPE your_desired_type;
If you want to enable safe update mode again:
SET SQL_SAFE_UPDATES=1;

find all rows that for a postgres jsonb column that have a specfic array value

(using Postgres 9.5)
Say you have a Postgres table with the following schema:
Table "public.tableA"
Column | Type | Collation | Nullable | Default
---------------+--------------------------+-----------+----------+----------------------------------------------
id | bigint | | not null | nextval('truelayer_tokens_id_seq'::regclass)
user_id | text | | not null |
created_at | timestamp with time zone | | | now()
updated_at | timestamp with time zone | | |
provider_id | text | | not null | ''::text
scopes | jsonb | | not null | '{}'::jsonb
and inside the scopes column is the a subset of the following data for various user_ids.
["A", "B", "C", "D", "E"]
Some users will have Scopes: ["A"]
Some users will have Scopes: ["B","C","D"]
Some users will have Scopes: ["A","D"]
I want to run a query that returns all the rows for a specific user_id where the array in "scopes" contains the value "A"
I am struggling to even get started with this so all advice appreciated.
Use the jsonb containment operator #>:
SELECT id, scopes
FROM "tableA"
WHERE scopes #> '["A"]';

Does size of row or column effect aggregation queries in Postgresql?

Consider the following table definition:
Column | Type | Collation | Nullable | Default
-----------------+--------------------------+-----------+----------+-------------
id | uuid | | not null |
reference_id | text | | |
data | jsonb | | |
tag | character varying(255) | | |
created_at | timestamp with time zone | | |
updated_at | timestamp with time zone | | |
is_active | boolean | | not null | true
status | integer | | | 0
message | text | | |
batch_id | uuid | | not null |
config | jsonb | | |
Overall table size to be over 500M and every row in the table contains a data column to have a JSON of over 50MB.
Questions -
Does the size of the data column effect aggregation such as count?
Assume we are running the below query -
select count(*)
from table
where batch_id = '88f30539-32d7-445c-8d34-f1da5899175c';
Does the size of the data column effect aggregation such as sum?
Assume we are running the below queries -
Query 1 -
select sum(data->>'count'::int)
from table
where batch_id = '88f30539-32d7-445c-8d34-f1da5899175c';
Query 2 -
select sum(jsonb_array_length(data->'some_array'))
from table
where batch_id = '88f30539-32d7-445c-8d34-f1da5899175c';
The best way to know is to measure.
Once the data is large enough to always be TOASTed, then its size will no longer affect the performance of queries which do not need to access the TOASTed data contents, like your first one. Your last two do need to access the contents and their performance will depend on the size.

Postgres varchar column giving error "invalid input syntax for integer"

I'm trying to use a INSERT INTO ... SELECT statement to copy columns from one table into another table but I am getting an error message:
gis=> INSERT INTO places (SELECT 0 AS osm_id, 0 AS code, 'country' AS fclass, pop_est::numeric(10,0) AS population, name, geom FROM countries);
ERROR: invalid input syntax for integer: "country"
LINE 1: ...NSERT INTO places (SELECT 0 AS osm_id, 0 AS code, 'country' ...
The SELECT statement by itself is giving a result like I expect:
gis=> SELECT 0 AS osm_id, 0 AS code, 'country' AS fclass, pop_est::numeric(10,0) AS population, name, geom FROM countries LIMIT 1;
osm_id | code | fclass | population | name | geom
--------+------+---------+------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0 | 0 | country | 103065 | Aruba | 0106000000010000000103000000010000000A000000333333338B7951C0C8CCCCCC6CE7284033333333537951C03033333393D82840CCCCCCCC4C7C51C06066666686E0284000000000448051C00000000040002940333333333B8451C0C8CCCCCC0C18294099999999418351C030333333B3312940333333333F8251C0C8CCCCCC6C3A294000000000487E51C000000000A0222940333333335B7A51C00000000000F62840333333338B7951C0C8CCCCCC6CE72840
(1 row)
But somehow it looks like it's getting confused thinking that the fclass column should be an integer when, in fact, it is actually a character varying(20)
gis=> \d+ places
Unlogged table "public.places"
Column | Type | Modifiers | Storage | Stats target | Description
------------+------------------------+------------------------------------------------------+----------+--------------+-------------
gid | integer | not null default nextval('places_gid_seq'::regclass) | plain | |
osm_id | bigint | | plain | |
code | smallint | | plain | |
fclass | character varying(20) | | extended | |
population | numeric(10,0) | | main | |
name | character varying(100) | | extended | |
geom | geometry | | main | |
Indexes:
"places_pkey" PRIMARY KEY, btree (gid)
"places_geom" gist (geom)
I've tried casting all of the columns to their exact types they need to be for the destination table but that doesn't seem to have any effect.
All of the other instances of this error message I can find online appear to be people trying to use empty strings as an integer which isn't relevant here because I'm selecting a constant string as fclass.
You need to specify the column names you are inserting into:
INSERT INTO places (osm_id, code, fclass, population, name, geom) SELECT ...
Without specifying them individually, it is assumed that all columns are to be inserted into - including gid, which you want to have auto-populate. So, 'country' is actually being inserted into code by your current INSERT statement.