Conversion of int values to numerals in Postgresql? - postgresql

I would like to know if there is a built-in way to convert integer values into numerals in PostgreSQL?
As an example, is it possible to convert the integer 10 into the string TEN.
Thank You.

There's nothing built-in. For this sort of thing your best bet will be to make use of PostgreSQL's pluggable procedural languages. Use PL/Perl or PL/Python with a suitable Perl or Python library to do the job.
In this case I'd probably use PL/Perl with Lingua::EN::Numbers.
CREATE OR REPLACE FUNCTION num2en(numeric) RETURNS text AS $$
use Lingua::EN::Numbers qw(num2en);
return num2en($_[0]);
$$ LANGUAGE plperlu;
You'll need to install Lingua::EN::Numbers into the Perl being used by PostgreSQL using CPAN or system packages first. In my case (Fedora 19) this was a simple yum install perl-Lingua-EN-Numbers.noarch, then I could:
regress=> SELECT num2en(10);
num2en
--------
ten
(1 row)
regress=# SELECT num2en(NUMERIC '142.5');
num2en
--------------------------------------
one hundred and forty-two point five
(1 row)
By default the function is accessible by normal users so you don't have to issue any extra GRANTs.

Try this query:
SELECT split_part (cash_words (10::VARCHAR::MONEY), 'dollar', 1);
It's a internal function of PostgreSQL.

Related

What is the postgresql equivalent of DUMP function in Oracle?

The dump function in Oracle displays the internal representation of data:
DUMP returns a VARCHAR2 value containing the data type code, length in bytes, and internal representation of expr
Fore example:
SELECT DUMP(cast(1 as number ))
2 FROM DUAL;
DUMP(CAST(1ASNUMBER))
--------------------------------------------------------------------------------
Typ=2 Len=2: 193,2
SQL> SELECT DUMP(cast(1.000001 as number ))
2 FROM DUAL;
DUMP(CAST(1.000001ASNUMBER))
--------------------------------------------------------------------------------
Typ=2 Len=5: 193,2,1,1,2
It shows that the first 1 uses 2 byte for storing and the second example uses 5 bytes for storing.
I suppose the similar function in PostgreSQL is pg_typeof but it returns only the type name without information about byte usage:
SELECT pg_typeof(33);
pg_typeof
integer (1 row)
Does anybody know if there is an equivalent function in PostgreSQL?
I don't speak PostgreSQL.
However, Oracle functionality page says that there's Orafce which
implements in Postgres some of the functions from the Oracle database that are missing (or behaving differently)
It, furthermore, mentions the dump function
dump (anyexpr [, int]): Returns a text value that includes the datatype code, the length in bytes, and the internal representation of the expression
One of examples looks like this:
postgres=# select pg_catalog.dump('Pavel Stehule',10);
dump
-------------------------------------------------------------------------
Typ=25 Len=17: 68,0,0,0,80,97,118,101,108,32,83,116,101,104,117,108,101
(1 row)
To me, it looks like Oracle's dump:
SQL> select dump('Pavel Stehule') result from dual;
RESULT
--------------------------------------------------------------
Typ=96 Len=13: 80,97,118,101,108,32,83,116,101,104,117,108,101
SQL>
I presume you'll have to visit GitHub and install the package to see whether you can use it or not.
It is not a complete equivalent, but if you want to figure out the byte values used to encode a string in PostgreSQL, you can simply cast the value to bytea, which will give you the bytes in hexadecimal:
SELECT CAST ('schön' AS bytea);
This will work for strings, but not for numbers.

Workaround for inlined SQL function dispatch in PostgreSQL

Goal
I'm trying to make PostgreSQL do something like function dispatch, but I'm open to any other solution for the problem that lets me keep the function I'm calling a SQL function (versus PL/PGSQL) because I want it to be inlined.
Suppose I have two functions like:
create or replace function is_in_view_one(p people) returns boolean as $$
select p.state = ANY(ARRAY['TX', 'NY', 'CA'])
$$ language sql strict stable;
And:
create or replace function is_in_view_two(p people) returns boolean as $$
select p.state = ANY(ARRAY['NV', 'FL', 'MT'])
$$ language sql strict stable;
I want to be able to write some code, or adapt the above functions, so I can write:
select count(*)
from people
where is_in_view(people, 'one');
And I want is_in_view to be fully inline-able according to these criteria: https://wiki.postgresql.org/wiki/Inlining_of_SQL_functions'
Attempted Solution via Domains
I've tried to set up a solution using domains as function identifiers, and although it doesn't work, I think someone more knowledgeable about PostgreSQL types, casts, and function identification might know how to hack it.
I tried to do:
create domain view_one_id as uuid check (value = 'ed744964-6561-11eb-878e-c7ad77d3260a');
create domain view_two_id as uuid check (value = 'fa9fe0f8-6561-11eb-878e-c79c81b46d0c');
create or replace function say_n(v view_one_id) returns integer as $$
select 1
$$ language sql strict;
create or replace function say_n(v view_two_id) returns integer as $$
select 2
$$ language sql strict;
Hoping that I could then do:
select say_n('ed744964-6561-11eb-878e-c7ad77d3260a') # 1
select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c') # 2
But instead I get:
=# select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c');
ERROR: function say_n(unknown) is not unique
LINE 1: select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c');
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
I can do:
=# select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c'::view_two_id);
say_n
-------
2
(1 row)
But in order to integrate with external tooling that knows how to call functions and only call functions (not also supply a variable type cast) I'm holding out hope for a solution that doesn't require modifications to this external tooling.
Happy to entertain alternatives! I feel like this solution might be possible by fiddling with the CAST or something, however.
What about using CASE:
select p.state = ANY (CASE WHEN $2 = 'one'
THEN ARRAY['TX', 'NY', 'CA']
ELSE ARRAY['NV', 'FL', 'MT']
END)
But if you want efficiency, you would be better off with the two functions, because the above cannot use an index.

Change how numbers are printed in psql

How can I set numeric display format in psql?
I have many character varying and double precision columns in the select query. I can use to_char() or round() on each of the numeric columns, but with many columns this makes the code too repetitive.
Is there a shortcut? For example, are there any psql session settings, something like \pset numeric '9.9EEEEE' (I just made it up, don't try to use it). I could not quickly find such settings in the manual: PostgreSQL: Documentation: psql.
Example:
-- Got this:
=# select bar from (values (123456789), (0.123456789), (0.000000123456789)) table_foo (bar);
bar
-------------------
123456789
0.123456789
0.000000123456789
-- I have to use this workaround:
=# select to_char(bar, '9.9EEEEE') as bar from (values (123456789), (0.123456789), (0.000000123456789)) table_foo (bar);
bar
----------
1.2e+08
1.2e-01
1.2e-07
-- I want this:
-- set some session settings, and then magically:
=# select bar from (values (123456789), (0.123456789), (0.000000123456789)) table_foo (bar);
bar
----------
1.2e+08
1.2e-01
1.2e-07
There is not other possibility to specify number of format then specification of numeric locale. Values are formatted on server side already, psql does formatting to final output format (table, html, csv), but the values are formatted, and psql doesn't try to do reformat (numericlocale is an exception). I remember long discussion about possibility to specify output format of boolean type, but still without actual results. psql has not strong functionality for generating rich reports. It should be fast, safe and robust interactive client. Although I would to have some stronger possibility in this area, the modern spreadsheets and reporting tools are for some tasks better.
You could always define your own formatting function, but you'd have to be doing a lot of formatting on a lot of columns for this to be considered an improvement in readability. I wouldn't recommend it for your toy example but if your real queries have dozens of columns it might be neater.
sophia=> create function x(numeric) returns text language sql as $$ select to_char($1, '9.9EEEEE') $$;
CREATE FUNCTION
sophia=> select x(bar) from (values (123456789), (0.123456789), (0.000000123456789)) table_foo (bar);
x
----------
1.2e+08
1.2e-01
1.2e-07
(3 rows)
sophia=>

bit_count function in PostgreSQL

We are in the process of migrating a MySQL 5.7 database to PostgreSQL 9.6.
A real issue is the lack of bit_count function in PostgreSQL. This function is also not available in the upcoming version 10.
Current MySQL code snippet (simplified):
-- mysql specific, tested with 5.7.19
select code,phash,bit_count(phash ^ -9187530158960050433) as hd
from documents
where phash is not null and bit_count(phash ^ -9187530158960050433) < 7
order by hd;
We tried a naive solution (converting the BIGINT to a String and counting the "1"'s), but it performs terribly compared to MySQL.
Java uses a trick from Hacker's Delight, but AFAIK this is not possible with PostgreSQL, because the >>> operator is (also) not available.
Question: Is a there solution/workaround available comparable with MySQL performance wise?
UPDATE 1
Best solution i could find is based on this SO answer:
First create bit_count function:
CREATE OR REPLACE FUNCTION bit_count(value bigint)
RETURNS numeric
AS $$ SELECT SUM((value >> bit) & 1) FROM generate_series(0, 63) bit $$
LANGUAGE SQL IMMUTABLE STRICT;
Now we can use almost the same SQL as with MySQL:
-- postgresql specific, tested with 9.6.5
select code,phash,bit_count(phash # -9187530158960050433) as hd
from documents
where phash is not null and bit_count(phash # -9187530158960050433) < 7
order by hd;
UPDATE 2
Based on #a_horse_with_no_name comment, i tried this function:
-- fastest implementation so far. 10 - 11 x faster than the naive solution (see UPDATE 1)
CREATE OR REPLACE FUNCTION bit_count(value bigint)
RETURNS integer
AS $$ SELECT length(replace(value::bit(64)::text,'0','')); $$
LANGUAGE SQL IMMUTABLE STRICT;
However, this is still 5 - 6 times slower than MySQL (tested wit exact the same data set of 200k phash values on the same hardware).
Function bit_count is available since PostgreSQL version 14, see
Bit String Functions and Operators.
Example:
select bit_count(B'1101');
Result is 3.
Note that the function is defined for types bit and bit varying. So if you want to use it with integer values, you need to cast.
Example:
select cast (cast (1101 as text) as bit varying);
Result is B'1101'.
Combining both examples:
select bit_count(cast (cast (1101 as text) as bit varying));
Question: Is a there solution/workaround available comparable with
MySQL performance wise?
To get a comparable speed, a compiled C function should be used.
If you can compile C code, see for instance
https://github.com/dverite/postgresql-functions/tree/master/hamming_weight
The code itself is very simple.
The result seems 10 times faster than the bit_count function based on counting the 0 characters in the bit(64) string as text.
Example:
plpgsql function:
test=> select sum(bit_count(x)) from generate_series(1,1000000) x;
sum
---------
9884999
(1 row)
Time: 2442,340 ms
C function:
test=> select sum(hamming_weight(x::int8)) from generate_series(1,1000000) x;
sum
---------
9884999
(1 row)
Time: 239,749 ms
If you are trying to compute the hamming distance of perceptual hashes or similar LSH bit strings, then this question may be a closely related to this answer
If you are looking specifically for a pre-built way to do hamming distance queries on a PostgreSQL database, then this may be the cure: an extension for hamming distance search

PostgreSQL execute statement conditionally by server version

I'm currently writing some installer script that fires SQL files against different database types depending on the system's configuration (the webapplication supports multiple database server like MySQL, MSSQL and PostgreSQL).
One of those types is PostgreSQL. I'm not fluent with it and I would like to know if it's possible to make a statement into a define/populate SQL file that makes an SQL query conditional to a specific PostgreSQL server version.
How to make an SQL statement conditionally in plain PGSQL so that it is only executed in version 9? The command is:
ALTER DATABASE dbname SET bytea_output='escape';
The version check is to compare the version with 9.
Postgres does have version() function, however there is no major_vesion(). Assuming that output string always includes version number as number(s).number(s).number(s) you could write your own wrapper as:
CREATE OR REPLACE FUNCTION major_version() RETURNS smallint
AS $BODY$
SELECT substring(version() from $$(\d+)\.\d+\.\d+$$)::smallint;
$BODY$ LANGUAGE SQL;
Example:
=> Select major_version();
major_version
---------------
9
(1 row)
However real issue here is that AFAIK you can't execute your commands conditionally in "pure" SQL and best what you can do is to write some stored function like this:
CREATE OR REPLACE FUNCTION conditionalInvoke() RETURNS void
AS $BODY$
BEGIN
IF major_version() = 9 THEN
ALTER DATABASE postgres SET bytea_output='escape';
END IF;
RETURN;
END;
$BODY$ LANGUAGE plpgsql;
I think that you should rather use some scripting language and generate appropriate SQL with it.
Or you could just use
select setting from pg_settings where name = 'server_version'
Or
select setting from pg_settings where name = 'server_version_num'
If you need major version only
select Substr(setting, 1, 1) from pg_settings where name = 'server_version_num'
or
select Substr(setting, 1, strpos(setting, '.')-1) from pg_settings where name = 'server_version'
if you want it to be compatible with two digit versions.
Maybe you could make things dependent on the output of
select version();
(probably you'll have to trim and substring that a bit)
BTW (some) DDL statements may not be issued from within functions; maybe you'll have to escape to shell-programming and here-documents.