SQL concat / + operator behaving strangely? - sql-server

So basically to explain my situation I have a program where a user can select code numbers, that are alpha numeric. These codes are stored in my SQL database as datatype char.
When they select all the codes they want, the program then sends a few parameters(the codes being one of them). The codes are strung together and look something like this:
',01,1,A3' etc. etc. with commas separating the codes. I have the comma in front, but changing the comma to the back does not change anything.
the #reasonCode variable is the reason codes strung together.
In my where clause I have a statement that is this:
(#reasonCode = 'ALL') OR
((#reasonCode <> 'ALL' AND (charindex(',' + ro_reason_code, #reasonCode) > 0)))
Basically I want to restrict my results to just those that have those specific reason codes the user selected(among other parameters). I am trying to achieve that by stringing together the codes, and then searching through them using charindex, seperated by commas.
However I am running into an issue. Here are the results using a few different variations of reason codes:
',1' = 625 records (correct number)
',01' = 1015(correct number)
',01,1 = 1640(correct number)
',1,01' = 1015(for whatever reason it isn't picking up the 1 reason codes)
That is my issue right there.
When I put the 1 in front of the 01, it doesn't pick up the 1 reason codes. But if I do it flip-flopped it works fine...
Any ideas as to why this happens?
(I have tried also using the concat function and get the same results, and also tried forcing everything to be char datatype.)
In the end I would like the same result, regardless if it is ,01,1 or ,1,01.

I'm pretty sure this is because you said you're using the char type instead of varchar. Try replacing your charindex expression with this:
charindex(',' + rtrim(ro_reason_code), #reasonCode)
When I used a type of char(2) in the table and char(16) for the #reasonCode, I could reproduce your result, and I found that adding the rtrim fixed the problem. But unfortunately I can't explain exactly what's going here, why having ',1' at the end of the string should work without the trim whereas having it at the beginning does not. Hopefully someone can provide a more in-depth answer that gets into the "why," but I thought I'd still post this for the time being to get you running.
Reproduction:
-- Forgive the "hackish" way of populating this table. I'm assuming sysobjects has >=1015 records.
declare #Code table (ro_reason_code char(2));
insert #Code select top 625 '1' from sysobjects;
insert #Code select top 1015 '01' from sysobjects;
declare #reasonCode char(16);
set #reasonCode = ',1,01';
select count(1) from #Code where #reasonCode = 'ALL' or charindex(',' + ro_reason_code, #reasonCode) > 0; -- Result: 1015
select count(1) from #Code where #reasonCode = 'ALL' or charindex(',' + rtrim(ro_reason_code), #reasonCode) > 0; -- Result: 1640
set #reasonCode = ',01,1';
select count(1) from #Code where #reasonCode = 'ALL' or charindex(',' + ro_reason_code, #reasonCode) > 0; -- Result: 1640
select count(1) from #Code where #reasonCode = 'ALL' or charindex(',' + rtrim(ro_reason_code), #reasonCode) > 0; -- Result: 1640

Because you are using char, which is a fixed length field, your data is stored padded out to the length of the field. So '1' is stored as '1 '
DECLARE #Code CHAR(2)
SET #Code = '1'
SELECT '''' + #Code + ''''
-- Printes '1 '
For that reason, when you add ',' to the value, you now have ',1 ' (notice the trailing whitespace)
DECLARE #Code CHAR(2)
SET #Code = '1'
SELECT '''' + ',' + #Code + ''''
-- prints ',1 '
Now if you're comparing off another char field, there will also be padded whitespace if the character data is less than the length of the field. So what appers to be ',11,1' is actually something like ',11,1 ' which does match the pattern of ',1 '
BUT, when you reverse the order, ',1,11' becomes ',1,11 ' which does not match the pattern of ',1 '
Unrelated
I just want to point out there is a subtle issue with the implementation. By only appending the leading comma, you may get false positives depending on your data. For example, ,2 will match the pattern ,25.
,2 does match 1,11,25,A01
You've gotta append the comma on both sides of each side of the evaluation.
CHARINDEX( ',' + RTRIM(ro_reason_code) + ',',
',' + RTRIM(#reasonCode) + ',') > 0
So to illustrate the difference it becomes
,2, does not match ,1,11,25,A01,

Related

Character length over 130 does not show in column

I have a lot of questions from a survey im using for a pivot table. To collect all the questions to my pivot dynamically im using stuff and for xml path. However it seems like question text > 130 in length is not showing.
And i can select all the columns from my cte Questions, so I know the data is there.
UPDATE: If I select my output, my total length is around 8.000 could it be something about the nvarchar(max) not storing more than 8.000 even though it should be able to store around 2gb?
What am I doing wrong?
SELECT QuestionList = cast(STUFF((
SELECT ',' + QUOTENAME(cast(question AS NVARCHAR(max)))
FROM questions
ORDER BY [AgpdbQuestionID]
FOR XML PATH('')
), 1, 1, '') AS NVARCHAR(max))
This is because of QUOTENAME, if input is larger than 128 it returns NULL because it is supposed to handle sysname, not (N)VARCHAR:
"character_string is sysname and is limited to 128 characters. Inputs greater than 128 characters return NULL."
Instead try:
SELECT QuestionList = cast(STUFF((
SELECT ',' + '[' + (cast(question AS NVARCHAR(max)) + ']')
FROM (
VALUES (REPLICATE('a', 130))
)q(question)
FOR XML PATH('')
), 1, 1, '') AS NVARCHAR(max))
Just as another way of achieving this. This method achieves the same without using XML so that you aren't restricted to certain characters. It itterates through your table, building the string with each row, with the last instance being set to your variable #QuestionList.
Declare #QuestionList AS NVARCHAR(max)
SELECT
#QuestionList = isnull(#QuestionList + ', ', '') + question
FROM
questions
ORDER BY
AgpdbQuestionID
It is important to use the isnull, as this achives ommiting the first comma when the existing string is null.
I'd be intregeagued to see how efficient this is compared to the XML method, but this has been useful for myself when I've needed ceratin characters like >, <, " and '

Substring not working in all my cases, how could I fix it?

I have a substring query which works but it breaks if there are no wordings after my searched query. I don't know how to fix it.
I tried removing and changing the numbers +1 +2 -1 -2 after the len but no chance to get it work.
declare
#FullText nvarchar(400) = 'My Code in ''ABC123, CDE456'' and my digit 1',
#KnownBeginning nvarchar(400) = 'Code in ''',
#KnownEnding nvarchar(400) = ''' '
select SUBSTRING(#FullText, CHARINDEX(#KnownBeginning, #FullText) + LEN(#KnownBeginning) ,
CHARINDEX(#KnownEnding,#FullText) - (CHARINDEX(#KnownBeginning, #FullText)
+ LEN(#KnownBeginning)))
--#FullText2 nvarchar(400) = 'My Code in ''ABC123, CDE456'''
The code works above, but if we replace the #FullText to the #FullText2 which there is no text after '' - "and my digit 1" the select breaks.
It seems that the substring works if there are any text after the searched string is found. But if there are nothing else after ''ABC123, CDE456'' it says:
Msg 537, Level 16, State 3, Line 7
Invalid length parameter passed to the LEFT or SUBSTRING function.
The select should return always, if there is or there isn't text after quotes.
You might try something along this:
DECLARE #tbl TABLE(YourString VARCHAR(100));
INSERT INTO #tbl VALUES
('My Code in ''ABC123, CDE456'' and my digit 1')
,('My Code in ''ABC123, CDE456''');
SELECT t.YourString
,A.CastedToXml.query('.') AS ThisIsHowItLooksLike
,A.CastedToXml.value('/x[2]','varchar(100)') AS TheSecondElement
FROM #tbl t
CROSS APPLY(SELECT CAST('<x>' + REPLACE(t.YourString,'''','</x><x>') + '</x>' AS XML)) A(CastedToXml);
Click the XML node to see the intermediate, casted value. We can use .value() to pick the second <x>, which is your data.
Alternatively you can follow your own approach along this:
DECLARE #KnownBeginning nvarchar(400) = 'Code in ''',
#KnownEnding nvarchar(400) = ''''; --<-- Without the blank!
SELECT t.YourString
,SUBSTRING(t.YourString,A.StartPosition,B.EndPosition-A.StartPosition) AS TheCodes
FROM #tbl t
CROSS APPLY(SELECT CHARINDEX(#KnownBeginning,t.YourString) + LEN(#KnownBeginning)) A(StartPosition)
OUTER APPLY(SELECT CHARINDEX(#KnownEnding,t.YourString,A.StartPosition+1)) B(EndPosition);
I use APPLY to calculate values row-wise. This allows for using computed values similar to variables in procedural approaches. The first APPLY computes the starting point, while the second APPLY uses the StartPosition as offset. Therefore we do not need the blank after the quote.

sql to check string contains with where clause

in Sql server
I have a following string
DECLARE #str nvarchar(max);
set #str = "Hello how are you doing today,Its Monday and 5 waiting days";
DECLARE #srch nvarchar(max);
set #srch = " how,doing,monday,waiting";
Now i want to check whether str contains any of string (comma separated string) of srch
I want it in only sql server
is there possibilites to write some query with in clause
like
select from #str where _____ in (select * from CommaSplit(#srch)
where CommaSplit function rerturns rows of #srch comma separted value
I dont want to use cursor or any loop concept as the #srch value can be very long
Thanks
you can use same function to get first string in rows
select string from CommaSplit(#srch,'') where string in (select * from CommaSplit(#srch)
You can use the following common table expressions query to split your string into parts. cte will contain one record per phrase in #srch. In my example below, I show where in #str each of the search phrase is located. It returns 0 if it cannot locate a search phrase.
Note 1: it won't show the location twice if your search phrase is duplicated - you would need another CTE for that.
Note 2: I have to add comma at the end of #srch to make my CTE work. You can do that inside the CTE if you prefer not to change the search string.
DECLARE #str nvarchar(max);
set #str = 'Hello how are you doing today,Its Monday and 5 waiting days';
DECLARE #srch nvarchar(max);
set #srch = 'how,doing,monday,waiting';
set #srch = #srch + ','
-- first split the text into 1 character per row
;with cte
as
(
select substring(#srch, 1, CHARINDEX(',', #srch, 1) - 1) as Phrase, CHARINDEX(',', #srch, 1) as Idx
union all
select substring(#srch, cte.Idx + 1, CHARINDEX(',', #srch, cte.Idx + 1) - cte.Idx - 1) as Phrase, CHARINDEX(',', #srch, cte.Idx + 1) as Idx
from cte
where cte.Idx < CHARINDEX(',', #srch, cte.Idx + 1)
)
select charindex(cte.Phrase, #str, 1) from cte
I don't think that the IN clause is what you need. Instead of this you can use the LIKE construction as following:
if (select count(*) from CommaSplit(#srch) where #str like '%' + val + '%') > 0
select 'true'
else
select 'false'
In this case you will receive 'true' when at least 1 result of CommaSplit function exists in the #str text. But in this case you also will receive a 'true' value when the result of CommaSplit function is a part of the word in the #str string.
If you need more accurate solution, this can be achieved by the following way: you need to split the #str into the words (also replacing punctuation by spaces beforehand). And, after this, intersect of CommaSplit (#srch) and SpaceSplit(#str) will be the answer on the question. Among this, you also will be able to check which words are matching between two strings.
The overhead of this method is to create function SpaceSplit which is copy of CommaSplit but with another separator. Or the function CommaSplit can be modified to receive a separator as parameter.

Search Entire Database To find extended ascii codes in sql

We have issues with extended ascii codes getting in our database (128-155)
Is there anyway to search the entire database and display the results of any of these characters that may be in there and where they are located within the tables and columns.
Hope that makes sense.
I have the script to search entire DB, but having trouble with opening line.
DECLARE #SearchStr nvarchar(100)
SET #SearchStr != between char(32) and char(127)
I have this originally that works, but I need to extend the range I'm looking for.
SET #SearchStr = '|' + char(9) + '|' + char(10) + '|' + char(13)
Thanks
It's very unclear what your data looks like, but this might help you to get started:
declare #TestData table (String nvarchar(100))
insert into #TestData select N'abc'
insert into #TestData select N'def'
insert into #TestData select char(128)
insert into #TestData select char(155)
declare #SearchPattern nvarchar(max) = N'%['
declare #i int = 128
while #i <= 155
begin
set #SearchPattern += char(#i)
set #i += 1
end
set #SearchPattern += N']%'
select #SearchPattern
select String
from #TestData
where String like #SearchPattern
Of course you'll need to add some code to loop over every table and column that you want to query (see this question), and it's possible that this code will behave differently on different collations.
... where dodgyColumn is your column with questionable data ....
WHERE(patindex('%[' + char(127) + '-' + char(255) + ']%', dodgyColumn COLLATE Latin1_General_BIN2) > 0)
This works for us, to identify extended ASCII characters in our otherwise normal ASCII data (characters, numbers, punctuation, dollar and percent signs, etc.)

Is CHAR(14) not allowed in SQL Server T-SQL patindex range?

What's the problem with CHAR(13) or perhaps CHAR(14) in TSQL patindex?
As soon as I include CHAR(14) in a pattern, I get no records found.
Searching for an answer, I just found my own question (unanswered) from 2009 (here: http://www.sqlservercentral.com/Forums/Topic795063-338-1.aspx).
Here is another simple test, to show what I mean:
/* PATINDEX TEST */
DECLARE #msg NVARCHAR(255)
SET #msg = 'ABC' + NCHAR(13) + NCHAR(9) + 'DEF'
DECLARE #unwanted NVARCHAR(50)
-- unwanted chars in a "chopped up" string
SET #unwanted = N'%[' + NCHAR(1) + '-' + NCHAR(13) + NCHAR(14) + '-' + NCHAR(31) + ']%'
SELECT patindex(#unwanted, #msg)
-- Result: 4
-- NOW LET THE unwanted string includ the whole range from 1 to 31
SET #unwanted = '%['+NCHAR(1)+'-'+NCHAR(31)+']%' -- -- As soon as Char(14) is included, we get no match with patindex!
SELECT patindex(#unwanted, #msg)
-- Result: 0
It is permitted.
You need to bear in mind that the ranges are based on collation sort order not character codes however so perhaps in your default collation it sorts in a position that you do not expect.
What is your database's default collation?
What does the following return?
;WITH CTE(N) AS
(
SELECT 1 UNION ALL
SELECT 9 UNION ALL
SELECT 13 UNION ALL
SELECT 14 UNION ALL
SELECT 31
)
SELECT N
FROM CTE
ORDER BY NCHAR(N)
For me it returns
N
-----------
1
14
31
9
13
So both characters 9 and 13 are outside the range 1-31. Hence
'ABC' + NCHAR(13) + NCHAR(9) + 'DEF' NOT LIKE N'%['+NCHAR(1)+N'-'+NCHAR(31)+N']%'
Which explains the results in your question. Character 14 doesn't enter into it.
You can use a binary collate clause to get it to sort more as you were expecting. e.g.
SELECT patindex(#unwanted COLLATE Latin1_General_100_BIN, #msg)
Returns 4 in the second query too.

Resources