I am new to crystal reports.
My data(employee ids) is of the following format
Abc123, uttd333, ddt-435
I want to extract only numbers and remove leading letters and special characters.
Also there are certain values that should never be printed.
Admin Ids such as
Gree999, ttt999
I know there is a mid function but that requires me to specify the position from where the substring should begin. These values don't have a fixed number of leading letters.
Is there anything like Ltrim like we have in SQL that we can use to achieve this in crystal reports?
Afaik there's no built-in function in CR to remove non-numeric characters from a string, so you'll have to roll your own (replace {Befehl.EmployeeId} with your field):
StringVar employeeId := {Befehl.EmployeeId};
StringVar result := "";
NumberVar i;
// transfer numeric chars into result one by one
For i := 1 To Length(employeeId) Do
(
If IsNumeric(employeeId[i]) Then
result := result + employeeId[i];
);
// output result only if employeeId is not in your admin list
If employeeId In Split('Gree999,ttt999', ',') Then
'--Admin--'
Else
result;
Here's an alternative approach to grabbing just the numeric portion of the string:
strReverse(ToText(Val(strReverse({EmpId})),0,""))
It takes advantage of knowing that the digits are always at the end.
To handle trailing zeros, you can use:
local stringvar withoutTrailingZeros := strReverse(ToText(Val(strReverse({EmpId})),0,""));
withoutTrailingZeros + ReplicateString("0", Len({EmpId}) - (instr({EmpId}, withoutTrailingZeros) + Len(withoutTrailingZeros) - 1))
But mweber's answer above is simpler now.
Related
How can I find all possible concatenations of characters in a table [ in a table ] and display them as strings with new lines?
Example, I have a array of tables with characters:
c={
{'1','2','3'},
{'a','b','c'},
{'A','B','C'}
}
I want to find all possible concatenations like
1aA
1aB
1aC
-- second table second character
1bA
1bB
1bC
-- second table third character
1cA
1cB
1cC
-- first table second character
2aA
2aB
2aC
-- and so on...
There could be many tables with however much characters. Appreciate the help!
EDIT:
I've found my answer here,
https://forum.cheatengine.org/viewtopic.php?p=5773931#5773931
This algorithm assumes that there are always an equal number of values in each row, with that assumption we can say you have (#c[1])^(#c) values and use this information to flatten the tables.
c = {
{'1','2','3'},
{'a','b','c'},
{'A','B','C'},
}
local columns = #c[1]
local rows = #c
for i = 0, (columns)^(rows) - 1 do
local output = ""
for k, v in ipairs(c) do
output = output .. v[i % ((columns)^k) // ((columns)^(k - 1)) + 1]
end
print(output)
end
Output:
1aA
2aA
3aA
1bA
2bA
3bA
1cA
2cA
3cA
1aB
2aB
3aB
1bB
2bB
3bB
1cB
2cB
3cB
1aC
2aC
3aC
1bC
2bC
3bC
1cC
2cC
3cC
I would like to check if a specific column in one of my tables meets the following conditions:
String must contain at least three characters
String must contain at least two different numbers [e.g. 123 would work but 111 would not]
Characters which are allowed in the string:
Numbers (0-9)
Uppercase letters
Lowercase letters
Underscores (_)]
Dashes (-)
I have some experience with Regex but am having issues with Snowflake's syntax. Whenever I try using the '?' regex character (to mark something as optional) I receive an error. Can someone help me understand a workaround and provide a solution?
What I have so far:
SELECT string,
LENGTH(string) AS length
FROM tbl
WHERE REGEXP_LIKE(string,'^[0-9]+{3,}[-+]?[A-Z]?[a-z]?$')
ORDER BY length;
Thanks!
Your regex looks a little confusing and invalid, and it doesn't look like it quite meets your needs either. I read this expression as a string that:
Must start with one or more digits, at least 3 or more times
The confusing part to me is the '+' is a quantifier, which is not quantifiable with {3,} but somehow doesn't produce an error for me
Optionally followed by either a dash or plus sign
Followed by an uppercase character zero or one times (giving back as needed)
Followed by and ending with a lowercase character zero or one times (giving back as needed)
Questions
You say that your string must contain 3 characters and at least 2 different numbers, numbers are characters but I'm not sure if you mean 3 letters...
Are you considering the numbers to be characters?
Does the order of the characters matter?
Can you provide an example of the error you are receiving?
Notes
Checking for a second digit that is not the same as the first involves the concept of a lookahead with a backreference. Snowflake does not support backreferences.
One thing about pattern matching with regular expressions is that order makes a difference. If order is not of importance to you, then you'll have multiple patterns to match against.
Example
Below is how you can test each part of your requirements individually. I've included a few regexp_substr functions to show how extraction can work to check if something exists again.
Uncomment the WHERE clause to see the dataset filtered. The filters are written as expressions so you can remove any/all of the regexp_* columns.
select randstr(36,random(123)) as r_string
,length(r_string) AS length
,regexp_like(r_string,'^[0-9]+{3,}[-+]?[A-Z]?[a-z]?$') as reg
,regexp_like(r_string,'.*[A-Za-z]{3,}.*') as has_3_consecutive_letters
,regexp_like(r_string,'.*\\d+.*\\d+.*') as has_2_digits
,regexp_substr(r_string,'(\\d)',1,1) as first_digit
,regexp_substr(r_string,'(\\d)',1,2) as second_digit
,first_digit <> second_digit as digits_1st_not_equal_2nd
,not(regexp_instr(r_string,regexp_substr(r_string,'(\\d)',1,1),1,2)) as first_digit_does_not_appear_again
,has_3_consecutive_letters and has_2_digits and first_digit_does_not_appear_again as test
from table(generator(rowcount => 10))
//where regexp_like(r_string,'.*[A-Za-z]{3,}.*') // has_3_consecutive_letters
// and regexp_like(r_string,'.*\\d+.*\\d+.*') // has_2_digits
// and not(regexp_instr(r_string,regexp_substr(r_string,'(\\d)',1,1),1,2)) // first_digit_does_not_appear_again
;
Assuming the digits need to be contiguous, you can use a javascript UDF to find the number in a string with with the largest number of distinct digits:
create or replace function f(S text)
returns float
language javascript
returns null on null input
as
$$
const m = S.match(/\d+/g)
if (!m) return 0
const lengths = m.map(m=> [...new Set (m.split(''))].length)
const max_length = lengths.reduce((a,b) => Math.max(a,b))
return max_length
$$
;
Combined with WHERE-clause, this does what you want, I believe:
select column1, f(column1) max_length
from t
where max_length>1 and length(column1)>2 and column1 rlike '[\\w\\d-]+';
Yielding:
COLUMN1 | MAX_LENGTH
------------------------+-----------
abc123def567ghi1111_123 | 3
123 | 3
111222 | 2
Assuming this input:
create or replace table t as
select * from values ('abc123def567ghi1111_123'), ('xyz111asdf'), ('123'), ('111222'), ('abc 111111111 abc'), ('12'), ('asdf'), ('123 456'), (null);
The function is even simpler if the digits don't have to be contiguous (i.e. count the distinct digits in a string). Then core logic changes to:
const m = S.match(/\d/g)
if (!m) return 0
const length = [...new Set (m)].length
return length
Hope that's helpful!
I have a database that has multiple columns populated with various numeric fields. While trying to populate from a CSV, I must have mucked up assigning delimited fields. The end result is a column containing It's Correct information, but also contains the next column over's data- seperated by a comma.
So instead of Column UPC1 containing "958634", it contains "958634,95877456". The "95877456" is supposed to be in the UPC2 column, instead UPC2 is NULL.
Is there a way for me to split on the comma and send the data to UPC2 while keeping UPC1 data before the comma in tact?
Thanks.
You can do this with string functions. To query the values and verify the logic, try this:
SELECT
LEFT(UPC1, CHARINDEX(',', UPC1) - 1),
SUBSTRING(UPC1, CHARINDEX(',', UPC1) + 1, 1000)
FROM myTable;
If the result is what you want, turn it into an update:
UPDATE myTable SET
UPC1 = LEFT(UPC1, CHARINDEX(',', UPC1) - 1),
UPC2 = SUBSTRING(UPC1, CHARINDEX(',', UPC1) + 1, 1000);
The expression for UPC1 takes the left side of UPC1 up to one character before the comma.
The expression for UPC2 takes the remainder of the UPC1 string starting one character after the comma.
The third argument to SUBSTRING needs some explaining. It's the number of characters you want to include after the starting position of the string (which in this case is one character after the comma's location). If you specify a value that's longer than the string SUBSTRING will just return to the end of the string. Using 1000 here is a lot easier than calculating the exact number of characters you need to get to the end.
I have a string "I love McDonald's burgers. it's the best."
and I would like to insert it into a column breaking them into 15 character strings.
Hence I need the result as string inserted in 3 rows
I love McDonald
's burgers. it'
s the best.
But if I use ' ' to include the ', an extra ' is present in the string which will affect my calculation of 15 character breakage.
Is there any other way to include ' without having to use one more ' to escape it?
Please help.
You don't need to add an extra ' if you're breaking the string into a variable:
DECLARE
mcdonald_string VARCHAR2(50) := 'I love McDonald''s burgers. it''s the best.';
BEGIN
WHILE LENGTH(mcdonald_string) > 0 LOOP
INSERT INTO your_table(your_field) VALUES (SUBSTR(mcdonald_string,1,15));
mcdonald_string := SUBSTR(mcdonald_string,16);
END LOOP;
COMMIT;
END;
Doubling the quotation marks within a complicated literal,
particularly one that represents a SQL statement, can be tricky. You
can also use the following notation to define your own delimiter
characters for the literal. You choose a character that is not present
in the string, and then do not need to escape other single quotation
marks inside the literal:
-- q'!...!' notation allows the of use single quotes
-- inside the literal
string_var := q'!I'm a string, you're a string.!';
http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/fundamentals.htm#sthref339
This is my procedure.
procedure format_integer_field(Atable: TDataSet);
var i: integer;
begin
if Atable.Active then
if Atable.FieldCount > 0 then
with Atable do
begin
for i:= 0 to FieldCount-1 do
if (Fields[i] is TIntegerField) then
begin
(Fields[i] as TIntegerField).DisplayFormat := '###,###';
(Fields[i] as TIntegerField).EditFormat := '#';
end
else
if (Fields[i] is TFloatField) then
begin
(Fields[i] as TFloatField).DisplayFormat := '###,###.##';
(Fields[i] as TFloatField).EditFormat := '#.##';
end;
end;
end;
This is work fine until a number like "0.9" has been entered and result will be ".9".
How can I have thousand separator and zero before floating point that smaller than "1".
Try (Fields[i] as TFloatField).DisplayFormat := '##0,000.00';
As you did read in documentation at http://docwiki.embarcadero.com/RADStudio/XE3/en/Using_Default_Formatting_for_Numeric,_Date,_and_Time_Fields it says
Default formatting is performed by the following routines:
FormatFloat -- TFloatField, TCurrencyField
And how you did read in the following documentation pages
http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.FormatFloat
http://docwiki.embarcadero.com/Libraries/XE3/en/Data.DB.TNumericField.DisplayFormat
the documentation quotes
0 -> Digit placeholder. If the value being formatted has a digit in the position where '0' appears in the format string, then
that digit is copied to the output string. Otherwise, a '0' is
stored in that position in the output string.
# -> Digit placeholder. If the value being formatted has a digit in the position where '#' appears in the format string, then
that digit is copied to the output string. Otherwise, nothing is
stored in that position in the output string.
So by using "#" in the formatting pattern you tell Delphi "i do not need any digits (and thousands separators with them) in this place, but you might put them if you want" - and since Delphi does not want to put leading zeros - you don't have any. However, if you really need those digits and the thousands separator with them, you put "0" instead of "#" and that way you tell Delphi "the digits just need to be here, whether you want to put them or not"
The format you need is ###,##0.0#