relative position of digits and ':' in postgres collating sequence - postgresql

say I have a table 't1' with a string column 'name'. And I have names 'n1','n2','n9' and 'n:'. If I do
select * from t1 orderby name asc
I expect
n1
n2
n9
n:
Given that ':' comes after '9' in ASCII, but instead, I get
n:
n1
n2
n9
Which is a surprise. Is there something I need to do to say 'use ASCII as the collating sequence for basic ASCII chars'

From my experience, this is a collation issue
SELECT * FROM t1 ORDER BY name COLLATE "POSIX";
This is a list of exapmle collations in case that collation have listed, SQL_Latin1_General_CP850_BIN does not work
https://www.postgresql.org/docs/9.1/static/collation.html

Related

Inclusive intervals don't work as expected in jooq and postgres

When using the jooq-postgres-extension and inserting a row with a field value IntegerRange.integerRange(10, true, 20, true) in the query it is translated by cast('[10,20]' as int4range).
It's interesting that if I run the query select cast('[10,20]' as int4range) I get [10,21) which is not an inclusive interval anymore.
My problem is: when I read the row back in Jooq the integerRange.end is now 21 and not 20.
Is this a known issue and is there a workaround rather than the obvious subtracting 1 to upper boundary?
From here Range Types:
The built-in range types int4range, int8range, and daterange all use a canonical form that includes the lower bound and excludes the upper bound; that is, [). User-defined range types can use other conventions, however.
So the cast transforms '[10,20]' to '[10,21)'.
You can do:
select upper_inc(cast('[10,20]' as int4range));
upper_inc
-----------
f
to test the upper bound for inclusivity and modify:
select upper(cast('[10,20]' as int4range));
upper
-------
21
accordingly.
The jOOQ 3.17 RANGE type support (#2968) distinguishes between
discrete ranges (e.g. DateRange, IntegerRange, LongRange, LocaldateRange)
non-discrete ranges (e.g. BigDecimalRange, LocalDateTimeRange, OffsetDateTimeRange, TimestampRange)
Much like in PostgreSQL, jOOQ treats these as the same:
WITH r (a, b) AS (
SELECT '[10,20]'::int4range, '[10,21)'::int4range
)
SELECT a, b, a = b
FROM r;
The result being:
|a |b |?column?|
|-------|-------|--------|
|[10,21)|[10,21)|true |
As you can see, PostgreSQL itself doesn't distinguish between the two identical ranges. While jOOQ maintains the information you give it, they're the same value in PostgreSQL. PostgreSQL itself won't echo back [10,20]::int4range to jOOQ, so you wouldn't be able to maintain this value in jOOQ.
If you need the distinction, then why not use BigDecimalRange instead, which corresponds to numrange in PostgreSQL:
WITH r (a, b) AS (
SELECT '[10,20]'::numrange, '[10,21)'::numrange
)
SELECT a, b, a = b
FROM r;
Now, you're getting:
|a |b |?column?|
|-------|-------|--------|
|[10,20]|[10,21)|false |

Does my Postgresl column contain only digits?

Is there a way to check if a character varying type column contains only digits or null values with Postgresql?
Maybe something like (this syntax is incorrect):
SELECT *
FROM mytable
ORDER BY
CASE WHEN mycol ~ '^[0-9\.]+$' THEN 1 ELSE 0 END
LIMIT 1
I'm expecting TRUE or FALSE as final result for the whole column.
If you want to to know if the values in all rows are digits, you can use
select not exists (select *
from mytable
where not (mycol ~ '^[0-9\.]+$'))
Online example
To get Nulls use COALESCE(mycol, 1) -- will return 1 if the value in mycol is NULL.
For checking numerics you could use regex LIKE'^[0-9]*' it wont detect decimal dots (dont know if your data have decimals)
BR!

Need help in parsing column value based on value in other column

I have two columns, COL1 and COL2. COL1 has value like 'Birds sitting on $1 and enjoying' and COL2 has value like 'the.location_value[/tree,\building]'
I need to update third column COL3 with values like 'Birds sitting on /tree and enjoying'
i.e. $1 in 1st column is replaced with /tree
which is the 1st word from list of comma separated words with in square brackets [] in COL2 i.e. [/tree,\building]
I wanted to know the best suitable combination of string function in postgresql to use to achieve this.
You need to first extract the first element from the comma separated list, to do that, you can use split_part() but you first need to extract the actual list of values. This can be done using substring() with a regular expression:
substring(col2 from '\[(.*)\]')
will return /tree,\building
So the complete query would be:
select replace(col1, '$1', split_part(substring(col2 from '\[(.*)\]'), ',', 1))
from the_table;
Online example: http://rextester.com/CMFZMP1728
This one should work with any (int) number after $:
select t.*, c.col3
from t,
lateral (select string_agg(case
when o = 1 then s
else (string_to_array((select regexp_matches(t.col2, '\[(.*)\]'))[1], ','))[(select regexp_matches(s, '^\$(\d+)'))[1]::int] || substring(s from '^\$\d+(.*)')
end, '' order by o) col3
from regexp_split_to_table(t.col1, '(?=\$\d+)') with ordinality s(s, o)) c
http://rextester.com/OKZAG54145
Note:it is not the most efficient though. It splits col2's values (in the square brackets) each time for replacing $N.
Update: LATERAL and WITH ORDINALITY is not supported in older versions, but you could try a correlating subquery instead:
select t.*, (select array_to_string(array_agg(case
when s ~ E'^\\$(\\d+)'
then (string_to_array((select regexp_matches(t.col2, E'\\[(.*)\\]'))[1], ','))[(select regexp_matches(s, E'^\\$(\\d+)'))[1]::int] || substring(s from E'^\\$\\d+(.*)')
else s
end), '') col3
from regexp_split_to_table(t.col1, E'(?=\\$\\d+)') s) col3
from t

UNION ALL, cast NULL as double precision, Postgres

In postgres, I'm getting an error when I try to union two tables where one table has a column (Amount) containing double precision data type, and the other table does not have a matching column and I'd like the records from that table to just have NULL in the Amount field.
Error:
"union types text and double precision cannot be matched postgres"
Pseudo-code:
SELECT * FROM (
SELECT
t1.Amount AS 'amount',
NULL::DATE AS 'date'
FROM Table1 AS t1
UNION ALL
SELECT
/* next line is the issue */
NULL AS 'amount',
t2.Date AS 'date'
FROM Table2 AS t2
) AS FOO
I feel fairly certain this solution is a simple casting problem but could not find anything from searching. How do I do the equivalent of NULL::DOUBLE in postgres?
EDIT::POSTERITY
The accepted answer from #klin and #a_horse_with_no_name's comment that points to a "historical" postgres cast expression :: where the syntax is equivalent:
CAST ( expression AS type )
expression::type
And, here is a list of the postgres data types.
In Postgres the type double precision is also known as float8 or simply float. Use one of them.
select null::double precision, null::float;

ORDER BY CASE & Ordinal not working

Sorting column #7 as an example -
This code does not sort data at all:
ORDER BY CASE WHEN '1'='2' THEN 5
WHEN '1'='1' THEN 7
ELSE 13 END
If I change it to a hard-coded ordinal it works:
ORDER BY 7
As long as the respective expressions in the SELECT list are of the same type, you can do it by using the expressions themselves instead of the SELECT list number:
SELECT expression1, expression2, ...
...
ORDER BY CASE
WHEN 1=2
THEN expression5
WHEN 1=1
THEN expression7
ELSE expression13
END;
If the data types are not the same, season with type casts.
Your query does not work because only integer literals can be used as column numbers in ORDER BY. In all other cases, an integer just stands for its constant value.
If it were not like this, ORDER BY expressions could easily become ambiguous. Look at the following:
... ORDER BY intcol + 3;
Should that mean “add three” or “add expression number three from the SELECT list”?