Expression Evaluation in SQL Stored Procedure - sql-server

I have formula table with some of the formula. Is it possible to write SQL stored procedure which could evaluate every formula and give the result ? Every formula is an expression which could have all different arithmetic operations. Every value used in formula is an id value referring to some other table
FormulaId | Formula
--------------+-------------
1 | `1 * 3`
2 | `(2 + 3) * (4 + 1)`
3 | `((2 + 3) * (4 + 1)) / 5`
4 | `(4 + 1) - (3 + 1) - (2 + 1)`
Id | Value
--------------+-------------
1 | 5
2 | 10
3 | 15
4 | 20
5 | 25
Result should be something like
FormulaId | EvaluatedValue
--------------+----------------
1 | 75
2 | 625
3 | 25
4 | -10

I'd strongly recommend doing this in your application rather than SQL. I have a solution, but it's ridiculously involved. I would create a proc which returns the formula, plus the values that need to be replaced. Then use .Replace() or a regex library to replace the tags with the values (Note, you're going to run into trouble if your tags are integers; consider wrapping them in something like some kind of bracket). You can then use one of several techniques to evaluate the expression at run time (see Evaluating string "3*(4+2)" yield int 18 for some ideas).
Note, I listed stuff primarily in C#, since that's what I know; but if you're using a different language, there should be something equivalent.

Here is how you can evaluate an expression stored in a table:
declare #SQL nvarchar(max)
Declare #Result As Int
Select #SQL = Formula
From dbo.Formulae
Where ID = 3
Set #SQL = 'select #x = ' + #SQL
exec sp_executesql #SQL, N'#x int out', #Result out
select #Result
Assuming that Xendi and Prasad are correct and the numbers in the Formula entry are keys used to look up values from the Values table, then you'll have to define the delimiters used in your formula strings and then write a routine to parse the formulae and do the substitution. Then you'd feed the resulting string into the logic I wrote above.
All that being said, TSQL is not the right tool for this job.

Related

Find first appearance of a character in a set of possible characters in a string in SQL Server 2012

I'm aware of the SQL Server CHARINDEX function which returns the position of a character (or sub-string) within another string. Still, I did not find any evident that there is support for regular expressions (unless I develop my own UDF).
What I'm looking for is the ability to find the first position of any character in a set within a string.
Example:
DECLARE #_Source_String NVARCHAR(100) = 'This is "MY" string \ and here is more text' ;
SELECT <some function> (#_Source_String,'"\') ;
This should return 9 because " appears before \. On the other hand:
SELECT <some function> (#_Source_String,'x\') ;
should return 21 because \ is before x.
I should add that performance is very important since this function/mechanism will be invoked with very high frequency.
Pattern matching capabilities in TSQL are pretty basic and often you would require CLR and regular expressions.
You can do this requirement with PATINDEX though. A list of characters in square brackets denotes a set of characters to match.
DECLARE #_Source_String NVARCHAR(100) = 'This is "MY" string \ and here is more text';
SELECT PATINDEX('%["\]%', #_Source_String),
PATINDEX('%[x\]%', #_Source_String);
Returns
+------------------+------------------+
| (No column name) | (No column name) |
+------------------+------------------+
| 9 | 21 |
+------------------+------------------+

LTRIM and RTRIM Truncating Floating Point Number

I am experiencing what I would describe as entirely unexpected behaviour when I pass a float value through either LTRIM or RTRIM:
CREATE TABLE MyTable
(MyCol float null)
INSERT MyTable
values (11.7333335876465)
SELECT MyCol,
RTRIM(LTRIM(MyCol)) lr,
LTRIM(MyCol) l,
RTRIM(MyCol) r
FROM MyTable
Which gives the following results:
MyCol | lr | l | r
--------------------------------------------
11.7333335876465 | 11.7333 | 11.7333 | 11.7333
I have observed the same behaviour on SQL Server 2014 and 2016.
Now, my understanding is that LTRIM and RTRIM should just strip off white space from a value - not cast it/truncate it.
Does anyone have an idea what is going on here?
Just to explain the background to this. I am generating SQL queries using the properties of a set of C# POCOs (the results will be used to generate an MD5 hash that will then be compared to an equivalent value from an Oracle table) and for convenience was wrapping every column with LTRIM/RTRIM.
Perhaps you can use format() instead
Declare #F float = 11.7333335876465
Select format(#F,'#.##############')
Returns
11.7333335876465

TSQL search exact match into a string

I stumbling on an issue with string parsing; what I'm trying to achieve is substitute a marker string with a value but the string match needs to be perfect.
Keep in mind that before the compare I split the entire string in a table (rowID int, segment nvarchar(max)) wherever i find a space so, a thing like 'The delta_s is §delta_s' will look like:
rowID | segment
1 | the
2 | deltaT_s
3 | is
4 | §deltaT_s
After this i cycle each row with my table of "replacements" (idString nvarchar(max), val float); example:
Marker string (#segment): '§deltaT_s'
String to replace (#idString): '§deltaT_s'
The instruction I am using (since "like" is a lost cause as far I can see):
SELECT STUFF(#segment, PATINDEX('%'+#idString+'[^a-z]%', #segment), LEN(#idString), CAST(#val AS NVARCHAR(MAX)))
with #val being the number to substitute taken from the "replacements" table.
Now, in my table of "replacements" i have 2 delta like markers
1) §deltaT_s
2) §deltaT
My issue is that when the cycle start comparing the segments with the markers and the §deltaT comes up it will substitute the first part of the string in this way
'§deltaT_s' -> '10_s'
I don't understand what I am doing wrong with the REGEX anyone can give me and hand on this matter?
I am available in case more info are required.
Thank you,
F.
If possible you should change the marking style putting a paragraph symbol (§) at both side of the token, making one of the example in your comment
the deltaT_s is §deltaT_s§, see ya!
doing that the sentence will be split as
rowID | segment
--------------------
1 | the
2 | deltaT_s
3 | is
4 | §deltaT_s§,
5 | see
6 | ya!
if the replace values are stored in a fact table you will have something like
token | value
------------------
§deltaT§ | foo
§deltaT_s§ | 10
or you can fake it putting the symbol at the end of the token in you query.
Than it's possible to search for the substitution with a LIKE and a LEFT JOIN between the two tables
SELECT COALESCE(REPLACE(segment, t.token, t.value), segment) Replaced
FROM Sentence s
LEFT JOIN Token t ON s.segment LIKE '%' + t.token + '%'
SQLFiddle demo
If you cannot change the fact table you can fake the change adding the symbol after the token
SELECT COALESCE(REPLACE(segment, t.token, t.value), segment) Replaced
FROM Sentence s
LEFT JOIN Token t ON s.segment LIKE '%' + t.token + '§%'
Maybe it is not an option, but for me helped ones.
If you can use Regex in sql or create CLR functions, look at this link http://www.sqllion.com/2010/12/pattern-matching-regex-in-t-sql/ last 2 options.
For you the best will be to take last choice using CLR function.
Then you will can do like this:
Text: the deltaT_s is §delta, see ya!
Regex: (?<=[^a-z])§delta(?![a-z_]) - this (?<=[^a-z]) means that will not take to match and (?![a-z_]) is not followed by letters and underline.
Replace to : 10
I also have tried regex \b§delta\b (\b :Start or End of word), but it seems it doesn't like §

creating a histogram using SQL

I am extremely new to SQL and wish to create a histogram which should look as the following - 2 columns, one with "z" values, ranging from 0 to 1, with an interval of 0.01, and the second which will include the number of count(z) per each interval. Visually, it should look something like this:
z | count(z)
-------------
0-0.01| 12312
0.01 - 0.02 | 143565
0.02 - 0.03 | 23445
and so on...
I tried looping, concatinating string and using EXEC, but nothing seems to work :(
the closest I've got to extracting some useful data was by using the following code, which produced a 2D matrix with the first column containing the data and the rest NULL:
DECLARE #i float = 0
WHILE #i < 0.1
BEGIN
exec ('select count(z) as ''' +#i +''' from specObj where z BETWEEN
' +#i +' and (' +#i +'+0.01)')
SET #i = #i + 0.01
END
Thanks
There is no need to loop. Just use some arithmetic in the group by. Here is the basic idea:
select cast(z * 100 as int)/100.0 as range_start, (1 + cast(z * 100 as int))/100.0 as range_end,
count(*)
from table t
group by cast(z * 100 as int);
Actually turning the range start and range end into a string (such as '[0.01-0.02]') requires string manipulations that, alas, depend on the particular database, which is not specified in the question.

How to retrieve multiple values when more than one string matches with TSQL?

Consider the a table that contains
ReturnValueID | ReturnValue TriggerValue
------------------------------------------
1 | returnValue1 | testvalue
2 | returnValue2 | testing...
3 | returnValue3 | value3
And given a string: HERE IS THE TEXT testing... AND MORE TEXT testvalue MORE TEXT
I have written a CTE using SQL Server 2008 that uses a FindInString() function I wrote to indicate where the matched text is found. 0 = not found:
1 | returnValue1 | 43
2 | returnValue2 | 18
3 | returnValue3 | 0
What I need to do now, is iterate through this result set in a loop where I will perform some additional logic based on each row.
I have seen a few examples of looping, but I would rather not use a cursor.
What is the best way to approach this?
Thanks.
-- UPDATE --
Once a match is made, the ID of the matched row is added to a table, if it doesn't already exist, then the return value is appended to an VARCHAR value, if it doesn't already exist in the dynamic string:
IF NOT EXISTS -- check if this value is already recorded
(
SELECT *
FROM RecordedReturnValue
WHERE ReturnValueID = #ReturnValueID
)
BEGIN
-- add the visitor/external tag ID to historical table
INSERT INTO RecordedReturnValue (...)
VALUES (...)
-- function checks if string is already present
SET #DynamicString = dbo.AppendDynamicOutput(#ReturnValue, #DynamicString)
END
This must be performed for each matched TriggerValue from the CTE.
Ended up using a CTE, added the values to a temp table, then iterated through the results and performed some logic.

Resources