Reporting Services - String Length being changed with UNION ALL T-SQL - sql-server

I have one data_source returning 2 rows from a query with one string with 8chars and another with 11chars. I'm really sure that one of the strings has 8 chars because i explicit put it in the where condition( something like this . where name = 'ftcc_ppp') .The truth is that reporting services is adding some extra blank chars and is fixing the length of the string to the max(length string of all the resultset without the where condition).
I wanted to make some filters and was having the wrong results because the string was not with 8 chars, but instead with 11 chars(8 + 3blank).
The where condition is done in a sub-query that has in that field one row with 8 chars and another with 11 chars(ftcc_ppp and ftcc_ppp_lx).
why reporting services act like this ? Any explanation?
Thank you all
EDIT: Query code:
BEGIN
DECLARE #dtDate DATETIME
DECLARE #lastDay DATE;
DECLARE #firstDay DATE;
DECLARE #currentDate DATE;
DECLARE #month_table AS TABLE(DATA DATE);
SET #dtDate = #DATA;
SET #lastDay = CAST(DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#dtDate)+1,0)) AS DATE)
SET #firstDay = DATEADD(d,-DAY(#dtDate)+1,#dtDate);
SET #currentDate = #firstDay;
WHILE #currentDate <> dateadd(d,1,#lastDay)
BEGIN
PRINT #currentDate;
INSERT INTO #month_table VALUES(#currentDate);
SET #currentDate = dateadd(d,1,#currentDate);
END
SELECT * FROM (
SELECT DATA,motivo,SUM(total) AS total,CAST(campanha AS VARCHAR(11)) AS campanha,[TYPE]
FROM [Client].[dbo].[ftcc_ppp_motivo_nelegivel_totais]
GROUP BY DATA,motivo,campanha,[TYPE]
UNION ALL
SELECT DATA,motivo,0 AS total,CAST('ftcc_ppp' AS VARCHAR(11)) campanha,'LOP' AS [TYPE]
FROM #month_table
CROSS JOIN dbo.ftcc_ppp_motivo_nelegivel_keys
UNION ALL
SELECT DATA,motivo,0 AS total,CAST('ftcc_ppp' AS VARCHAR(11)) campanha,'ALOP' AS [TYPE]
FROM #month_table
CROSS JOIN dbo.ftcc_ppp_motivo_nelegivel_keys
UNION ALL
SELECT DATA,motivo,0 AS total,CAST('ftcc_ppp_lx' AS VARCHAR(11)) campanha,'CARD' AS [TYPE]
FROM #month_table
CROSS JOIN dbo.ftcc_ppp_motivo_nelegivel_keys
) xpto
WHERE campanha = 'ftcc_ppp'
END

I found the issue!! :) :)
The problem was in the hardcoded string.
Apparently hardcoded strings is a char type. So i had one char with 8 plus one char with 11.
The result obvious was 11 chars.
Using the len() in SQL SERVER i couldn't find the blank spaces inserted because SQLSERVER automatically removes the blank chars. When editing the rows inside SQL SERVER the cursor shown me the blank spaces. Great Lesson :)
Thank you all for the time given to my problem.
It was a pleasure to utilize for the first time StackOverflow.
Thank you all.
Greetings.

Another possible cause: Check your database compatibility level. If set to 80 you may have an issue, but 90 (or higher?) you wouldn't.
According to:
http://msdn.microsoft.com/en-us/library/bb510680.aspx
The UNION of a variable-length column and a fixed length column
produces a fixed-length column.

Related

"Create sql function , select english characters?"

I am looking for a function that selects English numbers and letters only:
Example:
TEKA תנור ביל דין in HLB-840 P-WH לבן
I want to run a function and get the following result:
TEKA HLB-840 P-WH
I'm using MS SQL Server 2012
What you really need here is regex replacement, which SQL Server does not support. Broadly speaking, you would want to find [^A-Za-z0-9 -]+\s* and then replace with empty string. Here is a demo showing that this works as expected:
Demo
This would output TEKA in HLB-840 P-WH for the input you provided. You might be able to do this in SQL Server using a regex package or UDF. Or, you could do this replacement outside of SQL using any number of tools which support regex (e.g. C#).
SQL-Server is not the right tool for this.
The following might work for you, but there is no guarantee:
declare #yourString NVARCHAR(MAX)=N'TEKA תנור ביל דין in HLB-840 P-WH לבן';
SELECT REPLACE(REPLACE(REPLACE(REPLACE(CAST(#yourString AS VARCHAR(MAX)),'?',''),' ','|~'),'~|',''),'|~',' ');
The idea in short:
A cast of NVARCHAR to VARCHAR will return all characters in your string, which are not known in the given collation, as question marks. The rest is replacements of question marks and multi-blanks.
If your string can include a questionmark, you can replace it first to a non-used character, which you re-replace at the end.
If you string might include either | or ~ you should use other characters for the replacements of multi-blanks.
You can influence this approach by specifying a specific collation, if some characters pass by...
there is no build in function for such purpose, but you can create your own function, should be something like this:
--create function (split string, and concatenate required)
CREATE FUNCTION dbo.CleanStringZZZ ( #string VARCHAR(100))
RETURNS VARCHAR(100)
BEGIN
DECLARE #B VARCHAR(100) = '';
WITH t --recursive part to create sequence 1,2,3... but will better to use existing table with index
AS
(
SELECT n = 1
UNION ALL
SELECT n = n+1 --
FROM t
WHERE n <= LEN(#string)
)
SELECT #B = #B+SUBSTRING(#string, t.n, 1)
FROM t
WHERE SUBSTRING(#string, t.n, 1) != '?' --this is just an example...
--WHERE ASCII(SUBSTRING(#string, t.n, 1)) BETWEEN 32 AND 127 --you can use something like this
ORDER BY t.n;
RETURN #B;
END;
and then you can use this function in your select statement:
SELECT dbo.CleanStringZZZ('TEKA תנור ביל דין in HLB-840 P-WH לבן');
create function dbo.AlphaNumericOnly(#string varchar(max))
returns varchar(max)
begin
While PatIndex('%[^a-z0-9]%', #string) > 0
Set #string = Stuff(#string, PatIndex('%[^a-z0-9]%', #string), 1, '')
return #string
end

Select subset of delimited string field

My table has a field with data formatted like this:
Term 1~Term 2~Term 3~Term 4~Term 5~Term 6~
All non-blank values contain 6 tilde-separated strings, which may be several words long.
I need to extract the last 2 substrings from this field as part of a query.I'm not interested in splitting the data into multiple records, and I don't have permissions to create a stored procedure.
Thanks in advance for any advice.
DECLARE #Term VARCHAR(100)
SELECT #Term = 'abc~def~ghi~jkl~mno~pqr~'
SELECT RIGHT(#Term, CHARINDEX('~',REVERSE(#Term),CHARINDEX('~',REVERSE(#Term),2)+1)-1)
That will give the last two terms with ~ intact. Note you can wrap REPLACE() around that to put something other than the tilde in there.
another way to do is this.. use string_split (in 2016) or an equivalent UDF that can be found elsewhere.. to split the string
declare #term varchar(100) = 'abc~def~ghi~jkl~mno~pqr~'
; with mycte as (
select
value as string_value
, row_number() over (order by (select 1000) )as row_num
from string_split(#term,'~'))
select top 2 string_value
from mycte
where string_value<>''
order by row_num desc

SQL Server : Replace (Charindex)

I have a SQL Server table with numbers in column no:
12345670000115
14245670000116
58492010000118
I need a function that will remove one number 1 from right side of number, so result must be like:
1234567000015
1424567000016
5849201000018
I find some solutions to use charindex() with substring(), but my SQL skills are poor so I really need help.
Thanks
Assuming this is varchar data here is an easy way to accomplish this. BTW, I would suggest you not use column names like 'no'. It is a reserved word and it is horribly ambiguous. Does that mean number or the opposite of yes? If it is number as I assume it would be better to name the column with an indication of what the number is. PartNumber, ItemNumber, CatalogNumber whatever...
LEFT(no, len(no) - 2) + RIGHT(no, 1)
Try to use this query:
declare #charToReplace char = '1'
select REVERSE(stuff(REVERSE(no), charindex(#charToReplace, REVERSE(no)), 1, ''))
from table
or
declare #charToReplace char = '1'
declare #tmp_table TABLE (NO varchar(16))
insert into #tmp_table
select REVERSE(NO)
from yourtable
select REVERSE(stuff(NO, charindex(#charToReplace, NO), 1, ''))
For your particular data, if the numbers fit a BIGINT, one easy way is to treat them like numbers:
Setup
create table #tmp (
number VARCHAR(16)
)
insert into #tmp values ('12345670000115'), ('14245670000116'), ('58492010000118')
GO
Script:
select number, cast( (cast(number AS bigint) - 100) / 100 * 10 + cast(number AS bigint) % 100 as VARCHAR(16))
from #tmp
GO
I resolve problem. There is answer in which I remove one character 1 and update whole table. Thanks all for help!
Update myTableName
set barcode=substring(barcode,1,11)+substring(barcode,13,1)
where len(barcode)>= 14

How to make unique random alphanumeric sequence in SQL Server

I want to make unique random alphanumeric sequence to be the primary key for a database table.
Each char in the sequence is either a letter (a-z) or number (0-9)
Examples for what I want :
kl7jd6fgw
zjba3s0tr
a9dkfdue3
I want to make a function that could handle that task!
You can use an uniqueidentifier. This can be generated with the NEWID() function:
SELECT NEWID()
will return something like:
BE228C22-C18A-4B4A-9AD5-1232462F7BA9
It is a very bad idea to use random strings as a primary key.
It will effect performance as well as storage size, and you will be much better of using an int or a bigint with an identity property.
However, generating a random string in SQL maybe useful for other things, and this is why I offer this solution:
Create a table to hold permitted char values.
In my example the permitted chars are 0-9 and A-Z.
CREATE TABLE Chars (C char(1))
DECLARE #i as int = 0
WHILE #i < 10
BEGIN
INSERT INTO Chars (C) VALUES (CAST(#i as Char(1)))
SET #i = #i+1
END
SET #i = 65
WHILE #i < 91
BEGIN
INSERT INTO Chars (C) VALUES (CHAR(#i))
SET #i = #i+1
END
Then use this simple select statement to generate a random string from this table:
SELECT TOP 10 C AS [text()]
FROM Chars
ORDER BY NEWID()
FOR XML PATH('')
The advantages:
You can easily control the allowed characters.
The generation of a new string is a simple select statement and not manipulation on strings.
The disadvantages:
This select results with an ugly name (i.e XML_F52E2B61-18A1-11d1-B105-00805F49916B). This is easily solved by setting the result into a local variable.
Characters will only appear once in every string. This can easily be solved by adding union:
example:
SELECT TOP 10 C AS [text()]
FROM (
SELECT * FROM Chars
UNION ALL SELECT * FROM Chars
) InnerSelect
ORDER BY NEWID()
FOR XML PATH('')
Another option is to use STUFF function instead of As [Text()] to eliminate those pesky XML tags:
SELECT STUFF((
SELECT TOP 100 ''+ C
FROM Chars
ORDER BY NEWID()
FOR XML PATH('')
), 1, 1, '') As RandomString;
This option doesn't have the disadvantage of the ugly column name, and can have an alias directly. Execution plan is a little different but it should not suffer a lot of performance lose.
Play with it yourself in this Sql Fiddle
If there are any more advantages / disadvantages you think of please leave a comment. Thanks.
NewID() Function will generate unique numbers.So i have incremented them with loop and picked up the combination of alpha numeric characters using Charindex and Left functions
;with list as
(
select 1 as id,newid() as val
union all
select id + 1,NEWID()
from list
where id + 1 < 100
)
select ID,left(val, charindex('-', val) - 2) from list
option (maxrecursion 0)
The drawback of NEWID() for this request is it limits the character pool to 0-9 and A-F. To define your own character pool, you have to role a custom solution.
This solution adapted from Generating random strings with T-SQL
--Define list of characters to use in random string
DECLARE #CharPool VARCHAR(255)
SET #CharPool = '0123456789abcdefghijkmnopqrstuvwxyz'
--Store length of CharPool for use later
DECLARE #PoolLength TINYINT
SET #PoolLength = LEN(#CharPool) --36
--Define random string length
DECLARE #StringLength TINYINT
SET #StringLength = 9
--Declare target parameter for random string
DECLARE #RandomString VARCHAR(255)
SET #RandomString = ''
--Loop control variable
DECLARE #LoopCount TINYINT
SET #LoopCount = 0
--For each char in string, choose random char from char pool
WHILE(#LoopCount < #StringLength)
BEGIN
SELECT #RandomString += SUBSTRING(#Charpool, CONVERT(int, RAND() * #PoolLength), 1)
SELECT #LoopCount += 1
END
SELECT #RandomString
http://sqlfiddle.com/#!6/9eecb/4354
I must reiterate, however, that I agree with the others: this is a horrible idea.

SQL Server : converting varchar to INT

I am stuck on converting a varchar column UserID to INT. I know, please don't ask why this UserID column was not created as INT initially, long story.
So I tried this, but it doesn't work. and give me an error:
select CAST(userID AS int) from audit
Error:
Conversion failed when converting the varchar value
'1581............................................................................................................................' to data type int.
I did select len(userID) from audit and it returns 128 characters, which are not spaces.
I tried to detect ASCII characters for those trailing after the ID number and ASCII value = 0.
I have also tried LTRIM, RTRIM, and replace char(0) with '', but does not work.
The only way it works when I tell the fixed number of character like this below, but UserID is not always 4 characters.
select CAST(LEFT(userID, 4) AS int) from audit
You could try updating the table to get rid of these characters:
UPDATE dbo.[audit]
SET UserID = REPLACE(UserID, CHAR(0), '')
WHERE CHARINDEX(CHAR(0), UserID) > 0;
But then you'll also need to fix whatever is putting this bad data into the table in the first place. In the meantime perhaps try:
SELECT CONVERT(INT, REPLACE(UserID, CHAR(0), ''))
FROM dbo.[audit];
But that is not a long term solution. Fix the data (and the data type while you're at it). If you can't fix the data type immediately, then you can quickly find the culprit by adding a check constraint:
ALTER TABLE dbo.[audit]
ADD CONSTRAINT do_not_allow_stupid_data
CHECK (CHARINDEX(CHAR(0), UserID) = 0);
EDIT
Ok, so that is definitely a 4-digit integer followed by six instances of CHAR(0). And the workaround I posted definitely works for me:
DECLARE #foo TABLE(UserID VARCHAR(32));
INSERT #foo SELECT 0x31353831000000000000;
-- this succeeds:
SELECT CONVERT(INT, REPLACE(UserID, CHAR(0), '')) FROM #foo;
-- this fails:
SELECT CONVERT(INT, UserID) FROM #foo;
Please confirm that this code on its own (well, the first SELECT, anyway) works for you. If it does then the error you are getting is from a different non-numeric character in a different row (and if it doesn't then perhaps you have a build where a particular bug hasn't been fixed). To try and narrow it down you can take random values from the following query and then loop through the characters:
SELECT UserID, CONVERT(VARBINARY(32), UserID)
FROM dbo.[audit]
WHERE UserID LIKE '%[^0-9]%';
So take a random row, and then paste the output into a query like this:
DECLARE #x VARCHAR(32), #i INT;
SET #x = CONVERT(VARCHAR(32), 0x...); -- paste the value here
SET #i = 1;
WHILE #i <= LEN(#x)
BEGIN
PRINT RTRIM(#i) + ' = ' + RTRIM(ASCII(SUBSTRING(#x, #i, 1)))
SET #i = #i + 1;
END
This may take some trial and error before you encounter a row that fails for some other reason than CHAR(0) - since you can't really filter out the rows that contain CHAR(0) because they could contain CHAR(0) and CHAR(something else). For all we know you have values in the table like:
SELECT '15' + CHAR(9) + '23' + CHAR(0);
...which also can't be converted to an integer, whether you've replaced CHAR(0) or not.
I know you don't want to hear it, but I am really glad this is painful for people, because now they have more war stories to push back when people make very poor decisions about data types.
This question has got 91,000 views so perhaps many people are looking for a more generic solution to the issue in the title "error converting varchar to INT"
If you are on SQL Server 2012+ one way of handling this invalid data is to use TRY_CAST
SELECT TRY_CAST (userID AS INT)
FROM audit
On previous versions you could use
SELECT CASE
WHEN ISNUMERIC(RTRIM(userID) + '.0e0') = 1
AND LEN(userID) <= 11
THEN CAST(userID AS INT)
END
FROM audit
Both return NULL if the value cannot be cast.
In the specific case that you have in your question with known bad values I would use the following however.
CAST(REPLACE(userID COLLATE Latin1_General_Bin, CHAR(0),'') AS INT)
Trying to replace the null character is often problematic except if using a binary collation.
This is more for someone Searching for a result, than the original post-er. This worked for me...
declare #value varchar(max) = 'sad';
select sum(cast(iif(isnumeric(#value) = 1, #value, 0) as bigint));
returns 0
declare #value varchar(max) = '3';
select sum(cast(iif(isnumeric(#value) = 1, #value, 0) as bigint));
returns 3
I would try triming the number to see what you get:
select len(rtrim(ltrim(userid))) from audit
if that return the correct value then just do:
select convert(int, rtrim(ltrim(userid))) from audit
if that doesn't return the correct value then I would do a replace to remove the empty space:
select convert(int, replace(userid, char(0), '')) from audit
This is how I solved the problem in my case:
First of all I made sure the column I need to convert to integer doesn't contain any spaces:
update data set col1 = TRIM(col1)
I also checked whether the column only contains numeric digits.
You can check it by:
select * from data where col1 like '%[^0-9]%' order by col1
If any nonnumeric values are present, you can save them to another table and remove them from the table you are working on.
select * into nonnumeric_data from data where col1 like '%[^0-9]%'
delete from data where col1 like '%[^0-9]%'
Problems with my data were the cases above. So after fixing them, I created a bigint variable and set the values of the varchar column to the integer column I created.
alter table data add int_col1 bigint
update data set int_col1 = CAST(col1 AS VARCHAR)
This worked for me, hope you find it useful as well.

Resources