postgresql send variables to a function, casting? - sql

In one place I have
CREATE FUNCTION updateGeo2(text, float4, float4) RETURNS float AS $$
followed later by
SELECT updateGeo2('area', 40.88, -90.56);
and I get
error : ERROR: function updategeo2(unknown, numeric, numeric) does not exist
so it doesn't know that I tried to pass in a text variable, followed by a float variable and another float variable, it sees these as "unknown, numeric and numeric", lame. How do I let it know the types I am passing in?

try this way:
SELECT updateGeo2('area', (40.88)::float4, (-90.56)::float4);

Clarify misunderstanding
First of all, this should work as is, without type cast. I tested with PostgreSQL 9.1, 9.2 and also with 8.4.15. You must be running an earlier point-release or there is some other misunderstanding (like wrong search_path). Your information is misleading.
Except for ad-hoc calls, you should always add explicit type casts anyway to disambiguate. PostgreSQL allows function overloading. If another function should be created with the signature:
CREATE FUNCTION updateGeo2(text, numeric, numeric) RETURNS text AS $$ ..
... then it would take precedence over the other one due to the default type numeric for numeric literals. Existing code might break.
If, on the other hand, you add a function:
CREATE FUNCTION updateGeo2(char(5), numeric, numeric) RETURNS text AS $$ ..
Then Postgres does not know what to do any more and throws an exception:
ERROR: function updategeo2(unknown, numeric, numeric) is not unique
Proper syntax
SELECT updateGeo2('area', '40.88'::float4, '-90.56'::float4);
Or, more verbose in standard SQL:
SELECT updateGeo2('area', cast('40.88' AS float4), cast('-90.56' AS float4));
Or, if you really wanted to avoid single quotes (and colons):
SELECT updateGeo2('area', float4 '40.88', float4 '-90.56');
This way you cast a numeric literal to data type float4 (= real) directly.
More about type casting in the manual.
(40.88)::float4 works, too, but subtly less effective. First, 40.88 is taken to be of type numeric (the default type for this numeric literal containing a dot). Then the value is cast to float4. Makes two type casts.
More about numeric constants in the manual.

Related

How to convert a hash string to an integer in Snowflake?

I'm trying to get a hash of a decimal value and convert it to an integer. But the query results in the following error:
Numeric value 'b902cc4550838229a710bfec4c38cbc7eb11082367a409df9135e7f007a96bda' is not recognized
SELECT (CAST(sha2(TO_VARCHAR(ABS(12.5)), 256) AS INTEGER) % 100) AS temp_value
What is the correct way to convert a hash string to an integer in Snowflake?
I can not use any user defined functions. And have to go with Snowflake native functions.
The hash value contains alphabetic character so it will throw an error
SELECT --(CAST(
sha2(
TO_VARCHAR(
ABS(12.5)), 256)-- AS INTEGER) % 100)
AS temp_value;
You need to convert the hex value from the hash encoding to be int.
I've not been able to find a function built into Snowflake that does this, but if you have a look in the following link, it will explain how to create a javascript function to do the conversion for you:
https://snowflakecommunity.force.com/s/article/faq-does-snowflake-have-a-hex-to-int-type-function
If you use the function in the link, then your code becomes something like this:
SELECT (CAST(js_hextoint(sha2(TO_VARCHAR(ABS(12.5)), 256)) AS INTEGER) % 100) AS temp_value
I've not been able to test the above code I'm afraid, so there may be a bracket in the wrong place...
You have a 56 digit hexadecimal number. It's not going to fit into the maximum numeric precision of 38. You could use a floating point number, but that will lose precision.
create or replace function CONV(VALUE_IN string, OLD_BASE float, NEW_BASE float)
returns string
language javascript
as
$$
// Usage note: Loses precision for very large inputs
return parseInt(VALUE_IN, Math.floor(OLD_BASE).toString(Math.floor(NEW_BASE)));
$$;
select conv('b902cc4550838229a710bfec4c38cbc7eb11082367a409df9135e7f007a96bda', 16, 10);
--Returns 8.368282050700398e+76
For hex to integer, check Does snowflake have a ‘hex’ to ‘int’ type native function?. My guess is that most people checking this question (1k views) are looking for that.
But this specific question wants to convert a sha2 digest to integer for comparison purposes. My advice for that specific question is "don't".
That's because the hex string in the question represent the integer 83682820507003986697271120393377917644380327831689630185856829040117055843290, which is too much to handle even by Java's BigInteger.
Instead, just compare strings/binary to check if the values match or not.

ERROR: function regexp_matches(jsonb, unknown) does not exist in Tableau but works elsewhere

I have a column called "Bakery Activity" whose values are all JSONs that look like this:
{"flavors": [
{"d4js95-1cc5-4asn-asb48-1a781aa83": "chocolate"},
{"dc45n-jnsa9i-83ysg-81d4d7fae": "peanutButter"}],
"degreesToCook": 375,
"ingredients": {
"d4js95-1cc5-4asn-asb48-1a781aa83": [
"1nemw49-b9s88e-4750-bty0-bei8smr1eb",
"98h9nd8-3mo3-baef-2fe682n48d29"]
},
"numOfPiesBaked": 1,
"numberOfSlicesCreated": 6
}
I'm trying to extract the number of pies baked with a regex function in Tableau. Specifically, this one:
REGEXP_EXTRACT([Bakery Activity], '"numOfPiesBaked":"?([^\n,}]*)')
However, when I try to throw this calculated field into my text table, I get an error saying:
ERROR: function regexp_matches(jsonb, unknown) does not exist;
Error while executing the query
Worth noting is that my data source is PostgreSQL, which Tableau regex functions support; not all of my entries have numOfPiesBaked in them; when I run this in a simulator I get the correct extraction (actually, I get "numOfPiesBaked": 1" but removing the field name is a problem for another time).
What might be causing this error?
In short: Wrong data type, wrong function, wrong approach.
REGEXP_EXTRACT is obviously an abstraction layer of your client (Tableau), which is translated to regexp_matches() for Postgres. But that function expects text input. Since there is no assignment cast for jsonb -> text (for good reasons) you have to add an explicit cast to make it work, like:
SELECT regexp_matches("Bakery Activity"::text, '"numOfPiesBaked":"?([^\n,}]*)')
(The second argument can be an untyped string literal, Postgres function type resolution can defer the suitable data type text.)
Modern versions of Postgres also have regexp_match() returning a single row (unlike regexp_matches), which would seem like the better translation.
But regular expressions are the wrong approach to begin with.
Use the simple json/jsonb operator ->>:
SELECT "Bakery Activity"->>'numOfPiesBaked';
Returns '1' in your example.
If you know the value to be a valid integer, you can cast it right away:
SELECT ("Bakery Activity"->>'numOfPiesBaked')::int;
I found an easier way to handle JSONB data in Tableau.
Firstly, make a calculated field from the JSONB field and convert the field to a string by using str([FIELD_name]) command.
Then, on the calculated field, make another calculated field and use function:
REGEXP_EXTRACT([String_Field_Name], '"Key_to_be_extracted":"?([^\n,}]*)')
The required key-value pair will form the second caluculated field.

AnalysisException: Syntax error in line 1: error when taking modulus of a value using abs() in Impala

I want to take the modulus of a value when using Impala and I am aware of the abs() function. When I use this however like such
select abs(value) from table
It returns a value that is rounded to the nearest integer. The documentation found here states that I need to define the numeric_type. have tried this
select abs(float value) from table
but this gives me the following error
AnalysisException: Syntax error in line 1: ... abs(float value) from table ^ Encountered: FLOAT Expected: ALL, CASE, CAST, DEFAULT, DISTINCT, EXISTS, FALSE, IF, INTERVAL, NOT, NULL, TRUNCATE, TRUE, IDENTIFIER CAUSED BY: Exception: Syntax error
Any ideas how I set abs() to return a float?
This should work SELECT cast(Abs(-243.5) as float) AS AbsNum
I think you are misunderstanding the syntax. You call the function as abs(val). The return type is the same as the input type. It should work on integers, decimals, and floats.
If you want a particular type being returned, then you need to pass in that type, perhaps casting to the specific type.
The documentation is:
abs(numeric_type a)
Purpose: Returns the absolute value of the argument.
Return type: Same as the input value
Admittedly, this does look like the type should be part of the function call. But it is really using a programming language-style declaration to show the types that are expected.

BigQuery COALESCE/IFNULL type mismatch with literals

In SQL I usually use COALESCE and IFNULL to ensure that I get numbers and not NULL when my queries contain aggregate functions like COUNT and SUM, for example:
SELECT IFNULL(COUNT(foo), 0) AS foo_count FROM …
However, in BigQuery I run into an error:
Argument type mismatch in function IFNULL: 'f0_' is type uint64, '0' is type int32.
Is there a way to make BigQuery understand that a literal 0 should be interpreted as a unit64 in this context?
I've tried using CAST, but there's no unit64 type I can cast to, so I try INTEGER:
SELECT IFNULL(COUNT(foo), CAST(0 AS INTEGER)) AS foo_count FROM …
That gives me basically the same error, but at least I've successfully gotten a 64-bit zero instead of a 32-bit:
Argument type mismatch in function IFNULL: 'f0_' is type uint64, '0' is type int64.
The same happens if I use INTEGER(0).
I can get it to work if I cast both arguments to INTEGER:
SELECT IFNULL(INTEGER(COUNT(foo)), INTEGER(0)) AS foo_count FROM …
But now it starts to be verbose. Is this really how you're supposed to do it in BigQuery?
This is a bug in BigQuery which has been around for quite some time. For the time being you need to force the conversion of the COUNT, but you shouldn't need to do it for your "0".
The following should work:
SELECT IFNULL(INTEGER(COUNT(foo)), 0) AS foo_count FROM
Thanks #Kinaan Khan Sherwani for the link to the official bug report.

Postgres: define a default value for CAST failures?

Is it possible to define a default value that will be returned in case a CAST operation fails?
For example, so that:
SELECT CAST('foo' AS INTEGER)
Will return a default value instead of throwing an error?
There is no default value for a CAST:
A type cast specifies a conversion from one data type to another. PostgreSQL accepts two equivalent syntaxes for type casts:
CAST ( expression AS type )
expression::type
There is no room in the syntax for anything other than the expression to be casted and the desired target type.
However, you can do it by hand with a simple function:
create or replace function cast_to_int(text, integer) returns integer as $$
begin
return cast($1 as integer);
exception
when invalid_text_representation then
return $2;
end;
$$ language plpgsql immutable;
Then you can say things like cast_to_int('pancakes', 0) and get 0.
PostgreSQL also lets you create your own casts so you could do things like this:
create or replace function cast_to_int(text) returns integer as $$
begin
-- Note the double casting to avoid infinite recursion.
return cast($1::varchar as integer);
exception
when invalid_text_representation then
return 0;
end;
$$ language plpgsql immutable;
create cast (text as integer) with function cast_to_int(text);
Then you could say
select cast('pancakes'::text as integer)
and get 0 or you could say
select cast(some_text_column as integer) from t
and get 0 for the some_text_column values that aren't valid integers. If you wanted to cast varchars using this auto-defaulting cast then you'd have to double cast:
select cast(some_varchar::text as integer) from t
Just because you can do this doesn't make it a good idea. I don't think replacing the standard text to integer cast is the best idea ever. The above approach also requires you to leave the standard varchar to integer cast alone, you could get around that if you wanted to do the whole conversion yourself rather than lazily punting to the built in casting.
NULL handling is left as an (easy) exercise for the reader.
Trap the error as described in documentation and then specify an action to do instead.
Documentation on error trapping for PostgreSQL Snippet included below.
35.7.5. Trapping Errors
By default, any error occurring in a PL/pgSQL function aborts execution of the function, and indeed of the surrounding transaction as well. You can trap errors and recover from them by using a BEGIN block with an EXCEPTION clause. The syntax is an extension of the normal syntax for a BEGIN block:
[ <<label>> ]
[ DECLARE
declarations ]
BEGIN
statements
EXCEPTION
WHEN condition [ OR condition ... ] THEN
handler_statements
[ WHEN condition [ OR condition ... ] THEN
handler_statements
... ]
END;
If no error occurs, this form of block simply executes all the statements, and then control passes to the next statement after END. But if an error occurs within the statements, further processing of the statements is abandoned, and control passes to the EXCEPTION list. The list is searched for the first condition matching the error that occurred. If a match is found, the corresponding handler_statements are executed, and then control passes to the next statement after END. If no match is found, the error propagates out as though the EXCEPTION clause were not there at all: the error can be caught by an enclosing block with EXCEPTION, or if there is none it aborts processing of the function.