I have a table in which all entries are in form of arithmetic formulas (i.e. '0.51 + 2.50 + 3.50').
In this table all columns are of type varchar. The table has many columns like this.
I want to calculate formula within a function and use it for some other calculation, but my problem is "EXEC" can not be used in side function and i can not take function within Stored procedure.
So, Any suggestions on how to achieve this?
Disclaimer: I'm the owner of the project Eval SQL.NET
Instead to create your own SQL CLR as suggested, you can use an existing one which allow you to esily write C# syntax in T-SQL.
-- SELECT 6.51
SELECT SQLNET::New('0.51 + 2.50 + 3.50').Eval()
You can even use column as formula and parameter
-- Evaluate dynamically expression in T-SQL
DECLARE #tableFormula TABLE (
Formula VARCHAR(255), X INT, Y INT, Z INT
)
INSERT INTO #tableFormula VALUES ('x+y*z', 1, 2, 3 ),
('(x+y)*z', 1, 2, 3 )
-- SELECT 7
-- SELECT 9
SELECT SQLNET::New(Formula)
.Val('x', X)
.Val('y', Y)
.Val('z', Z).EvalInt()
FROM #tableFormula
Documentation: Dynamically evaluating arithmetic operation in T-SQL
Related
If an array is provided as an input to a normally scalar argument of some functions like:
INDEX
GCD
GOOGLETRANSLATE
OFFSET
QUERY
IMPORTRANGE
AND
It doesn't return an array (even if wrapped by ARRAYFORMULA); it only gets the value for the first item in the array. Take this example,
Key
Value
1
A
2
B
3
C
4
D
5
E
6
F
7
G
I want to get row 1 and row 5's Value. I tried
=INDEX(A2:B16,{1;5},2)
Syntax for INDEX is INDEX(array, row,column). When a array is provided as a row argument, It only returns the value for first item, 1 instead of returning the value for {1;5}.
Actual output
Expected output
A
A
E
How to fix?
To achieve the result you're looking for, you can use BYROW to supply the argument once per array:
=BYROW({1;5},LAMBDA(row,INDEX(A2:B16,row,2)))
BYROW sends the array argument provided once per row to the function inside LAMBDA. As a general formula,
=BYROW(range, LAMBDA(row, your_formula(row)))
If you want to send two arguments, use MAP instead.
=MAP({1;5},{1;2},LAMBDA(arr_1,arr_2,INDEX(A2:B16,arr_1,arr_2)))
This will get row 1, column 1 and row 5, column 2 respectively.
Actual output
1
E
MAP supports unlimited number of arguments and therefore can be used with complex formulas.
Caveat: Only one value per function can be returned. If you want to return more values, use SPLIT/TRANSPOSE/SPLIT technique mentioned here
I'd like to insert 2 column wide fields under each other. I tried with embedded arrays but was not successful.
So basically from:
a 1 e 5
b 2 f 6
c 3
I would like to get:
a 1
b 2
c 3
e 5
f 6
I tried with
={{A:A,B:B};{C:C,D:D}}
but could not get it working, however
={{A:A,B:B},{C:C,D:D}}
put the columns the same as they were so its intresting that with ; its not working.
The blocks are always 2 column wide but the rows are different length
Thanks for your help in advance!
Try:
=filter({A:B;C:D},{A:A;C:C}<>"")
This will return rows where Columns A or C are not blank.
Just under c assuming a is in A1:
=ArrayFormula(C1:D2)
You're not going to find a clean built-in formulaic solution to this one that doesn't utilize some sort of built-in magic auto expansion (like pnuts's answer). Here is my approach using OFFSET that will also work in Microsoft Excel.
In two columns, copy this formula.
=OFFSET($A$1,(ROW()-ROW($G$1))/2,IF(MOD(ROW()-ROW($G$1),2)=1,2,0))
In the second column, modify the formula, adding 1 to the column offset parameter:
=OFFSET($A$1,(ROW()-ROW($G$1))/2,1+IF(MOD(ROW()-ROW($G$1),2)=1,2,0))
where $A$1 is replaced with the address of the top left of your range and $G$1 is the starting location of your output range. This should be resistant to auto-update of formulas from range insertions and deletions (which I despise butchering my formulae and conditional formatting rules) by using only the bare number of references, which are all absolute.
This works by dividing the row offset from your starting position by 2 and rounding down (via an implicit cast to integer when used as a parameter to the OFFSET function) to get the row number of your input range. Then it shifts over 2 columns on every odd row to get data from the second column pair.
Note this is not a size-aware function, so it interweaves the second column pair:
a 1
e 5
b 2
f 6
c 3
Suppose I've got a text values column (named Data), generated by =unique() function. Also, there is an array of patterns to find and replace for (Find and Replace columns).
Which formula should I use to scan each cell in Data for multiple patterns in Find and replace it, if match?
Data Find Replace Result
1 a c z a
2 b f y b
3 c e x z
4 d d
5 e x
6 c z
Tried =SUBSTITUTE() and =IF() functions, but it fails, when I set an array of patterns, instead of single one.
If the table you is in range A1:E7, try this formula
=TRANSPOSE(SPLIT(REGEXREPLACE(REGEXREPLACE(REGEXREPLACE(ARRAYFORMULA(CONCATENATE($B$2:$B$7&"|")),$C$2,$D$2),$C$3,$D$3),$C$4,$D$4),"|"))
You can read further about this in an older post and google docs forum.
Array solution
This formula takes range to replace, so it can be used for variable number of patterns:
=QUERY(ARRAYFORMULA({REGEXMATCH(A2,$B$2:$B$4),
REGEXREPLACE(A2,$B$2:$B$4,$C$2:$C$4)}),
"select Col2 order by Col1 desc limit 1")
or this one:
=INDEX(ArrayFormula(REGEXREPLACE(A2,$B$2:$B$4,$C$2:$C$4)),IFERROR(MATCH(A2,$B$2:$B$4,0),1))
or this:
=IFERROR(INDEX($C$2:$C$4,MATCH(A2,$B$2:$B$4,0)),A2)
The formula is need to be dragged down.
Single Formula & Array solution
Also this single ArrayFormula will do the trick:
=ArrayFormula(trim(transpose(query({IF(--REGEXMATCH(TRANSPOSE(A2:A7),$B$2:$B$4)=1,
REGEXREPLACE(TRANSPOSE(A2:A7),$B$2:$B$4,$C$2:$C$4),"");
TRANSPOSE(if(--not(REGEXMATCH(A2:A7,JOIN("|",B2:B4))),A2:A7,""))},,COUNTA(A2:A)))))
or this shorter formula:
=ArrayFormula(IFERROR(VLOOKUP(MATCH(A2:A7,B2:B4,0),{ROW(INDIRECT("a1:a"&COUNTA(C2:C4))),C2:C4},2,0),A2:A7))
Please, see explanations in Sample file
How can I concatenate two char columns within a perform screen?
example:
table
sample
col1
char(1)
col2
char(1)
after edit/add of sample
let label_3 = sample.col1 + sample.col2
.. this didn't work, I even tried using subscripts for the 2 cols but no dice!
There isn't a simple way to do it. Your closest approach would be a custom C function to do the concatenation:
LET label_3 = CONCATENATE(sample.col1, sample.col2)
That, of course, relies on you having a custom Perform runner with a concatenate function added to it.
Perform pre-dates the addition of the '||' string concatenation operator into SQL and does not support it.
The alternative is to use an Informix 4GL (I4GL) program instead. You can do a lot of things in I4GL that you cannot do in ISQL - at the cost of writing the code.
I have a Google Spreadsheet with 3 columns that are either blank or have a value. I want to get the count of the number of rows that has A and either B or C populated. If I were writing a SQL query it would be
select count(*)
from Table
where A is not null and (B is not null or C is not null)
But I can't for the life of me figure out how to get this in a Google Spreadsheet
The formula below should do what you are after:
=ROWS(FILTER(A2:A, NOT(ISBLANK(A2:A)), NOT(ISBLANK(B2:B))+NOT(ISBLANK(C2:C)) ))
And to explain:
ROWS counts the rows of the argument (filtered, in our case)
FILTER returns the rows of arg1 (A2:A) that all subsequent arguments match
The + (addition) symbol combines two predicates with a logical OR
Finally, if you are not using header columns you can change the references from A2:A to A:A
Alternatively, you can use the QUERY function:
(Broken into multiple lines for readability)
=ROWS(QUERY(A2:C,
"SELECT A WHERE A IS NOT NULL AND (B IS NOT NULL OR C IS NOT NULL)"))
For more information on the syntax of the queries, see the Visualization API Query Language Reference and specifically the Language Reference
=SUMPRODUCT(((A:A<>"")*((B:B<>"")+(C:C<>"")))>0)
if there is only one argument for SUMPRODUCT() it works just as SUM(ARRAYFORMULA(N( )))