python's eval() in Amazon-Redshift: evaluating strings as expressions - eval

My strong conviction is that no solution for this exists, but I thought I'd ask anyways. My question is precisely this one:
Are there any way to execute a query inside the string value (like eval) in PostgreSQL?
But for Amazon Redshift, which does not support stored functions. Specifically, I believe my expressions can all be evaluated as Booleans with one or more expressions such as 'X <= 10' concatenated with ANDs and ORs, i.e.:
string = 'X <= 10 AND Y = 5 AND Z >= 0'
I can easily replace into the variables with the correct values but am trying not to resort to building my own parser. Anyone see a nice way to do this? Thanks!

You're in luck! Amazon Redshift recently introduced User Defined Functions that can call Python code.
Therefore, you can take advantage of the Python eval() command to evaluate a condition.
Here's some code that worked for me:
CREATE FUNCTION f_eval(condition TEXT)
RETURNS boolean
VOLATILE AS $$
return eval(condition)
$$ LANGUAGE plpythonu;
Then run it with:
SELECT f_eval('5 < 6 and 3 < 4');
This returns true in SQL.

Related

Unsupported feature '|'. At Statement.execute error in snowflake regular expression

create or replace procedure sp(path varchar)
returns varchar
language javascript
as
$$
var a="regexp_replace(PATH,'\\[\'\|\'\\]')";
var b="SELECT \n"+ a + " as path_name";
var c=snowflake.createStatement({sqlText:b});
var d=c.execute();
d.next();
return d.getColumnValue(1);
$$;
call sp('['Bank Country']');
This regular expression is working outside the procedure when I try to use it in the stored procedure it is displaying an error.
Execution error in store procedure SP: Unsupported feature '|'. A Statement. execute, line 6 position 8.
I am trying to do this with regular expression, if my string is in this format "['Bank Country']"--> "Bank Country" that means it has to strip off "['" and "']" in the string if "['" is at the first index position. If these special characters are present between the string, it has to do it in this way.
For Example, "Customer['Bank Details']" --> "Customer.Bank Details"
It has to strip off "['" and replace it with "." and remove the "']" in the string.
So given this is a carrying on of this question
You had valid SQL there.
select regexp_replace('[\'Customers NY\']','\\[\'|\'\\]','') as customername;
or
select translate('[\'Customers NY\']',$$[']$$,'');
So as Felipe notes: return early to check the SQL you have constructed:
create or replace procedure sp(path varchar)
returns varchar
language javascript
as
$$
var a="regexp_replace(PATH,'\\[\'\|\'\\]')";
var b="SELECT \n"+ a + " as path_name";
return b;
//var c=snowflake.createStatement({sqlText:b});
//var d=c.execute();
//d.next();
//return d.getColumnValue(1);
$$;
and call it:
call sp('[\'Bank Country\']');
gives:
SELECT regexp_replace(PATH,'\['|'\]') as path_name;
At this point there are two problems, you have not substituted PATH into your dynamic SQL. And you have lost some escaping.
I am assuming you are a student, so I will not give you a working solution, as that is your job to learn.
You current are substituting a into b, but PATH is not substituted into a
Your composed SQL is going to be parsed by the SQL parser, so needs to be valid SQL, thus the strings need to valid started/stopped, AND if you are going to use the same token like you have here it will need to be escaped, but the escapes also will need escaping to get past the parsing of the Stored Procedure.
So given I believe you are a student, and Felipie's "this can be done simpler out of a stored procedure" is correct it's simpler, if you have to do it "this way for the assignment", then the things you need to learn are the above points.
If you return b before createStatement, you will see exactly what kind of query this code is creating.
In this case it's SELECT regexp_replace(PATH,'\['|'\]') as path_name, which doesn't make sense.
Please start a new question explaining what's the goal of this function, sample inputs, sample desired outputs, and we can help with that.
In the meantime, a simple way to do what the last paragraph is asking for, without the need of an UDF:
select translate($$Customer['Bank Details']$$, $$[]'$$, '.')

What is regexp_replace equivalent in SQL Server

I have this piece of code in Oracle which I need to convert into SQL Server to get the same behavior. I have used the REPLACE function. It seems to be working but I just wanted to make sure.
REGEXP_REPLACE(
phonenumber,
'([[:digit:]]{3})([[:digit:]]{3})([[:digit:]]{4})',
'(\1)\2-\3'
) phonenumber
As Martin said in his answer, SQL Server does not have built-in RegEx functionality (and while it has not been suggested here, just to be clear: no, the [...] wildcard of LIKE and PATINDEX is not RegEx). If your data has little to no variation then yes, you can use some combination of T-SQL functions: REPLACE, SUBSTRING, LEFT, RIGHT, CHARINDEX, PATINDEX, FORMATMESSAGE, CONCAT, and maybe one or two others.
However, if the data / input has even a moderate level of complexity, then the built-in T-SQL functions will be at best be cumbersome, and at worst useless. In such cases it's possible to do actual RegEx via SQLCLR (as long as you aren't using Azure SQL Database Single DB or SQL Server 2017+ via AWS RDS), which is (restricted) .NET code running within SQL Server. You can either code your own / find examples here on S.O. or elsewhere, or try a pre-done library such as the one I created, SQL# (SQLsharp), the Free version of which contains several RegEx functions. Please note that SQLCLR, being .NET, is not a POSIX-based RegEx, and hence does not use POSIX character classes (meaning: you will need to use \d for "digits" instead of [:digit:]).
The level of complexity needed in this particular situation is unclear as the example code in the question implies that the data is simple and uniform (i.e. 1112223333) but the example data shown in a comment on the question appears to indicate that there might be dashes and/or spaces in the data (i.e. xxx- xxx xxxx).
If the data truly is uniform, then stick with the pure T-SQL solution provided by #MartinSmith. But, if the data is of sufficient complexity, then please consider the RegEx example below, using a SQLCLR function found in the Free version of my SQL# library (as mentioned earlier), that easily handles the 3 variations of input data and more:
SELECT SQL#.RegEx_Replace4k(tmp.phone,
N'\(?(\d{3})\)?[ .-]*(\d{3})[ .-]*(\d{4})', N'($1)$2-$3',
-1, -- count (-1 == unlimited)
1, -- start at
N'') -- RegEx options
FROM (VALUES (N'8885551212'),
(N'123- 456 7890'),
(N'(777) 555- 4653')
) tmp([phone]);
returns:
(888)555-1212
(123)456-7890
(777)555-4653
The RegEx pattern allows for:
0 or 1 (
3 decimal digits
0 or 1 )
0 or more of , ., or -
3 decimal digits
0 or more of , ., or -
4 decimal digits
NOTE
It was mentioned that the newer Language Extensions might be a better choice than SQLCLR. Language Extensions allow calling R / Python / Java code, hosted outside of SQL Server, via the sp_execute_external_script stored procedure. As the Tutorial: Search for a string using regular expressions (regex) in Java page shows, external scripts are actually not a good choice for many / most uses of RegEx in SQL Server. The main problems are:
Unlike with SQLCLR, the only interface for external scripts is a stored procedure. This means that you can't use any of that functionality inline in a query (SELECT, WHERE, etc).
With external scripts, you pass in the query, work on the results in the external language, and pass back a static result set. This means that compiled code now has to be more specialized (i.e. tightly-coupled) to the particular usage. Changing how the query uses RegEx and/or what columns are returned now requires editing, compiling, testing, and deploying the R / Python / Java code in addition to (and coordinated with!) the T-SQL changes.
I'm sure external scripts are absolutely wonderful, and a better choice than SQLCLR, in certain scenarios. But they certainly do not lend themselves well to the highly varied, and often ad hoc, nature of how RegEx is used (like many / most other functions).
SQL Server does not have native regex support. You would need to use CLR (or as #Lukasz Szozda points out in the comments one of the newer Language Extensions) .
If I have understood the regex correctly though it matches strings of 10 digits and assigns the first 3 to group 1, second 3 to group 2, and last 4 to group 3 and then uses the back references in the expression (\1)\2-\3
You can use built in string functions to do this as below
SELECT CASE
WHEN phonenumber LIKE REPLICATE('[0-9]', 10)
THEN FORMATMESSAGE('(%s)%s-%s',
LEFT(phonenumber, 3),
SUBSTRING(phonenumber, 4, 3),
RIGHT(phonenumber, 4))
ELSE phonenumber
END
You can write SQL function using CLR, that will wrap standard dotnet regex. I have wrote this and you can use it there. It will look this:
DECLARE #SourceText NVARCHAR(MAX) = N'My first line <br /> My second line';
DECLARE #RegexPattern NVARCHAR(MAX) = N'([<]br\s*/[>])';
DECLARE #Replacement NVARCHAR(MAX) = N''
DECLARE #IsCaseSensitive BIT = 0;
SELECT regex.Replace(#SourceText, #RegexPattern, #Replacement, #IsCaseSensitive);

Set local variable in rdlc expression

Is it possible to somehow create variable in rdlc report expression in 'online' manner?
For example, I have following expression:
=IIf(First(Fields!BillingAccount_billtostateprovince.Value, "Invoice") <> "",
First(Fields!BillingAccount_billtostateprovince.Value, "Invoice") + " ",
"")
I suppose that I'm evaluating following expression First(Fields!BillingAccount_billtostateprovince.Value, "Invoice") twice. I don't like it and it looks ugly... I would prefer to create variable in the scope of current expression and use it.
Is it possible?
As user3056839 said, Welcome to SSRS!
Anyway, what you want is not possible since what you are writing right now is not a script but it's just an expression. It's a single statement that returns a value, so you cannot declare variables, use loops or anything that is part of a script.
You have to use exactly what you are writing.
Also it's not rare to see an IIF expression like yours. The one I constantly see is IFF( IS NOT NULL, , 'N/A'). The field may actually be evaluated twice, but there's nothing you can do. It's ugly but it's the only way you can do that.
Just think about the CASE WHEN clause:
SELECT
CASE WHEN MyField IS NOT NULL THEN
MyField ELSE 0
END
You are evaluating the field twice, but there's nothing you can do :)
It is possible to do it in SQL Server 2008 and above. You can create a Report Variable which can be accessed through out your report.
Reference: sqlchick.com

Evaluate logical expressions in string column SQL

I have a table containing columns id(int), logical expression(varchar) and result(bit). The logical expression is stored in a varchar which I need to evaluate and put the result into the result column. For example, the column could contain:
'1=1'
'2<3 AND 1^1=1'
'3>4 OR 4<2'
The result column should then contain
1
0
0
Currently I am using a cursor to navigate the rows and using dynamic sql to evaluate the expression.
"IF(" + #expression + ") SET #result = 1"
Is there a better, more efficient way to do this? I would ideally like to get rid of the cursor. Any ideas? Would this be better performed using an assembly?
I'd go with a CLR.
I posted a very similar answer here: Convert string with expression to decimal
infact, the above answer would work fine unmodified for (and any other simple expressions):
SELECT dbo.eval('1=1' )
SELECT dbo.eval('3>4 OR 4<2' )
However, it would fail for the one using the ^ (caret) operator - you would need to tweak the CLR to handle the bitwise XOR.
Some time ago, I wrote a user-defined function in SQL to give the decimal result of evaluating infix arithmetic expressions like 1+2+3+4/(5-2). The code is here. You could probably adapt it to work for your boolean expressions. It uses a table of integers called Sequence0_8000, which you can populate in any way you want.

How to declare local variables in postgresql?

There is an almost identical, but not really answered question here.
I am migrating an application from MS SQL Server to PostgreSQL. In many places in code I use local variables so I would like to go for the change that requires less work, so could you please tell me which is the best way to translate the following code?
-- MS SQL Syntax: declare 2 variables, assign value and return the sum of the two
declare #One integer = 1
declare #Two integer = 2
select #One + #Two as SUM
this returns:
SUM
-----------
3
(1 row(s) affected)
I will use Postgresql 8.4 or even 9.0 if it contains significant fetaures that will simplify the translation.
Postgresql historically doesn't support procedural code at the command level - only within functions. However, in Postgresql 9, support has been added to execute an inline code block that effectively supports something like this, although the syntax is perhaps a bit odd, and there are many restrictions compared to what you can do with SQL Server. Notably, the inline code block can't return a result set, so can't be used for what you outline above.
In general, if you want to write some procedural code and have it return a result, you need to put it inside a function. For example:
CREATE OR REPLACE FUNCTION somefuncname() RETURNS int LANGUAGE plpgsql AS $$
DECLARE
one int;
two int;
BEGIN
one := 1;
two := 2;
RETURN one + two;
END
$$;
SELECT somefuncname();
The PostgreSQL wire protocol doesn't, as far as I know, allow for things like a command returning multiple result sets. So you can't simply map T-SQL batches or stored procedures to PostgreSQL functions.

Resources