Using SELECT sub-query inside jsonb_array_elements_text function in Postgres - postgresql

I have the following query
SELECT DISTINCT ON (user_id) user_id, timestamp
FROM entries
WHERE user_id in (1,2)
AND entry_type IN(
SELECT jsonb_array_elements_text(
SELECT entry_types
FROM users INNER JOIN orgs
ON org_id = orgs.id
WHERE users.id = 1
)
);
I'm getting a syntax error at or near select
syntax error at or near "select" LINE 1: ... entry_type in( select
jsonb_array_elements_text(select ent.
The field entry_types is a JSONB field, so I am trying to convert it to text in order to use it in the WHERE IN clause.
PostgreSQL 13.0
This sub-query within jsonb_array_elements_text
SELECT entry_types
FROM users INNER JOIN orgs
ON org_id = orgs.id
WHERE users.id = 1
Returns a single JSONB entry like this:
entry_types
--------------------------------------------
["type1", "type2", "type3"]
I'm simply trying to use the array of text values returned there as the criteria inside the WHERE IN clause.

The syntax error seems to point somewhere else, so maybe I am wrong, but the problem I see is a missing pair of parentheses around the subquery:
jsonb_array_elements_text((SELECT ...))

Related

Postgres Crosstab query with CTE (with clause)

Recently started working on Postgres and need to pivot data.
I wrote the following query:
select *
from crosstab (
$$
with tmp_kv as (
select distinct pat_id
,col.name as key, replace(replace(replace(value, '[',''), ']', ''),'"','') as value
from (
select p.Id as pat_id, nullif(kv.key,'undefined')::int as key, trim(kv.value::text,'"') as value
from pat_table p
left join e_table e on e.pat_id = p.id and e.id is null
,jsonb_each_text(p.data) as kv
) t
left join lateral (
select name::text as name from public.config_fields fld
where id = t.key
) col on true
)
select pat_id, key, value
from tmp_kv
where nullif(trim(key),'') is not null
order by pat_id, key
$$,$$
select distinct key from tmp_kv -- (Get error "relation "tmp_kv" does not exist" )
where nullif(trim(key),'') is not null
order by 1
$$
) as (
pat_id bigint
...
...
);
Query works if I take the WITH clause out into temporary table. But will be deploying it to production with read replicas, so need it to be working with a CTE. Is there a way?
The two queries passed as strings to the crosstab() function are separate queries.
A CTE can only be attached to a single query.
What you ask for is strictly impossible.
Since you have to spell out the (static) return type for crosstab() anyway, and the result of the query in the 2nd parameter has to match that, it's pointless to use a query with a dynamic result as 2nd parameter to begin with.

access subquery return value in union to other table postgres

Given the following query:
select parent_id from (
select parent_id, (most_recent_sit(date, child_id, key)).*
from links
where child_id = ANY(source_ids)
group by parent_id
) as subquery
union select * from link_traversal(
array(select parent_id from subquery),
);
Postgres throws the following error for the link_traversal function call:
relation "subquery" does not exist
link_traversal() and most_recent_sit() are custom functions I've created. I'd like to use the two functions and union the results together, where I reference the results of most_recent_sit() as the input for link_traversal().
Is it possible to reference a subquery select like so in a subsequent function call?

Populate column with query results in Postgresql

Table columns structured like:
longitude, latitude, gid, Hash
-78.885636, 36.854, 1, empty
Using PostgreSQL 9.4 and trying to update column Hash with results of a geohash function:
SELECT ST_GeoHash(ST_SetSRID(ST_MakePoint(longitude::float, latitude::float), 4326))
FROM my_table;
To update the column, I am using:
UPDATE my_table SET Hash = (SELECT ST_GeoHash(ST_SetSRID(ST_MakePoint(longitude::float, latitude::float), 4326))
FROM my_table);
But I get an error:
ERROR: more than one row returned by a subquery used as an expression.
I'm new to this so I may be asking a tedious question. Any help would be appreciated. For now I'll be RTFM-ing.
To update Hash column with a calculated value you need to use this query, where gid is a primary key(probably you need to change it).
UPDATE my_table
SET Hash = my_table_2.geo_hash
FROM
(SELECT gid,
ST_GeoHash(ST_SetSRID(ST_MakePoint(longitude::float, latitude::float), 4326)) as geo_hash
FROM my_table) as my_table_2
WHERE my_table.gid = my_table_2.gid
This worked for me:
UPDATE my_table SET "Hash" = (
SELECT ST_GeoHash(ST_SetSRID(ST_MakePoint(longitude::float, latitude::float), 4326))
FROM my_table p
WHERE my_table.gid = p.gid
);

PostgreSQL - return most common value for all columns in a table

I've got a table with a lot of columns in it and I want to run a query to find the most common value in each column.
Ordinarily for a single column, I'd run something like:
SELECT country
FROM users
GROUP BY country
ORDER BY count(*) DESC
LIMIT 1
Does PostgreSQL have a built in function for doing this or can anyone suggest a query I could run to achieve this?
Using the same query, for more than one column you should do:
SELECT *
FROM
(
SELECT country
FROM users
GROUP BY 1
ORDER BY count(*) DESC
LIMIT 1
) country
,(
SELECT city
FROM users
GROUP BY 1
ORDER BY count(*) DESC
LIMIT 1
) city
This works for any type and will return all the values in the same row, with the columns having its original name.
For more columns just had more subquerys as:
,(
SELECT someOtherColumn
FROM users
GROUP BY 1
ORDER BY count(*) DESC
LIMIT 1
) someOtherColumn
Edit:
You could reach it with window functions also. However it will not be better in performance nor in readability.
Starting from PG 9.4 there is aggregate function for this:
mode() WITHIN GROUP (ORDER BY sort_expression)
returns the most frequent input value (arbitrarily choosing the first one if there are multiple equally-frequent results)
And for earlier versions, you could create one...
CREATE OR REPLACE FUNCTION mode_array(anyarray)
RETURNS anyelement AS
$BODY$
SELECT a FROM unnest($1) a GROUP BY 1 ORDER BY COUNT(1) DESC, 1 LIMIT 1;
$BODY$
LANGUAGE SQL IMMUTABLE;
CREATE AGGREGATE mode(anyelement)(
SFUNC = array_append, --Function to call for each row. Just builds the array
STYPE = anyarray,
FINALFUNC = mode_array, --Function to call after everything has been added to array
INITCOND = '{}'--Initialize an empty array when starting
) ;
Usage: SELECT mode(column) FROM table;
If I were doing this, I'd write a query like this one:
SELECT 'country', country
FROM users
GROUP BY country
ORDER BY count(*) DESC
LIMIT 1
UNION ALL
SELECT 'city', city
FROM USERS
GROUP BY city
ORDER BY count(*) DESC
LIMIT 1
-- etc.
It should be noted this only works if all the columns are of compatible types. If they are not, you'll probably need a different solution.
This window function version will read the users table and the computed table once each. The correlated subquery version will read the users table once for each of the columns. If the columns are many as in the OPs case then my guess is that this is faster. SQL Fiddle
select distinct on (country_count, age_count) *
from (
select
country,
count(*) over(partition by country) as country_count,
age,
count(*) over(partition by age) as age_count
from users
) s
order by country_count desc, age_count desc
limit 1

Calling a function on every row returned by a subquery

I need to run the following query to extract the values of my raster records in a specific point.
select st_value((select rast from mytable),
(select st_GeomFromText('POINT(30.424 -1.978)', 4326)))
But I encounter with the following error:
ERROR: more than one row returned by a subquery used as an expression
SQL state: 21000
It needs just one record for this function but I need to extract values of all of records.
If a subquery returns multiple rows, you must either use it in a common table expression (CTE / WITH query) and FROM alias, or use FROM (SELECT ...) alias. In this case, though, it looks like it's simpler than that:
select st_value(rast, st_GeomFromText('POINT(30.424 -1.978)', 4326))
FROM mytable;
Both subqueries appear to be unnecessary.
If you truly needed the subquery you'd write something syntactically like:
WITH sq(rast) AS ( SELECT rast FROM mytable )
SELECT st_value(rast, st_GeomFromText('POINT(30.424 -1.978)', 4326))
FROM sq;
or
SELECT st_value(rast, st_GeomFromText('POINT(30.424 -1.978)', 4326))
FROM (SELECT rast FROM mytable) sq(rast);
Try:
Select st_value(rast),
st_GeomFromText('POINT(30.424 -1.978)', 4326)
from mytable
If you have a function with multiple columns, you can do something like this
SELECT (info).column1, (info).column2, (info).column3
FROM (select st_value(rast, st_GeomFromText('POINT(30.424 -1.978)', 4326)) AS info
FROM mytable
) AS foo