Below is part of a SQL triggered that uses a subquery to return a value in the column TableString. The subquery will return the first two value and the "marker" for Fname, however nothing after that (not even the semicolon). The TableString column is an nvarchar that is set to 255 characters and captures all similar data during an insert or an update.
INSERT INTO TransactionLog (TransactionDate, Operator, TableName, Action
, TableString, UserId)
SELECT LastChangeDate
, 'Op'
, #tableName
, #action
, CAST('sNum:' + CAST(sNumber as nvarchar(10)) + ' entType:' + EntityType
+ ' Fname:' + ISNULL(FirstName, 'NULL')
+ ' Lname:' + ISNULL(LastName, 'NULL')
+ ' suff:' + ISNULL(NameSuffix, 'NULL')
+ ' corpName:' + ISNULL(CorporateName, 'NULL' )
+ ' ctrlId:' + ISNULL(CAST(ControlId as nvarchar(3)), 'NULL')
AS nvarchar(30)) as TableString
, LastChangeOperator
FROM deleted
Returned values in TableString:
sNum:1000024 entType:S Fname
This has nothing to do with ISNULL but rather, is the result of a truncation of data.
Pay attention to:
.. AS nvarchar(30)
That is, much more data is being omitted. (The output does not even contain "Fname:" in this case.)
As a side note, his return character count is 28 because Nvarchar(30) means, store 30 bytes and Unicode (N) is two bytes a letter, even if you are storing non-Unicode. The number in the varchar declare is not the number of characters, its the number of bytes to use. It really gets people when they use Nvarchar.
Related
I am trying to perform the below query where some variables are being used. This below SQL code is a part of a stored procedure. Idea is to dynamically set the target columns and its values based on FileKey.
DECLARE #TargetColNames nvarchar(max) = '';
DECLARE #SourceColNames nvarchar(max) = '';
DECLARE #SourceColNamesInsert nvarchar(max) = '';
DECLARE #UpdateColumns nvarchar(max) = '';
SELECT
CASE
WHEN #FileKey IN ('s_1','s_2')
THEN #TargetColNames = #TargetColNames + ' [CreatedUser], [UpdatedUser], [CreatedDateTime],[UpdatedDateTime],[IsDeleted],[DeletedOn]'
ELSE #TargetColNames = #TargetColNames + ' [CreatedUser], [UpdatedUser], [CreatedDateTime], [UpdatedDateTime]'
END,
#SourceColNames = CONCAT('CreatedUser','UpdatedUser','CreatedDateTime', 'UpdatedDateTime'),
#SourceColNamesInsert = CONCAT(''',#User, ''',''',#User, ''', 'Getdate()', 'Getdate()' ),
CASE
WHEN #FileKey IN ('s_1','s_2')
THEN #UpdateColumns = CONCAT('Target.UpdatedUser= ''',#User,''', 'Target.[IsDeleted]=0','Target.[DeletedOn]=null')
ELSE #UpdateColumns = CONCAT('Target.UpdatedUser= ''',#User,''', 'Target.UpdatedDateTime=Getdate()')
END
The above SQL statement throws an error:
Msg 102, Level 15, State 1, Procedure uspDynamicStageToPropLayer1, Line 165 [Batch Start Line 5]
Incorrect syntax near '='.
What am I missing here? Maybe this is quite a silly mistake...
Also, by doing concat with your quoted parts, you might allow SQL injection into your query even building dynamically. What if a user's name (or forced parameter has a leading single quote, then garbage injection such as
#User = ';drop table X --
Having said that, some of the stuff could be more simplified, such as
SELECT
#TargetColNames = #TargetColNames
+ ' [CreatedUser], [UpdatedUser], [CreatedDateTime], [UpdatedDateTime]'
+ CASE WHEN #FileKey IN ('s_1','s_2')
THEN ', [IsDeleted], [DeletedOn]'
else ''
end
For the insert, you will probably get a failure. If you look at a possible result of the #User
#SourceColNamesInsert = concat(''',#User, ''',''',#User, ''',
'Getdate()', 'Getdate()' ),
will result with the value below which is not what I think is intended. Notice no comma's between values because the triple ' is creating start and end literals, and leaves no actual comma between column insert values.
',#User, '',#User, 'Getdate()Getdate()
But instead...
select concat('''#User'', ''#User''', ', Getdate(), Getdate()' );
which will result in...
'#User', '#User', Getdate(), Getdate()
The ''' actually creates an opening quoted string immediately with the value after it, then the '' (double) closes the quoted string, but it also adds the comma separator before the next '' (double) to start second user and ''' (triple) to close the second #User, then adding comma and both getdate() calls.
'#User', '#User', Getdate(), Getdate()
Now, if the value for #User was 'Bob', and your intent was to have the string output as
'Bob', 'Bob', Getdate(), Getdate()
change to
select concat('''', #User, ''', ','''', #User, '''', ', Getdate(), Getdate()' );
The '''' (quad) means I want to open a string, do a single quote (by the inner two ''), and close this as its own string. then get the value of the #User. Then follow by an open string ' with '' for closing the quote around the name, then open a single quote to start the next, the comma before starting the next quote for the second user and closing it as well via '''', then the value of the user again, and finally closing the second user with close quote ''''. Finally adding a comma and getdate() calls. Yes, stupid tricky in the quoting.
An easier implementation without CONCAT() is just using + between each explicit part such as
select '''' + #User + '''' + ', ' + '''' + #User + '''' + ', Getdate(), getdate()' ;
where each '''' is a single quote thus resulting in
' + Bob + ' + , + ' + Bob + ' + , Getdate(), getdate()
resulting in
'Bob', 'Bob', Getdate(), getdate()
I'll leave the final UpdateColumns to you to confirm your intended output.
But, as mentioned, beware of possible SQL injection when you are dynamically building SQL statements with embedded parameter values as this appears to be doing.
Here is another way to approach this:
Declare #FileKey varchar(10) = 's_3'
, #TargetColNames nvarchar(max) = ''
, #SourceColNames nvarchar(max) = ''
, #SourceColNamesInsert nvarchar(max) = ''
, #UpdateColumns nvarchar(max) = '';
Set #TargetColNames = concat_ws(', ', '[CreatedUser]', '[UpdatedUser]', '[CreatedDateTime]', '[UpdatedDateTime]');
If #FileKey In ('s_1', 's_2')
Set #TargetColNames = concat_ws(', ', #TargetColNames, '[UpdatedDateTime]', '[IsDeleted]', '[DeletedOn]');
Print #TargetColNames;
Set #SourceColNames = concat_ws(', ', '[CreatedUser]', '[UpdatedUser]', '[CreatedDateTime]', '[UpdatedDateTime]');
Set #SourceColNamesInsert = concat_ws(', ', quotename('#User', char(39)), quotename('Getdate()', char(39)), quotename('Getdate()', char(39)));
Print #SourceColNames;
Print #SourceColNamesInsert;
In my LastName Column, I have either one name or two names. In some records, I have more than one empty space between the two names.
I will have to select the records which has more than one empty space in the field name.
declare #nam nvarchar(4000)
declare #nam1 nvarchar(4000)
set #nam = 'sam' + ' ' + 'Dev'
set #nam1 = 'ed' + ' ' + ' ' + 'Dev'
In the sample query, i expect the output value should be #nam1.
You can do this using LEN and REPLACE to replace the spaces from string and then get original length - replaced length and then check that in WHERE clause,
SELECT *
FROM
mytTable
WHERE
LEN(LastName)-LEN(REPLACE(LastName, ' ', '')) > 1
I have a large table of data where some of my columns contain line breaks. I would like to remove them and replace them with some spaces instead.
Can anybody tell me how to do this in SQL Server?
Thanks in advance
SELECT REPLACE(REPLACE(#str, CHAR(13), ''), CHAR(10), '')
This should work, depending on how the line breaks are encoded:
update t
set col = replace(col, '
', ' ')
where col like '%
%';
That is, in SQL Server, a string can contain a new line character.
#Gordon's answer should work, but in case you're not sure how your line breaks are encoded, you can use the ascii function to return the character value. For example:
declare #entry varchar(50) =
'Before break
after break'
declare #max int = len(#entry)
; with CTE as (
select 1 as id
, substring(#entry, 1, 1) as chrctr
, ascii(substring(#entry, 1, 1)) as code
union all
select id + 1
, substring(#entry, ID + 1, 1)
, ascii(substring(#entry, ID + 1, 1))
from CTE
where ID <= #max)
select chrctr, code from cte
print replace(replace(#entry, char(13) , ' '), char(10) , ' ')
Depending where your text is coming from, there are different encodings for a line break. In my test string I put the most common.
First I replace all CHAR(10) (Line feed) with CHAR(13) (Carriage return), then all doubled CRs to one CR and finally all CRs to the wanted replace (you want a blank, I put a dot for better visability:
Attention: Switch the output to "text", otherwise you wont see any linebreaks...
DECLARE #text VARCHAR(100)='test single 10' + CHAR(10) + 'test 13 and 10' + CHAR(13) + CHAR(10) + 'test single 13' + CHAR(13) + 'end of test';
SELECT #text
DECLARE #ReplChar CHAR='.';
SELECT REPLACE(REPLACE(REPLACE(#text,CHAR(10),CHAR(13)),CHAR(13)+CHAR(13),CHAR(13)),CHAR(13),#ReplChar);
I have the same issue, means I have a column having values with line breaks in it. I use the query
update `your_table_name` set your_column_name = REPLACE(your_column_name,'\n','')
And this resolves my issue :)
Basically '\n' is the character for Enter key or line break and in this query, I have replaced it with no space (which I want)
Keep Learning :)
zain
I researched this and found that a text column in SQL Server can store a lot more than 8000 characters. But when I run the following insert in the text column, it only inserts 8000 characters:
UPDATE a
SET [File] = b.Header + CHAR(13) + CHAR(10) + d.Detail + c.Trailer + CHAR(13) + CHAR(10) + CHAR(26)
FROM Summary a
JOIN #Header b ON b.SummaryId = a.SummaryId
JOIN #Trailer c ON c.SummaryId = a.SummaryId
JOIN #Detail d ON d.SummaryId = a.SummaryId
WHERE
a.SummaryId = #SummaryId
I am trying to generate a fixed width flat file and every row should be 3900 characters long, and they are in the respective temp tables. But when I do the insert in the permanent table, the Trailer data gets truncated.
I am adding char(10) + char(13) to add carriage return and line feed and char(26) for end of file, and it seems like they are adding characters to the fixed width layout.
According to http://msdn.microsoft.com/en-us/library/ms187993.aspx TEXT fields are deprecated. Use VARCHAR(MAX) fields instead. They should support 2GB in text.
The problem with your code is not the data type of the field that you store the value in, it's the type of the value that you put together to store in it.
The type of b.Header is not text but varchar, which is used as type for the whole expression. When the strings are concatenated, the result will be truncated to fit in a varchar value.
If you cast the first string to text, the whole expression gets that type, and can become longer than 8000 characters:
SET [File] = cast(b.Header as text) + CHAR(13) + CHAR(10) + d.Detail + c.Trailer + CHAR(13) + CHAR(10) + CHAR(26)
Naturally you should transition into using the new type varchar(max) instead of text, but that is not the reason for your problem.
Your source fields aren't VARCHAR(MAX), so there is an 8000 character limit when concatenating them together, you can fix this by casting the first source field in the concat list as VARCHAR(MAX):
UPDATE a
SET [File] = CAST(b.Header AS VARCHAR(MAX)) + CHAR(13) + CHAR(10) + d.Detail + c.Trailer + CHAR(13) + CHAR(10) + CHAR(26)
FROM Summary a
JOIN #Header b ON b.SummaryId = a.SummaryId
JOIN #Trailer c ON c.SummaryId = a.SummaryId
JOIN #Detail d ON d.SummaryId = a.SummaryId
WHERE a.SummaryId = #SummaryId
If you concat a thousand VARCHAR(25) fields together, the length of the resulting string would be 8000, as that's the limit of the VARCHAR() type when supplied a numeric length. VARCHAR(MAX) does not share this limit, but the concat list inherets the type of the first string supplied. It's an interesting behavior, but that's how it works.
TEXT is deprecated - don't use it! Use VARCHAR(MAX) instead!
I think you need to explicitly cast all columns that you use in your UPDATE statement to VARCHAR(MAX) in order for this to work:
UPDATE a
SET [File] = CAST(b.Header AS VARCHAR(MAX)) + CHAR(13) + CHAR(10) +
CAST(d.Detail AS VARCHAR(MAX)) + CAST(c.Trailer AS VARCHAR(MAX))) + CHAR(13) + CHAR(10) + CHAR(26)
FROM Summary a
JOIN #Header b ON b.SummaryId = a.SummaryId
JOIN #Trailer c ON c.SummaryId = a.SummaryId
JOIN #Detail d ON d.SummaryId = a.SummaryId
WHERE
a.SummaryId = #SummaryId
I have a stored procedure that is getting information from my employee table and returning the data.
There are 3 columns that are used:
B.[SiloDesc] + ' (' + B.[TitleDesc] + ') ' + B.[SkillSetDesc] as SkillSetDesc,
My issue is, if one of those happens to be null, it wont display any of the data. What is the best way to have it include the data regardless of if one of those fields are null.
You could use coalesce() or isnull() for each individual column... or you could simply use...
CONCAT ( string_value1, string_value2 [, string_valueN ] )
Takes a variable number of string arguments and concatenates them into a single string. It requires a minimum of two input values; otherwise, an error is raised. All arguments are implicitly converted to string types and then concatenated. Null values are implicitly converted to an empty string.
A.: Remove the parentheses when TitleDesc is null:
select concat(B.[SiloDesc], ' (' + B.[TitleDesc] + ')', ' ' + B.[SkillSetDesc])
Because of the way null is treated in sql, the expression ' (' + null + ')' results in null which concat() will treat as an empty string... which is kind of nice as it effectively removes the parentheses if the value is null.
B.: Keep the parentheses regardless:
select concat(B.[SiloDesc], ' (', B.[TitleDesc], ') ', B.[SkillSetDesc])
Samples:
select concat('john', ' (' + null + ')', ' adams') -- john adams
select concat('john', ' (test)', ' ' + null) -- john (test)
select concat('john', ' (the man)', ' adams') -- john (the man) adams
isnull(B.[SiloDesc], '')
+ ' (' + isnull(B.[TitleDesc], '') + ') '
+ isnull(B.[SkillSetDesc], '') as SkillSetDesc,