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
Related
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;
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.
I have tried several methods (by position, by white space, regex) but cannot figure how to best parse the following lines as a table. For e.g. let's say the two lines I want to parse are:
Bonds Bid Offer (mm) (mm) Chng
STACR 2015-HQA1 M1 125 120 5 x 1.5 0
STACR 2015-HQA12 2M2 265 5 x -2
I want that it should parse as follows for [BondName] [Bid] [Offer]:
[STACR 2015-HQA1 M1] [125] [120]
[STACR 2015-HQA12 2M2] [265] [null]
Notice the null which is an actual value and also the spaces should be retained in the bond name. FYI, the number of spaces in the Bond Name will be 2 as in the above examples.
Edit: Since many of you have asked for code here it is. The spaces between the points can range from 1-5 so I cannot reply on spaces (it was straightforward then).
string bondName = quoteLine.Substring(0, 19);
string bid = quoteLine.Substring(19, 5).Trim();
string offer = quoteLine.Substring(24, 6).Trim();
The only way I can see this working is that:
1st data point is STACR (Type)
2nd data point is the year and Series
(e.g. 2015-HQA1)
3rd data point is Tranche (M1)
4th data point is bid
(e.g. 125 ** bid is always available **)
5th data point is offer (e.g. 120 but can be blank
or whitespace which introduces complexity)
With the current set of requirements, I'm assuming the following
1. String starts with 3 part bond name
2. Followed by bid
3. Followed by offer (optional)
4. After that, we'll have something like ... x ... ... (we'll use x as reference)
Given they are valid, you can use the following code
var str = "STACR 2015-HQA1 M1 125 120 5 x 1.5 0"; //your data
var parts = str.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();
//we'll use this pattern : <3 part bond name> <bid> <offer/null> <something x ....>
var xIsAt = parts.IndexOf("x"); //we'll use x as reference
if (xIsAt > 2) //first three are BondName
parts.RemoveRange(xIsAt - 1, parts.Count - xIsAt + 1); //remove "5 x 1.5 ..."
var bond = string.Join(" ", parts.Take(3)); //first 3 parts are bond
var bid = parts.Count > 3 ? parts.ElementAt(3) : null; //4th is bid
var offer = parts.Count > 4 ? parts.ElementAt(4) : null; //5th is offer
[EDIT]
I did not account for the blank 'Offer' so this method will fail on a blank 'Offer'. Looks like someone already has a working answer, but i'll leave the linq example for anyone that finds it useful.
[END EDIT]
Linq based option.
Split the string by spaces, and remove empty spaces. Then reverse the order so you can start from the back and work your way forward. The data appears more normalized at the end of the string.
For each successive part of the line, you skip the previous options and only take what you need. For the last part which is the long string, you skip what you don't need, then reverse the order back to normal, and join the segments together with spaces.
string test = "STACR 2015-HQA1 M1 125 120 5 x 1.5 0";
var split_string_remove_empty = test.Split(new char[]{ ' ' }, StringSplitOptions.RemoveEmptyEntries).Reverse();
var change = split_string_remove_empty.Take(1)
.SingleOrDefault();
var mm2 = split_string_remove_empty.Skip(1)
.Take(1)
.SingleOrDefault();
var mm3 = split_string_remove_empty.Skip(3)
.Take(1)
.SingleOrDefault();
var offer = split_string_remove_empty.Skip(4)
.Take(1)
.SingleOrDefault();
var bid = split_string_remove_empty.Skip(5)
.Take(1)
.SingleOrDefault();
var bonds = string.Join(" ", split_string_remove_empty.Skip(6)
.Reverse());
Output:
I have an SQL query :
SELECT DATEDIFF(deadline,CURDATE()) FROM tasks WHERE 1
The Result is : 3
How can I return the result as : 3 Days instead of 3
I know that I can manually append the string from my C# code something like :
string result = getSqlresult();
string result += " Days";
But I want to get the result directly as 3 Days from MySQL database.
The reason :
I'm binding information directly to datagridview and therefore, In order to modify the result i need to iterate through all rows and update them. So to increase performance, I need to get the result directly from database as 3 Days instead of 3
Anyhelp would be highly appreciated
you can concatenate the string Days into the result of DATEDIFF using CONCAT.
SELECT CONCAT(DATEDIFF(deadline,CURDATE()), ' Days')
FROM tasks
WHERE 1
if you are using old versions of MySQL, convert it to string so you will not get bolb result.
SELECT CONCAT(CAST(DATEDIFF(deadline,CURDATE()) AS CHAR(5)), ' Days')
FROM tasks
WHERE 1
UPDATE 1
SELECT CASE
WHEN DATEDIFF(deadline,CURDATE()) >= 0
THEN CONCAT(DATEDIFF(deadline,CURDATE()), ' Days')
ELSE CONCAT('Expired since ', DATEDIFF(deadline,CURDATE()) * -1, ' Days')
END
FROM tasks
SQLFiddle Demo
If I have a string such as the following:
String myString = "SET(someRandomName, \"hi\", u)";
where I know that "SET(" will always exists in the string, but the length of "someRandomName" is unknown, how would I go about deleting all the characters from "(" to the first instance of """? So to re-iterate, I would like to delete this substring: "SET(someRandomName, \"" from myString.
How would I do this in C#.Net?
EDIT: I don't want to use regex for this.
Providing the string will always have this structure, the easiest is to use String.IndexOf() to look-up the index of the first occurence of ". String.Substring() then gives you appropriate portion of the original string.
Likewise you can use String.LastIndexOf() to find the index of the first " from the end of the string. Then you will be able to extract just the value of the second argument ("hi" in your sample).
You will end up with something like this:
int begin = myString.IndexOf('"');
int end = myString.LastIndexOf('"');
string secondArg = myString.Substring(begin, end - begin + 1);
This will yield "\"hi\"" in secondArg.
UPDATE: To remove a portion of the string, use the String.Remove() method:
int begin = myString.IndexOf('(');
int end = myString.IndexOf('"');
string altered = myString.Remove(begin + 1, end - begin - 1);
This will yield "SET(\"hi\", u)" in altered.
I know it's been years, but .Net been has also evolved in the meantime.
Consider using range operator in case anyone looking here for an answer.
Assuming that Set( and \"hi\", u) is constant value (8 digit without the escapes):
var sub = myString[^4...^8];
myString.Replace(sub, replaceValue);
more examples and a good explanation in this article or of course in microsoft docs
This is pretty awful, but this will accomplish what you want with a simple linq statement. Just presenting as an alternative to the IndexOf answers.
string myString = "SET(someRandomName, \"hi\", 0)";
string fixedStr = new String( myString.ToCharArray().Take( 4 ).Concat( myString.ToCharArray().SkipWhile( c => c != '"' ) ).ToArray() );
yields: SET("hi", 0)
Note: the skip is hard-coded for 4 characters, you could alter it to skip over the characters in an array that contains them instead.
I assume you want to transform
SET(someRandomName, "hi", u)
into:
SET(u)
To achieve that, you can use:
String newString = "SET(" + myString.Substring(myString.LastIndexOf(',') + 1).Trim();
To explain this bit by bit:
myString.LastIndexOf(',')
will give you the index (position) of your last , character. Increment it by 1 to get the start index of the third argument in your SET function.
myString.Substring(myString.LastIndexOf(',') + 1)
The Substring method will eliminate all characters up to the specified position. In this case, we’re eliminating everything up to (and including) the last ,. In the example above, this would eliminate the SET(someRandomName, "hi", part, and leave us with u).
The Trim is necessary simply to remove the leading space character before your u.
Finally, we prepend SET( to our substring (since we had formerly removed it due to our Substring).
Edit: Based on your comment below (which contradicts what you asked in your question), you can use:
String newString = "SET(" + myString.Substring(myString.IndexOf(',') + 1).Trim();