I have report that displays name (FirstName MiddleName LastName). Each field has a space between and works great when name has MiddleName. However, when MiddleName is NULL, space renders between FirstName and LastName. What is the best way to solve that problem?
I tried something like that:
=Fields!FirstName.Value & "" & iif (isNothing(Fields!MiddleName.Value), "", Fields!MiddleName.Value) & "" & Fields!LastName.Value
With your current expression there should be no spaces. If there are any spaces currently they must exist in your database... This can be confirmed by using TRIM on FirstName, MiddleName, LastName.
To properly format your string you need to use spaces between the "", and ensure the space following the middle name is contained within the iif statement
=TRIM(Fields!FirstName.Value) & " " &
iif (isNothing(TRIM(Fields!MiddleName.Value)),
"",
TRIM(Fields!MiddleName.Value) & " ")
& TRIM(Fields!LastName.Value)
Firstly your expression above doesn't returns a name with space.
I think you mean something like this with space:
=Fields!FirstName.Value & " " & iif (isNothing(Fields!MiddleName.Value), " ", Fields!MiddleName.Value) & " " & Fields!LastName.Value
Aside from Jonnus answer you could also use 'Len' to check its length.
If you only worry about the MiddleName you could use this expression using Len
=Fields!FirstName.Value & " " & IIF(Len(Fields!MiddleName.Value) < 1,"", Fields!MiddleName.Value) & " " & Fields!LastName.Value
But if I were you I would rather use sql function to handle this rather than do it in expression which is messy. And I could also reuse this for other reports than repeat the expression again.
Here is an example of function that returns a fullname with middle name of your format.
IF EXISTS (
SELECT *
FROM dbo.sysobjects
WHERE ID = OBJECT_ID(N'[dbo].[GetClientFullNameWithMiddleName]') AND
xtype in (N'FN', N'IF', N'TF'))
DROP FUNCTION [dbo].[GetClientFullNameWithMiddleName]
GO
CREATE FUNCTION [dbo].[GetClientFullNameWithMiddleName](#ClientID UNIQUEIDENTIFIER)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #FULLNAME NVARCHAR(MAX)
DECLARE #LASTNAME NVARCHAR(MAX)
DECLARE #FIRSTNAME NVARCHAR(MAX)
DECLARE #MIDDLENAME NVARCHAR(MAX)
SET #LASTNAME = ISNULL((SELECT Lastname FROM Client WHERE ClientID = #ClientID),'')
SET #FIRSTNAME = ISNULL((SELECT Firstname FROM Client WHERE ClientID = #ClientID),'')
SET #MIDDLENAME = ISNULL((SELECT Middlename FROM Client WHERE ClientID = #ClientID),'')
IF(#ClientID <> '00000000-0000-0000-0000-000000000000')
BEGIN
IF(#FIRSTNAME <> '')
BEGIN
SET #FULLNAME = CAST((#FIRSTNAME) AS NVARCHAR(MAX))
IF(#MIDDLENAME <> '' AND #FULLNAME <> '')
BEGIN
SET #FULLNAME = CAST((#FULLNAME + ' ' + #MIDDLENAME) AS NVARCHAR(MAX))
END
IF(#LASTNAME <> '' AND #FULLNAME <> '')
BEGIN
SET #FULLNAME = CAST((#FULLNAME + ' ' + #LASTNAME) AS NVARCHAR(MAX))
END
END
ELSE
BEGIN
SET #FULLNAME = CAST((#FIRSTNAME)AS NVARCHAR(MAX))
END
END
ELSE
BEGIN
SET #FULLNAME = ''
END
RETURN #FULLNAME
END
GO
Then you could use it like this
SELECT
[dbo].[GetClientFullNameWithMiddleInitial](ClientID)
FROM Client
Related
I've a library of questions and answers and building an API in NodeJS which allows to search for answers based on the question passed as input. Following is my goal:
Split the question by space
Tokenize it and remove stopwords
Query database for records where question contains one or more words from the tokenized array
Ideally sort in descending order total number of matches in the question. For eg: If the question A contains 'module' and 'solution' and question B contains only 'solution', then question A should be shown before question B
I've been able to achieve 1 to 3, using the below code:
let question = req.query.question;
let arrQuestions = question.split(" ");
let tokenizedQuestion = stopwords.removeStopwords(arrQuestions);
let whereClause = tokenizedQuestion.join("%' OR answer LIKE '%");
whereClause = " answer LIKE '%" + whereClause + "%' ";
let query = "SELECT * FROM tbl_libraries WHERE " + whereClause;
I'm not able to figure out how to achieve 4. Can somebody provide pointers?
Thanks!
Are you sure that you do not want to use MySQL fulltext search for this?
If the answer is some flavour of 'No', you can continue reading...
In one of my project I was implementing something like this.
Query wise it looks like this (simplified version):
SELECT
name
FROM
table
WHERE
name REGEXP 'term1|term2|term3' -- you can use your OR + LIKE way
ORDER BY
SP_TermsWeitght(name, 'term1 term2 term3') DESC
All the magic is in my SP_TermsWieght function that returns "weight" (number) and I'm supplying a list of terms (cleaned and normalized) to the function.
The function:
CREATE FUNCTION `SP_TermsWeight`(
`sValue` TEXT,
`sTerms` VARCHAR(127)
)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE p INT DEFAULT 1;
DECLARE w INT DEFAULT 0;
DECLARE l INT;
DECLARE c CHAR(1);
DECLARE s VARCHAR(63);
DECLARE delimiters VARCHAR(15) DEFAULT ' ,';
SET sTerms = TRIM(sTerms);
SET l = LENGTH(sTerms);
IF (l > 0) THEN
-- checking is value matched terms exactly
IF (sTerms = sValue) THEN
SET w = 50000;
ELSE
-- supposing that "the terms" is one single term so it it match in full, the weight will be high
IF (l <= 63) THEN
SET w = w + SP_TermWeight(sValue, sTerms, 5000, 1000, 100);
END IF;
-- not processing it term by term if it is already matched as full
IF (w = 0) THEN
-- processing term by term using space or comma as delimiter
WHILE i <= l DO
BEGIN
SET c = SUBSTRING(sTerms, i, 1);
IF (LOCATE(c, delimiters) > 0) THEN
SET s = SUBSTRING(sTerms, p, i - p);
SET w = w + SP_TermWeight(sValue, s, 50, 10, 0);
SET p = i + 1;
END IF;
SET i = i + 1;
END;
END WHILE;
IF (p > 1 AND p < i) THEN
SET s = SUBSTRING(sTerms, p, i - 1);
SET w = w + SP_TermWeight(sValue, s, 50, 10, 0);
END IF;
END IF;
END IF;
END IF;
RETURN w;
END
Technically speaking it is 'separating' terms using delimiters and checking if the value "contains" the term.
It's a bit hard to explain everything what it does (I've added a few comments in the code for you).
Feel free to ask questions if you do not understand some bits.
In your case it can be simplified dramatically as you do not need to differentiate begin/end/middle matches.
Another helper function that used internally:
CREATE FUNCTION `SP_TermWeight`(
`sValue` TEXT,
`sTerm` VARCHAR(63),
`iWeightBegin` INT,
`iWeightEnd` INT,
`iWeightMiddle` INT
)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE r INT DEFAULT 0;
SET sTerm = TRIM(sTerm);
IF (LENGTH(sTerm) > 1) THEN
IF (iWeightBegin != 0 AND sValue REGEXP CONCAT('[[:<:]]', sTerm)) THEN
SET r = r + iWeightBegin;
END IF;
IF (iWeightEnd != 0 AND sValue REGEXP CONCAT(sTerm, '[[:>:]]')) THEN
SET r = r + iWeightEnd;
END IF;
IF (r = 0 AND iWeightMiddle != 0 AND sValue REGEXP sTerm) THEN
SET r = r + iWeightMiddle;
END IF;
END IF;
RETURN r;
END
This function used for assigning different weights if the term matched to value from the beginning of the string, at the end of the string or in the middle. It is important in my case. In your case it might be simple LIKE.
I ended up using Full Text Search. Following is the stored procedure I created to enable searching:
DROP PROCEDURE IF EXISTS SP_Search $$
CREATE PROCEDURE `SP_Search`(IN QuestionToSearch TEXT, IN TagsToSearch TEXT, IN CollectionsToSearch TEXT, IN ReturnRecordsFromIndex INT, IN TotalRecordsToReturn INT)
BEGIN
SET #MainQuery = CONCAT("SELECT *, MATCH(question, answer_content) AGAINST (", CONCAT("'", QuestionToSearch, "'"), " IN NATURAL LANGUAGE MODE) AS score ");
SET #MainQuery = CONCAT(#MainQuery, " FROM tbl_libraries ");
SET #MainQuery = CONCAT(#MainQuery, " WHERE MATCH(question, answer_content) AGAINST (", CONCAT("'", QuestionToSearch, "'"), " IN NATURAL LANGUAGE MODE) ");
IF F_IsNullOrEmpty(TagsToSearch) AND NOT F_IsNullOrEmpty(CollectionsToSearch) THEN
SET #MainQuery = CONCAT(#MainQuery, " AND collections LIKE '%", CollectionsToSearch, "%' ");
ELSEIF F_IsNullOrEmpty(CollectionsToSearch) AND NOT F_IsNullOrEmpty(TagsToSearch) THEN
SET #MainQuery = CONCAT(#MainQuery, " AND tags LIKE '", TagsToSearch, "' ");
ELSEIF NOT F_IsNullOrEmpty(TagsToSearch) AND NOT F_IsNullOrEmpty(CollectionsToSearch) THEN
SET #MainQuery = CONCAT(#MainQuery, " AND tags LIKE '", TagsToSearch, "' AND collections LIKE '", CollectionsToSearch, "' ");
END IF;
SET #MainQuery = CONCAT(#MainQuery, " ORDER BY score DESC ");
SET #MainQuery = CONCAT(#MainQuery, " LIMIT ", ReturnRecordsFromIndex, ", ", TotalRecordsToReturn);
PREPARE SqlQuery FROM #MainQuery;
EXECUTE SqlQuery;
END $$
DELIMITER ;
This uses a custom function I created F_IsNullOrEmpty, which is as shown below for completion:
CREATE FUNCTION F_IsNullOrEmpty(ValueToCheck VARCHAR(256)) RETURNS BOOL
DETERMINISTIC
BEGIN
IF((ValueToCheck IS NULL) OR (LENGTH(ValueToCheck) = 0) OR (ValueToCheck = 'null')) THEN
Return True;
ELSE
Return False;
END IF;
END;
My print statement fails each time and so does my exec, anytime I try to run this statement it just tells me command completed successfully. I added the print statement to try to see what was actually being executed, but I am still mind-blown on this. Can someone here please help me?
Shouldn't the Print statement at least show me what I am trying to run? If I print each variable individually before trying to run the update it shows the correct value, so I can only assume it is something way wrong with my update statement?
Declare #fulldata varchar(30), #rsi varchar(50), #employeename varchar(50), #email varchar(50), #rsi2 varchar(50), #email2 varchar(50),
#rsiID varchar(50), #calldate datetime, #calltime datetime, #orderdate datetime, #email3 varchar(50), #uniqueID int, #sql varchar(max)
Set #fullData = 'tvdb'
Set #rsi = 'Alphabet'
Set #employeename = 'Mike Jones'
Set #email = '123abc#gmail.com'
Set #rsi2 = 'Broccoli'
Set #email2 = 'abc123#gmail.com'
Set #rsiID = 'alt16bc'
Set #calldate = '06/15/2015'
Set #calltime = '12:15:00'
Set #orderdate = '06/16/2015'
Set #email3 = 'pineapple1841#gmail.com'
Set #uniqueID = 172855
Set #sql =
'update '+#fulldata+' '
+ 'set rsi = COALESCE('''+#rsi+''',''''), '
+ 'employeename = COALESCE('''+#employeename+''',''''), '
+ 'email = COALESCE('''+#email+''',''''), '
+ 'rsi2 = COALESCE('''+#rsi2+''',''''), '
+ 'email2 = COALESCE('''+#email2+''',''''), '
+ 'rsiID = COALESCE('''+#rsiID+''',''''), '
+ 'calldate = COALESCE('''+CAST(#calldate As Varchar)+''',''''), '
+ 'calltime = COALESCE('''+CAST(#calltime As Varchar)+''',''''), '
+ 'orderdate = COALESCE('''+CAST(#orderdate As Varchar)+''',''''), '
+ 'email3 = COALESCE('''+#email3+''','''') '
+ 'where uniqueID = '+CAST(#uniqueID As Varchar)+' and '+CAST(#uniqueID As Varchar)+' > 0 '
Print #sql
exec (#sql)
EDIT ---
If I try to insert my statements into a table to check it is null. Which leads me to why is #sql not being assigned?
Insert Into #SqlStatement (sql12) VALUES (#sql)
Select * FROM #SqlStatement
Are you sure you have all lines included here? and you can run it without error?
At least your don't have declaration of #sql.
Even you declare the #sql, this line will give you error:
Set uniqueID = 172855
It should be
Set #uniqueID = 172855
Without assigning values to #uniqueID, your whole #sql is be NULL and print will generate NO output.
update tvdb set rsi = COALESCE('Alphabet',''), employeename = COALESCE('Mike Jones',''), email = COALESCE('123abc#gmail.com',''), rsi2 = COALESCE('Broccoli',''), email2 = COALESCE('abc123#gmail.com',''), rsiID = COALESCE('alt16bc',''), calldate = COALESCE('Jun 15 2015 12:00AM',''), calltime = COALESCE('Jan 1 1900 12:15PM',''), orderdate = COALESCE('Jun 16 2015 12:00AM',''), email3 = COALESCE('pineapple1841#gmail.com','') where uniqueID = 172855 and 172855 > 0
Msg 208, Level 16, State 1, Line 1
Invalid object name 'tvdb'.
To debug your code, you can comment out some lines like:
Set #sql =
'update '+#fulldata+' '
+ 'set rsi = COALESCE('''+#rsi+''',''''), '
---+ 'employeename = COALESCE('''+#employeename+''',''''), '
----+ 'email = COALESCE('''+#email+''',''''), '
----+ 'rsi2 = COALESCE('''+#rsi2+''',''''), '
----+ 'email2 = COALESCE('''+#email2+''',''''), '
----+ 'rsiID = COALESCE('''+#rsiID+''',''''), '
----+ 'calldate = COALESCE('''+CAST(#calldate As Varchar)+''',''''), '
----+ 'calltime = COALESCE('''+CAST(#calltime As Varchar)+''',''''), '
----+ 'orderdate = COALESCE('''+CAST(#orderdate As Varchar)+''',''''), '
----+ 'email3 = COALESCE('''+#email3+''','''') '
----+ 'where uniqueID = '+CAST(#uniqueID As Varchar)+' and '+CAST(#uniqueID As Varchar)+' > 0 '
Print #sql
exec (#sql)
and uncomment one line a time until you find the problematic line.
To catch values and make sure you have a non-null #sql, you need use the COALESCE this way:
Set #sql =
'update '+#fulldata+' ' ...
+ 'email = '''+COALESCE(#email,'')+''','
Good day!
I get a "Varchar" which is the full name of a client, and need to split this "varchar" as:
"FirstName MidleName MiddleNameTwo LastName "
Need to enter the "FirstName" on "Col1"
Need to enter the "MiddleName, MiddleNameTwo in" col2 "
Need to enter the "LastName" in "Col3"
Thanks for your help!
You Should use subqueries for this task
INSERT INTO names(firstname, middlename, lastname)
SELECT substringfunctionofyours(name), substringfunctionofyours(name),
substringfunctionofyours(name) FROM namefield
keep in mind this is just a dummy query it won't work if you use it like this
Can you change the input method to 4 separate fields ?
If you can't, then implement a specific delimeter between the different parts of the string ?
In cases where the last name is built with 2 words, there is no deterministic way of processing the string if the delimeter is [space] or any other character like '-' or ' which are common in names.
Using a delimeter like '|' :
Declare #FullName varchar(100) = 'FirstName|MidleName|MiddleNameTwo|LastName'
Print 'FirstName = ' + Substring(#FullName,1,charindex('|',#FullName)-1)
SELECT #FullName = Substring(#FullName,charindex('|',#FullName)+1,len(#fullName))
Print 'MiddleName1 = ' + Substring(#FullName,1,charindex('|',#FullName)-1)
SELECT #FullName = Substring(#FullName,charindex('|',#FullName)+1,len(#fullName))
Print 'MiddleName2 = ' + Substring(#FullName,1,charindex('|',#FullName)-1)
SELECT #FullName = Substring(#FullName,charindex('|',#FullName)+1,len(#fullName))
Print 'LastName = ' + #FullName
This will also work if you don't have one of the parts, as long as you maintain the structure.
Declare #FullName varchar(100) = 'FirstName|MidleName||LastName'
Print 'FirstName = ' + Substring(#FullName,1,charindex('|',#FullName)-1)
SELECT #FullName = Substring(#FullName,charindex('|',#FullName)+1,len(#fullName))
Print 'MiddleName1 = ' + Substring(#FullName,1,charindex('|',#FullName)-1)
SELECT #FullName = Substring(#FullName,charindex('|',#FullName)+1,len(#fullName))
Print 'MiddleName2 = ' + Substring(#FullName,1,charindex('|',#FullName)-1)
SELECT #FullName = Substring(#FullName,charindex('|',#FullName)+1,len(#fullName))
Print 'LastName = ' + #FullName
Let say you have a name like this "ABC DEF GHI"
now you want First Name, Middle Name and last Name From this string.
Here We Go
Select
Substring(Cast('ABC DEF GHI' as nvarchar),0,charindex(' ',Cast('ABC DEF GHI' as nvarchar))) as Col1,
Substring(Cast('ABC DEF GHI' as nvarchar),charindex(' ',Cast('ABC DEF GHI' as nvarchar)),4)as Col2,
Substring(Cast('ABC DEF GHI' as nvarchar),8,charindex(' ',Cast('ABC DEF GHI' as nvarchar)))as Col3
Note: This is MSSQL SERVER Query and Substring Function is Built in in MSSQL SERVER
When i run the following code in mysql workbench im getting the above mentioned error Pls do help
# IF p_Name is not null THEN
# SET m_sql = m_sql +' Name=''' + p_Name + ''' and ';
# END IF;
If p_ReligionID is not null THEN
SET m_sql = m_sql + '" ReligionId= "' + 'CAST(p_ReligionID AS CHAR(30))' + 'and';
END IF;
If p_CasteID is not null THEN
SET m_sql = m_sql + '" CasteId= "' + 'CAST(p_CasteID AS CHAR(30))' + 'and';
END IF;
If p_CountryID is not null THEN
SET m_sql=m_sql+ '" Countryid= "' + 'CAST(p_CountryID AS CHAR(30))' + 'and';
END IF;
If p_MotherTongueID is not null THEN
SET m_sql=m_sql+'" MotherTongueID= "' + 'CAST(p_MotherTongueID AS CHAR(30))' + 'and';
END IF;
Assuming m_sql is valid on entry and at least one of the conditions passes, the final statement ends with an 'and'. That's not valid SQL in any dialect I know. Then you have to deal with what appears to be extraneous quoting.
I'd suggest the ANDs be inserted at the beginning of the follow-on condition
If p_ReligionID is not null THEN
SET m_sql = m_sql + ' and ReligionId = ' + 'CAST(p_ReligionID AS CHAR(30))';
END IF;
(I'm a SQL Server person, so the particulars of MySql's procedural language are beyond my ken)
I am trying to add the group_concat function to hsqldb so that I can properly test a query as a unit/integration test. The query works fine in mysql, so I need it to work in hsqldb (hopefully).
// GROUP_CONCAT
jdbcTemplate.update("DROP FUNCTION GROUP_CONCAT IF EXISTS;");
jdbcTemplate.update(
"create aggregate function group_concat(in val varchar(100), in flag boolean, inout buffer varchar(1000), inout counter int) " +
" returns varchar(1000) " +
" contains sql " +
"begin atomic " +
" if flag then" +
" return buffer;" +
" else" +
" if val is null then return null; end if;" +
" if buffer is null then set buffer = ''; end if;" +
" if counter is null then set counter = 0; end if;" +
" if counter > 0 then set buffer = buffer || ','; end if;" +
" set buffer = buffer + val;" +
" set counter = counter + 1;" +
" return null;" +
" end if;" +
"end;"
);
Adding this aggregation function solves most of the problem. It will correctly behave like mysql's group_concat. However, what it won't do is let me use the distinct keyword like this:
group_concat(distinct column)
Is there any way to factor in the distinct keyword? Or do I rewrite the query to avoid the distinct keyword altogether?
HSQLDB has built-in GROUP_CONCAT and accepts DISTINCT.
http://hsqldb.org/doc/2.0/guide/dataaccess-chapt.html#dac_aggregate_funcs
At the moment you cannot add DISTINCT to a user-defined aggregate function, but this looks like an interesting feature to allow in the future.