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.
Related
I'm not sure how to put it into words. I have this select in a ms sql table
select * from nomencl where Denumire='NGT2-65/201-32/1.3-72(DO) BMT 65 ,radial tool holder, DOOSAN'
and it returns nothing.
But if I use :
select * from nomencl where Denumire LIKE 'NGT2-65/201-32/1.3-72(DO) BMT 65 ,radial tool holder, DOOSAN%'
the record is there.
I'm not very skilled with ms sql, but I need to make it work. What could be the problem? Is it something in that string? I've searched for spaces at the end of the string but still found nothing.
It's because there is no row where Denumire equals 'NGT2-65/201-32/1.3-72(DO) BMT 65 ,radial tool holder, DOOSAN'. However, there is a row where Denumire starts with that value. The % on the end with LIKE denotes this.
LIKE is the ANSI/ISO standard operator for comparing a column value to another column value, or to a quoted string.
It returns either 1 (TRUE) or 0 (FALSE).
The equals to(=) operator is a comparison operator and used for equality test within two numbers or expressions.
LIKE is generally used only with strings however equals (=) is used for exact matching and it seems faster.
I'm trying to get rows where a column of type text[] contains a value similar to some user input.
What I've thought and done so far is to use the 'ANY' and 'LIKE' operator like this:
select * from someTable where '%someInput%' LIKE ANY(someColum);
But it doesn't work. The query returns the same values as that this query:
select * from someTable where 'someInput' = ANY(someColum);
I've got good a result using the unnest() function in a subquery but I need to query this in WHERE clause if possible.
Why doesn't the LIKE operator work with the ANY operator and I don't get any errors? I thought that one reason should be that ANY operator is in the right-hand of query, but ...
Is there any solution to this without using unnest() and if it is possible in WHERE clause?
It's also important to understand that ANY is not an operator but an SQL construct that can only be used to the right of an operator. More:
How to use ANY instead of IN in a WHERE clause with Rails?
The LIKE operator - or more precisely: expression, that is rewritten with to the ~~ operator in Postgres internally - expects the value to the left and the pattern to the right. There is no COMMUTATOR for this operator (like there is for the simple equality operator =) so Postgres cannot flip operands around.
Your attempt:
select * from someTable where '%someInput%' LIKE ANY(someColum);
has flipped left and right operand so '%someInput%' is the value and elements of the array column someColum are taken to be patterns (which is not what you want).
It would have to be ANY(someColum) LIKE '%someInput%' - except that's not possible with the ANY construct which is only allowed to the right of an operator. You are hitting a road block here.
Related:
Is there a way to usefully index a text column containing regex patterns?
Can PostgreSQL index array columns?
You can normalize your relational design and save elements of the array in separate rows in a separate table. Barring that, unnest() is the solution, as you already found yourself. But while you are only interested in the existence of at least one matching element, an EXISTS subquery will be most efficient while avoiding duplicates in the result - Postgres can stop the search as soon as the first match is found:
SELECT *
FROM tbl
WHERE EXISTS (
SELECT -- can be empty
FROM unnest(someColum) elem
WHERE elem LIKE '%someInput%'
);
You may want to escape special character in someInput. See:
Escape function for regular expression or LIKE patterns
Careful with the negation (NOT LIKE ALL (...)) when NULL can be involved:
Check if NULL exists in Postgres array
An admittedly imperfect possibility might be to use ARRAY_TO_STRING, then use LIKE against the result. For example:
SELECT *
FROM someTable
WHERE ARRAY_TO_STRING(someColum, '||') LIKE '%someInput%';
This approach is potentially problematic, though, because someone could search over two array elements if they discover the joining character sequence. For example, an array of {'Hi','Mom'}, connected with || would return a result if the user had entered i||M in place of someInput. Instead, the expectation would probably be that there would be no result in that case since neither Hi nor Mom individually contain the i||M sequence of characters.
My question was marked duplicate and linked to a question out of context by a careless mod. This question comes closest to what I asked so I leave my answer here. (I think it may help people for who unnest() would be a solution)
In my case a combination of DISTINCT and unnest() was the solution:
SELECT DISTINCT ON (id_) *
FROM (
SELECT unnest(tags) tag, *
FROM someTable
) x
WHERE (tag like '%someInput%');
unnest(tags) expands the text array to a list of rows and DISTINCT ON (id_) removes the duplicates that result from the expansion, based on a unique id_ column.
Update
Another way to do this without DISTINCT within the WHERE clause would be:
SELECT *
FROM someTable
WHERE (
0 < (
SELECT COUNT(*)
FROM unnest(tags) AS tag
WHERE tag LIKE '%someInput%'
)
);
Please check this out.
This answer was exactly what I was looking for. It also provides for some useful tips (and examples) in case you need more flexibility.
It basically explains the ANY(), the #> and the && operators.
"If you want to search multiple values, you can use #> operator"
"#> means contains all the values in that array. If you want to search if the current array contains any values in another array, you can use &&"
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.
Is there any way/use of putting pipe symbol || in select clause.
I have come across following query in one of the article(probably to concatenate two values), but when I try to use the same in my query I am getting syntax error.
select FirstName ||''|| LastName As CustomerName from Customer
Please correct if I am using wrong syntax.
You can use CONCAT() function, which works in SQL Server 2012 and above, or just a plain + sign to do concatenation.
https://msdn.microsoft.com/en-us/library/hh231515(v=sql.110).aspx
Returns a string that is the result of concatenating two or more
string values.
you need to use '+' to perform Concat() instead of pipe if you are using SQL-Server. Pipe operator is not used in SQL-Server
It is used to concatenate you columns and output a single result i.e in one column.
For example, if i want to see first name and last name together as in one column then i could use pipes:
SELECT Fname||Lname FROM my_table;
If you are asking whether you can use pipes || for concatenation in Microsoft SQL, then the short answer is no.
If you’re asking about the concatenation operator itself, then read on.
|| is the standard ANSI concatenation operator. This is apparent in PostgreSQL, SQLite and Oracle, among others.
Microsoft, however uses +, because, why not. Except Microsoft Access uses &, because, why not.
MariaDB/MySQL have two modes. In traditional mode, || is interpreted as “or”, and there is no concatenation operator. In ANSI mode, || is interpreted as the concatenation operator.
Most DBMS (not SQLite) have the non-standard concat() function which will also concatenate. They also coalesce any NULLs to empty strings, so they’re a bit more forgiving if you don’t care about NULLs.
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