SQL Server: regex search capture group count - c#

I'm currently working on a search method in C# for a SQL Server database.
The regex:
/(a)|(b)|(c)|(d)/g
a, b, c & d are the search keywords.
The string that I apply the regex to:
a fdh eidb
Consists of random words(represented as letters) and some of the keywords from above.
Expected output:
3 keywords matches.
But how does a SQL query for SQL Server look like that returns a table with a Matches column with the keyword match count for each row?
I know how to do this in C# but I want to do it in the search query itself so I can sort the output.
Thanks for any help towards the right direction :)

I don't think there is a way to do regular expressions in SQL Server queries - other than adding some managed code which adds that functionality.
Here is an example of how to do that - SQL Server Regular expressions in T-SQL

It seems that REGEX wasn't really the solution.
Instead I wrote multiple SQL functions that do the job:
CREATE FUNCTION [dbo].[KeywordMatches]
(
#String nvarchar(1000),
#Keywords nvarchar(1000),
#Seperator text
)
RETURNS INT
AS
BEGIN
DECLARE #Count int = 0;
DECLARE #Keyword varchar(1000);
DECLARE KeywordsCursor CURSOR FOR
SELECT *
FROM [dbo].StringSplit(#Keywords, #Seperator)
OPEN KeywordsCursor
FETCH NEXT FROM KeywordsCursor INTO #Keyword
WHILE ##FETCH_STATUS = 0
BEGIN
IF #String LIKE '%' + #Keyword + '%'
SET #Count += 1
FETCH NEXT FROM KeywordsCursor INTO #Keyword
END
CLOSE KeywordsCursor
DEALLOCATE KeywordsCursor
RETURN #Count
END
And (fallback for server 2016 split_string):
CREATE FUNCTION [dbo].[StringSplit]
(
#SeperatedWords nvarchar(1000),
#Seperator char
)
RETURNS #Words TABLE
(
Word nvarchar(1000)
)
AS
BEGIN
DECLARE #Position int = -1
SET #SeperatedWords += #Seperator
WHILE (#Position > 0 OR #Position = -1)
BEGIN
SET #SeperatedWords = SUBSTRING(#SeperatedWords, #Position + 1, LEN(#SeperatedWords) - #Position + 1)
SET #Position = CHARINDEX(#Seperator, #SeperatedWords)
/* Only add words that have a length bigger then 0 */
IF #Position > 1
/* Add the word to the table */
INSERT INTO #Words(Word) VALUES(LEFT(#SeperatedWords, #Position - 1))
END
RETURN
END
Usage:
SELECT Id, Title, [dbo].KeywordMatches(Title, 'blue red green', ' ') AS Matches
FROM Questions
ORDER BY Matches DESC, Date DESC
Above query orders by the amount of keywords found in the title and date.
I also read about full text search which is probably faster then this solution.

Related

splitting strings in oracle query

I want to split my strings in Oracle based on length with space as a delimiter.
For example,
`MY_STRING="Before continuing, turn off the top title display without changing its definition:"`
My output should be
`STRING1="Before continuing, turn off the"`
`STRING2="top title display without changing"`
`STRING3="its definition:"`
The strings should be a maximum of 35 characters in length. The words after position 105 can be ignored.
It colud be done with a stored function :
create or replace FUNCTION get_part(p_value IN VARCHAR2, part in number)
RETURN VARCHAR2
IS temp VARCHAR2(1000);
BEGIN
temp := p_value;
FOR i IN 1 .. (part-1) LOOP
if (Length(temp) <35) then
return '';
ELSE
FOR j in REVERSE 1 .. 35 LOOP
if SUBSTR(temp,j,1) = ' ' then
temp := SUBSTR(temp,j+1);
EXIT;
end if;
END LOOP;
temp := SUBSTR(temp,36);
end if;
END LOOP;
if (Length(temp) <=35) then
return temp;
else
FOR j in reverse 1 .. 35 LOOP
if SUBSTR(temp,j,1) = ' ' then
return SUBSTR(temp,1,j-1);
end if;
END LOOP;
return SUBSTR(temp,1,35);
end if;
END;
usage:
select
get_part(string_value,1),
get_part(string_value,2),
get_part(string_value,3) from ( select 'Before continuing, turn off the top title display without changing its definition:' string_value from dual)
It surely will fail if there are more than 35 chars without space, i'll leave that to you
EDIT: now it should split hard after 35 chars if there are no spaces
You have tagged C# so i chose the language to answer you. I hope it helps. It perfectly splits the text.
Regarding the rules, in your case it splits the text in 3 parts.
This is the Outcome :
STR1 :"Before continuing, turn off the"
STR2 :" top title display without changing"
STR3 :" its definition:"
static void Main(string[] args)
{
const string txt = "Before continuing, turn off the top title display without changing its definition:";
var txtArr = txt.ToCharArray();
var counter = 0;
var stringList = new List<string>();
var str = string.Empty;
for (var i = 0; i < txt.Count(); i++)
{
counter++;
if (counter == 35)
{
while (txtArr[i].ToString() != " ")
{
i--;
str = str.Remove(i);
}
stringList.Add(str);
str = string.Empty;
counter = 0;
}
str = str + txtArr[i];
}
stringList.Add(str);
}
This is how I implemented the algorithm in ORACLE (PL/SQL). Ignore the error and look at the output. It returns 3 lines and works properly. Now write some extra code and modify it as you want. The error does not seem important and I have no idea what the reason is.
declare
--
txt nvarchar2(1000):='Before continuing, turn off the top title display without changing its definition:';
charc nvarchar2(1):='';
TYPE txtArrTyp IS VARRAY(1000) OF NVARCHAR2(1);
txtArr txtArrTyp :=txtArrTyp();
--
str nvarchar2(35):='';
cntr number:=0;
j number:=0;
lent number:=0;
begin
--
lent:=LENGTHB(txt);
--
for i In 1 ..lent
loop
if(txt is null )then
dbms_output.put_line('SHIT');
end if;
charc := SUBSTR(txt,i,1);
txtArr.extend;
txtArr(i):=charc;
end loop;
--
While(j>=1 or j<=lent)
loop
j:=j+1;
cntr :=cntr+1;
if(cntr = 35) then
while(txtArr(j)<>' ')
loop
j:=j-1;
end loop;
str:=substr(str,0,j);
dbms_output.put_line(str);
str:=null;
cntr:=0;
end if;
str := str || txtArr(j);
end loop;
dbms_output.put_line(str);
end;

Execute oracle DDL script with functions from c# code

I've some integration tests written in C# code using a Oracle Database. The test project has a CreateDatabase.sql that contains the DDL to create the entire database in each test execution.
When I had only sequences and tables, I was splitting the content of this file in ";" char and executing each create statement separately, but now I've some functions and their statements contains some ; chars in it, so I can't use this approach anymore.
I've checked on .NET / Oracle: How to execute a script with DDL statements programmatically question, but it did not help.
1) If I try to execute the entire file content in a single OracleCommand, I get an error ORA-00911: invalid character because of the ; chars
2) If I try to wrap the file content in a "begin {0} end;" I get an error PLS-00103: Encountered the symbol "CREATE" when expecting one of the following: ...
3) I could try to parse the SQL file and put each statement inside a EXECUTE IMMEDIATE, but it will be harder...
Is there another option?
I'm using the Oracle.DataAccess version 4.112.3.0 to execute the commands.
EDIT
#kevinsky ask for a script, here it is a simplified example... the entire script create hundreds of objects...
CREATE SEQUENCE SQ_ARAN_SQ_ARQUIVO_ANEXO;
CREATE OR REPLACE FUNCTION UFC_SPSW_DISP_COMPOSICAO(p_id_composicao IN NUMBER) RETURN NUMBER
IS
retorno NUMBER:= 0;
numeroItens NUMBER;
temDefinicao BOOLEAN := false;
CURSOR item_cur is
select idc.itdc_sq_item_definicao_composi, idc.insu_sq_insumo, idc.comp_sq_composicao, idc.itdc_nr_coeficiente, idc.COMP_DS_COMPOSICAO, idc.comp_sq_composicao_pai
from item_definicao_composicao idc;
BEGIN
FOR item_rec IN item_cur LOOP
temDefinicao := true;
IF (item_rec.itdc_nr_coeficiente is null) THEN
RETURN null;
ELSE
IF (item_rec.insu_sq_insumo is null) THEN
numeroItens := UFC_SPSW_DISP_COMPOSICAO(nvl(item_rec.comp_sq_composicao_pai, item_rec.comp_sq_composicao));
else
retorno := retorno + 1;
END IF;
END IF;
END LOOP;
IF (temDefinicao = false) THEN
RETURN 0;
END IF;
RETURN retorno;
END;
CREATE SEQUENCE SQ_CALC_SQ_CALCULO;
I had the same prolem and solved.
Use both 'BEGIN END' and 'EXECUTE IMMEDIATE'.
This is my test (success case)
begin
EXECUTE IMMEDIATE 'create or replace procedure SP_JHKIM2 IS
begin
dbms_output.put_line(''ABC'');
end;';
end;
Here is an idea. Make the front slash / (alone on a separate line) your new standard way of terminating every statement in your script instead of relying on the semi colon. For instance, your sample script could become:
CREATE SEQUENCE SQ_ARAN_SQ_ARQUIVO_ANEXO
/
CREATE OR REPLACE FUNCTION UFC_SPSW_DISP_COMPOSICAO(p_id_composicao IN NUMBER) RETURN NUMBER
IS
retorno NUMBER:= 0;
numeroItens NUMBER;
temDefinicao BOOLEAN := false;
CURSOR item_cur is
select idc.itdc_sq_item_definicao_composi, idc.insu_sq_insumo, idc.comp_sq_composicao, idc.itdc_nr_coeficiente, idc.COMP_DS_COMPOSICAO, idc.comp_sq_composicao_pai
from item_definicao_composicao idc;
BEGIN
FOR item_rec IN item_cur LOOP
temDefinicao := true;
IF (item_rec.itdc_nr_coeficiente is null) THEN
RETURN null;
ELSE
IF (item_rec.insu_sq_insumo is null) THEN
numeroItens := UFC_SPSW_DISP_COMPOSICAO(nvl(item_rec.comp_sq_composicao_pai, item_rec.comp_sq_composicao));
else
retorno := retorno + 1;
END IF;
END IF;
END LOOP;
IF (temDefinicao = false) THEN
RETURN 0;
END IF;
RETURN retorno;
END;
/
CREATE SEQUENCE SQ_CALC_SQ_CALCULO
/
By using the /, your script remains perfectly valid if you wish to run it using SQL*Plus. But it now has the advantage that it becomes trivial to parse by statement in C# so that you can execute each statement separately without the semi colon problems.
I've used this technique in the past and it has worked well.
(Relevant reading in case you are not familiar with the use of the slash in Oracle SQL scripts: When do I need to use a semicolon vs a slash in Oracle SQL?.)
I split only on ; that was followed by some reserved words (or in the end of the file) using regex lookahead assertion.
Ex:
var statements = Regex.Split(
fileContent,
#"\s*;\s*(?=(?:CREATE|ALTER|DROP|RENAME|TRUNCATE)\s|\s*$)",
RegexOptions.IgnoreCase);

Why Are Ellipsis Appearing In My Textbox? [duplicate]

Suppose I have a MySQL table of one column: "Message". It is of type TEXT.
I now want to query all rows, but the text can be large (not extremely large but large) and I only want to get a summary of them. For example the result can be populated into a list.
Is there a way to trim the text to a specific length (say, 10 characters), and add ellipsis if the text is trimmed?
For example:
Message
-----------
12345678901234
1234567890
12345
12345678901
Query result:
1234567...
1234567890
12345
1234567...
Thanks!
select case when length(message) > 7
then concat(substring(message, 1, 7), '...')
else message end as adapted_message
from ...
to test/confirm:
SELECT CASE WHEN LENGTH('1234567890') > 7
THEN CONCAT(SUBSTRING('1234567890', 1, 7), '...')
ELSE '1234567890' END AS adapted_message
UNION
SELECT CASE WHEN LENGTH('12345') > 7
THEN CONCAT(SUBSTRING('12345', 1, 7), '...')
ELSE '12345' END AS adapted_message
Here's a simple one line solution:
IF(CHAR_LENGTH(message) > 10, CONCAT(LEFT(message, 7),"..."), message)
or...
SELECT CONCAT(LEFT(message, 7), IF(LENGTH(message)>7, "…", ""))
FROM table
You can declare a new ELLIPSIS function in order to make your query readable:
DELIMITER //
CREATE FUNCTION ELLIPSIS ( str TEXT, max_length INT )
RETURNS TEXT
BEGIN
DECLARE str_out TEXT;
IF LENGTH(str) <= max_length THEN
SET str_out = str;
ELSE
SET str_out = CONCAT(SUBSTR(str, 1, max_length-3), '...');
END IF;
RETURN str_out;
END; //
DELIMITER ;
Then you simply do:
SELECT ELLIPSIS(Message, 10);
Have a look at the MySQL string functions, documented here. You should be able to use some combination of substring and concat to achieve your desired behaviour.
My approach:
Let x be the maximum number of characters to display (therefore x + 3 dots will be the longest string displayed)
You always want LEFT(field,x)
If LENGTH(field) > x + 3, append 3 dots
Otherwise if LENGTH(field) > x, append the remainder of field
SELECT CONCAT(
LEFT(field,x),
IF(LENGTH(field) > x+3,
'...',
IF(LENGTH(field) > x,
MID(field,x+1,LENGTH(field)),
''
)
)
) FROM table

Syntax error converting datetime from character string 4 [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
Getting this code error when executing the following code.. Help would be appreciated
DECLARE #Last_Timestamp DATETIME,
#New_Timestamp DATETIME
SELECT DISTINCT Rtrim(of_displayname) + ' ('
+ Rtrim(ro_officer) + ')',
CONVERT(DATETIME, CONVERT(CHAR(11), ro_shift_start, 106), 106),
Rtrim(ac_name) + ' (' + Rtrim(ac_pin) + ')',
ro_officer
FROM roster WITH(nolock),
template WITH(nolock),
activity WITH(nolock),
officer WITH(nolock)
WHERE ro_status = 'INFO'
AND ro_activity IN ( 'LEAVE', 'SAL LEAVE' )
AND ro_timestamp > #Last_Timestamp
AND ro_timestamp <= #New_Timestamp
AND ro_shift_start > Getdate()
AND ac_pin = to_activity
AND to_pin = ro_officer
AND Unicode(to_type) = Unicode('M')
AND ro_officer = of_pin
--AND (Of_Payroll_No IN ('w','s')
AND ac_product_code IN ( '01', '09' ) /* Corporate Only */ /* W & S */
UNION
SELECT [officer],
[activity],
[status],
[comment]
FROM production.rolecall.[dbo].[holidayrequest]
ORDER BY Rtrim(of_displayname) + ' ('
+ Rtrim(ro_officer) + ')',RTRIM(Of_Displayname) + ' (' + RTRIM(Ro_Officer) + ')',CONVERT(DATETIME,CONVERT(CHAR(11), RO_Shift_Start,106),106)
There are 3 places where you are doing date conversions here:
CONVERT(DATETIME, CONVERT(CHAR(11), ro_shift_start, 106), 106)
CONVERT(DATETIME,CONVERT(CHAR(11), RO_Shift_Start,106),106)
the UINON
The first two look like you are actually trying to get the day without the time. If so: get the day without the time; there are various ways of doing this, but in recent versions of sql server, casting it to a date would work fine:
select cast(columnname as date) as [columnname]
or if you really need it as datetime:
select cast(cast(columnname as date) as datetime) as [columnname]
On other database versions you can use the sneeky "cast it as a float" approach:
select cast(floor(cast(columnname as float)) as datetime) as [columnname]
The UNION is more interesting; it looks like these are very different shapes - in which case: why are you unioning them? I very much doubt that [activity] is a datetime, yet that is the column it will become part of. It is entirely possible that this error is coming from trying to convert [activity] to a datetime.

What is wrong with this regex, not working correctly?

My query is working fine inside my oracle, but when I add it inside my oracle command getting error at [A-Z] and \1\3 saying unrecognized character. I think I need to use # some where to make it correct but I don't know where?
*My Query to see definition of trigger without user name: also deleting line that start with ALTER TRIGGER.. *
OracleCommand Command = new OracleCommand(#"SELECT regexp_replace(dbms_metadata.get_ddl('TRIGGER','" + triggernames + "'),'(CREATE OR REPLACE TRIGGER )("[A-Z]+"\.)(.+)(ALTER TRIGGER .+)','\1\3', 1, 0, 'n')FROM dual", connection))
Result:
CREATE OR REPLACE TRIGGER "USER"."EMP"
BEFORE INSERT OR UPDATE
of salary
on employee
for each row
declare
v_error VARCHAR2(20);
begin
if :new.salary > 10
then
v_error:=:old.first_name||' cannot have that much!';
raise_application_error(-20999,v_error);
end if;
end;
ALTER TRIGGER "USER"."EMP" ENABLE
Expected Result:
CREATE OR REPLACE TRIGGER "EMP"
BEFORE INSERT OR UPDATE
of salary
on employee
for each row
declare
v_error VARCHAR2(20);
begin
if :new.salary > 10
then
v_error:=:old.first_name||' cannot have that much!';
raise_application_error(-20999,v_error);
end if;
end;
# Won't do all the escaping for you. You need to escape the quotes around [A-Z] with '\'.
Same deal with the \1\3, '\' is saying it's an escape sequence where you really mean a literal '\'.
E.g:
OracleCommand Command = new OracleCommand(#"SELECT regexp_replace(dbms_metadata.get_ddl('TRIGGER','" + triggernames + "'),'(CREATE OR REPLACE TRIGGER )(\"[A-Z]+\"\.)(.+)(ALTER TRIGGER .+)','\\1\\3', 1, 0, 'n')FROM dual", conn1))
Alternatively if you want to use a 2nd '#' you need to use the quote-escape-sequence (a double "")
e.g.:
#"SELECT regexp_replace(dbms_metadata.get_ddl('TRIGGER','" + triggernames + #"'),'(CREATE OR REPLACE TRIGGER )(""[A-Z]+""\.)(.+)(ALTER TRIGGER .+)','\1\3', 1, 0, 'n')FROM dual"

Categories