ERROR: invalid input syntax for type json - postgresql

I am using PostgreSQL 10.4 and have 2 tables:
person:
CREATE TABLE person (
nationality character varying(100),
name character varying(100),
age integer
);
country:
CREATE TABLE country (
demonym character varying(50),
name character varying(50)
);
This is the query that I am trying to run:
select "c"."name",
(SELECT row_to_json(r) FROM (
SELECT
COALESCE(sum(CASE WHEN p."nationality"='finn' THEN 1 ELSE 0 END),0) as "1",
COALESCE(sum(CASE WHEN p."nationality"='spanish' THEN 1 ELSE 0 END),0) as "2"
FROM "person" as p
WHERE "p"."nationality"="c"."demonym"
) as r) as "nationalitiesCount"
from "country" as c
WHERE 'nationalitiesCount'::json->'1' > 10
This yields an error:
ERROR: invalid input syntax for type json
LINE 11: WHERE 'nationalitiesCount'::json->'1' > 10
DETAIL: Token "nationalitiesCount" is invalid.
CONTEXT: JSON data, line 1: nationalitiesCount
The second line has the first ' highlighted as the code which causes the error to appear.
Question: How could the error be rectified?

The first issue is that references to columns should not be in single quotes, though they can be / sometimes have to be in double quotes, which is why it doesn't like 'nationalitiesCount'::json->'1'. The second is that the column name nationalitiesCount is defined in the SELECT part of the query and can't be referenced in the WHERE clause of the query where it's defined, so you can move your query into a subquery so you can reference it outside of the subquery.
select *
from (select "c"."name",
(SELECT row_to_json(r)
FROM (
SELECT
COALESCE(sum(CASE WHEN p."nationality"='finn' THEN 1 ELSE 0 END),0) as "1",
COALESCE(sum(CASE WHEN p."nationality"='spanish' THEN 1 ELSE 0 END),0) as "2"
FROM "person" as p
WHERE "p"."nationality"="c"."demonym"
) as r) as "nationalitiesCount"
from "country" as c
) t
WHERE (t."nationalitiesCount"->>'1')::integer > 10

Related

CASE on ORDER BY based on field type

How can one execute ORDER BY taking into consideration the field value type?
CREATE TABLE public.test(
data jsonb
);
TRUNCATE test;
INSERT INTO test (data) VALUES ('{"age":2, "name": "b"}');
INSERT INTO test (data) VALUES ('{"age":1, "name": "cc"}');
INSERT INTO test (data) VALUES ('{"age":4, "name": "d"}');
INSERT INTO test (data) VALUES ('{"age":33, "name": "a"}');
-- works
SELECT * FROM test ORDER BY data->>'name' ASC;
-- works
SELECT * FROM test ORDER BY (data->>'age')::numeric ASC;
-- does not work
/*
ERROR: CASE types text and numeric cannot be matched
LINE 5: WHEN 'number' THEN (data->>'age')::numeric
*/
SELECT data->>'name' as name, data->>'age' as age
FROM test
ORDER BY
CASE jsonb_typeof(data->'age')
WHEN 'number' THEN (data->>'age')::numeric
ELSE data->>'age'
END
ASC;
(The actual field name will be injected into the query from code)
You could simply order by data->'age' or data->'name' without casting it in some cases.
(note the use of the -> operator which returns a JSONB value, instead of the ->> which always returns text)
This seems to work in both cases:
SELECT data->>'name' as name, data->>'age' as age
FROM test
ORDER BY data->'age' ASC;
-- name age
-- cc 1
-- b 2
-- d 4
-- a 33
SELECT data->>'name' as name, data->>'age' as age
FROM test
ORDER BY data->'name' ASC;
-- name age
-- a 33
-- b 2
-- cc 1
-- d 4

PostgreSQL find by value in array in jsonb data

How can I get records from table where array in column value contains any value to find.
Well, the column can contain any data type of array, objects, strings, etc and null value. And arrays in column can contain any serializable data type
id|value |
--+------------+
1|null |
2|[0.05, 0.11]|
You can use a JSON path expression:
select *
from the_table
where value ## '$[*] == 0.11'
If the column doesn't contain an array, you can use
select *
from the_table
where value ## '$.* == 0.11'
This assumes value is defined as jsonb (which it should be). If it's not, you have to cast it value::jsonb
Online example
Some samples:
-- sample 1
with sample_data as (
select 1 as "id", null::jsonb as "value"
union all
select 2 as "id", '[0.05, 0.11]'::jsonb as "value"
)
select a2.pval::float4 from sample_data as a1
cross join jsonb_array_elements(a1."value") as a2(pval)
--Return:
0.05
0.11
-- sample 2
with sample_data as (
select 1 as "id", null::jsonb as "value"
union all
select 2 as "id", '[0.05, 0.11]'::jsonb as "value"
)
select a2.pval::float4 from sample_data as a1
cross join jsonb_array_elements(a1."value") as a2(pval)
where a2.pval::float4 > 0.1
--Return:
0.11

Report an amount field by a value in another field

I have a large table that has a structure something like the following:
Create Table public.example
(
id character varying(7) ,
type character varying(1) ,
amount decimal ,
date1 date ,
plan character varying(2) ,
date2 date );
Insert into public.example
( Id,Type,Amount,Date1,Plan,Date2)
values
( '1343657' , 'e',235.26 ,'2021-01-03', 'HS', '2021-07-03'),
( '1343657' , 's',6234.25,'2021-01-15', 'RT', '2021-05-09'),
( '1343657' , 's',235.26 ,'2021-01-05', 'HS', '2021-05-03'),
( '1343657' , '3',235.26 ,'2021-01-05', 'HS', '2021-05-17'),
( '1343657' , 's',235.26 ,'2021-01-05', 'HS', '2021-03-19'),
( '5364324' , 'e',1245.90,'2021-01-17', 'MM', '2021-04-23'),
( '5364324' , '1',5285.88,'2021-01-14', 'MM', '2021-02-28'),
( '5364324' , 'e',1245.10,'2021-01-08', 'VI', '2021-06-30'),
( '5364324' , 'e',7452.05,'2021-01-10', 'DT', '2021-03-07') ;
I need to list the "amount" field across the report in different buckets based on the value of the “Plan” field. I also need to summarize the amount by Id and Type. My method doesn’t work because it adds another required Group BY and I don’t get a summarized amount by Id and Type.
Select id,type,
case When a.plan ='HS' then sum(amount) else 0 end as "HS",
case When a.plan ='RT' then sum(amount) else 0 end as "RT",
case When a.plan ='MM' then sum(amount) else 0 end as "MM",
case When a.plan ='VI' then sum(amount) else 0 end as "VI",
case When a.plan ='DT' then sum(amount) else 0 end as "DT"
from public.example a
where date2>='2021-01-01' and date2<='2021-12-31'
group by 1,2,a.plan
The perfect solution would allow me to add date1 to the Select output as well.
Select OUTPUT
Thx

How to select rows where the condition where all rows are being extracted for a given condition?

I have this table
CREATE TABLE fruits
(
id SERIAL,
name VARCHAR
);
with these entries
INSERT INTO fruits(name)
VALUES('Orange');
INSERT INTO fruits(name)
VALUES('Ananas');
INSERT INTO fruits(name)
VALUES(null);
When I try to to select all rows that not equal to 'Ananas' by querying
select *
from fruits
where name <> 'Ananas'
I get these rows:
id name
-----------
1 Orange
What I would have expected was this
id name
-----------
1 Orange
3 null
How do I ensure that all rows that fulfills the condition gets selected?
Example in dbfiddle:
https://dbfiddle.uk/?rdbms=postgres_11&fiddle=a963d39df0466701b0a96b20db8461e6
Any "normal" comparison with null yields "unknown" which is treated as false in the context of the WHERE clause.
You need to use the null safe operator is distinct from:
select *
from fruits
where name is distinct from 'Ananas';
Alternatively you could convert NULL values to something different:
select *
from fruits
where coalesce(name, '') <> 'Ananas';

PostgreSQL: ERROR: column "bb" does not exist

I am not good at SQL, I wanted to take MAX using partition by in the following query, but when I use the same query without where clause of that max drive column it says that column does not exist but if I remove the column from where I can see in select the same column is present.
select
MAX(case when total_split_count = 0 or total_split_count is null then total_split_count else 1 end) OVER (PARTITION BY ia.col1,ia.col2,ia.col3,ia.col4,ia.col5,ia.col6) as bb
from audits.tbl_name ia
where bb = 1
ERROR: column "bb" does not exist Position: 304
where bb = 1
^ 1 statement failed.
but the query runs with where clause:
select
MAX(case when total_split_count = 0 or total_split_count is null then total_split_count else 1 end) OVER (PARTITION BY ia.col1,ia.col2,ia.col3,ia.col4,ia.col5,ia.col6) as bb
from audits.tbl_name ia
Note: I created that column at run time through "as".
The alias defined in select clause in not visible in where clause.
Use
select * from (select ... as bb from audits.tbl_name ia) x where bb = 1
or CTE:
with x as (select ... as bb from audits.tbl_name ia) select * from x where bb = 1