How to generate alphanumeric tokens in SQL Server? - sql-server-2005

I am looking to generate soem alpha numeric tokens. Is there a function that can do to generate set length tokens?

You can do this and then take a substring of it of whatever length you need:
select replace(newid(), '-', '')
E.g., for eight characters:
select substring(replace(newid(), '-', ''), 1, 8)

I have done this by combining a unique id from the table with a random number:
update [table]
set token = cast([id] as varchar(10)) + cast(cast(round((rand() * 500000000.0), 0) as int) as varchar(10))
Adjust the varchar sizes and the multiplication factor to get the toekn size you need. If you need a precise size, make the resulting string a little too long and use substring to set the length.
This is not crytographically strong, but it works for me in most cases. Using the id field from the table guarantees that the token is unique, and the random number makes it difficult to guess.

Another possible solution (for SQL Server, i think NEWID() is not available in other DBMS but use another similar function):
SELECT CONVERT(VARCHAR(10), RIGHT(NEWID(), 5))
The NEWID() function returns a Alpha numeric ID generated by SQL Server which you can use in the way you need.
Works well for me.

Related

Problem with using SUBSTRING and CHARINDEX

I have a column (RCV1.ECCValue) in a table which 99% of the time has a constant string format- example being:
T0-11.86-273
the middle part of the two hyphens is a percentage. I'm using the below sql to obtain this figure which is working fine and returns 11.86 on the above example. when the data in that table is in above format
'Percentage' = round(SUBSTRING(RCV1.ECCValue,CHARINDEX('-',RCV1.ECCValue)+1, CHARINDEX('-',RCV1.ECCValue,CHARINDEX('-',RCV1.ECCValue)+1) -CHARINDEX('-',RCV1.ECCValue)-1),2) ,
However...this table is updated from an external source and very occasionally the separators differ, for example:
T0-11.86_273
when this occurs I get the error:
Invalid length parameter passed to the LEFT or SUBSTRING function.
I'm very new to SQL and have got myself out of many challenges but this one has got me stuck. Any help would be mostly appreciated. Is there a better way to extract this percentage value?
Replace '_' with '-' to string in CHARINDEX while specifying length to the substring
'Percentage' = round(SUBSTRING(RCV1.ECCValue,CHARINDEX('-',RCV1.ECCValue)+1, CHARINDEX('-',replace(RCV1.ECCValue,'_','-'),CHARINDEX('-',RCV1.ECCValue)+1) -CHARINDEX('-',RCV1.ECCValue)-1),2) ,
If you can guarantee the structure of these strings, you can try parsename
select round(parsename(translate(replace('T0-11.86_273','.',''),'-_','..'),2), 2)/100
Breakdown of steps
Replace . character in the percentage value with empty string using replace.
Replace - or _, whichever is present, with . using translate.
Parse the second element using parsename.
Round it up to 2 digits, which will also
automatically cast it to the desired numeric type.
Divide by 100
to restore the number as percentage.
Documentation & Gotchas
Use NULLIF to null out such values
round(
SUBSTRING(
RCV1.ECCValue,
NULLIF(CHARINDEX('-', RCV1.ECCValue), 0) + 1,
NULLIF(CHARINDEX('-',
RCV1.ECCValue,
NULLIF(CHARINDEX('-', RCV1.ECCValue), 0) + 1
), 0)
- NULLIF(CHARINDEX('-', RCV1.ECCValue), 0) - 1
),
2)
I strongly recommend that you place the repeated values in CROSS APPLY (VALUES to avoid having to repeat yourself. And do use whitespace, it's free.

SQL Query to select a value between two known strings

I need a SQL query to get the value between two known strings in a text column.
The column name is d_info and the table name is Details.
The text is an XML fragment, but stored as a text value.
What I need is to get the value between the bookends <nettoeinkommen> and </nettoeinkommen> which is 718 in this example.
I also need the output to be saved in new column named income with data type float(8).
land>DE</land></wohnanschrift><taetigkeit>rentner</taetigkeit><dkbkundenstatus><bestandskunde>false</bestandskunde></dkbkundenstatus><haushaltsangaben><einnahmen><einkommen><nettoeinkommen>718</nettoeinkommen></einkommen><kindergeld>0</kindergeld><vermietungverpachtungnetto>0</vermietungverpachtungnetto><elterngeld>0</elterngeld><rentenunbefristet>0</rentenunbefristet><unselbststaendigetaetigkeit>740</unselbststaendigetaetigkeit><geringfuegigebeschaeftigung>0</geringfuegigebeschaeftigung></einnahmen><ausgaben><warmmiete>550</warmmiete><ratenimmobilienfinanzierung>0</ratenimmobilienfinanzierung>
I tried this code:
SELECT cast(SUBSTRING(d_info, CHARINDEX('<nettoeinkommen>', d_info)
, CHARINDEX('</nettoeinkommen>', d_info) - CHARINDEX('<nettoeinkommen>', d_info)) as float(8)) as income
from dbo.Details
But it's returning an Error converting data type varchar to real.
When I remove the cast function, the script works but it returns <nettoeinkommen>718 instead of only 718.
Thanks.
It is starting at the start of the tag not the end of it.
SELECT cast(
SUBSTRING(
d_info,
CHARINDEX('<nettoeinkommen>', d_info) + len('<nettoeinkommen>'),
CHARINDEX('</nettoeinkommen>', d_info) - (CHARINDEX('<nettoeinkommen>', d_info) + len('<nettoeinkommen>'))
) as float(8)) as income
from dbo.Details
you might even have these defined in variables:
SELECT cast(
SUBSTRING(
d_info,
CHARINDEX(#startTag, d_info) + len(#startTag),
CHARINDEX(#endTag, d_info) - (CHARINDEX(#startTag,d_info)+ len(#startTag))
) as float(8)) as income
from dbo.Details
I think the code is much easier to understand with the variables.
You need to add the length of your opening tag from the start index and subtract from the length of your substring statement:
SUBSTRING(d_info, CHARINDEX('<nettoeinkommen>', d_info)+16,
CHARINDEX('</nettoeinkommen>', d_info) - CHARINDEX('<nettoeinkommen>', d_info)-16)
As it seems, you are querieing plain xml data, for such purpose sql-server provides xquery functionality:
SELECT CAST(r.d_info AS XML).value('(/haushaltsangaben/einnahmen/einkommen/nettoeinkommen)[1]', 'decimal(19,2)')
FROM
(
SELECT '<taetigkeit>rentner</taetigkeit>
<dkbkundenstatus>
<bestandskunde>false</bestandskunde>
</dkbkundenstatus>
<haushaltsangaben>
<einnahmen>
<einkommen>
<nettoeinkommen>718</nettoeinkommen>
</einkommen>
</einnahmen>
</haushaltsangaben>' AS d_info
) AS r
If you intend to query more info from your source, you will end up with a bunch of stacked substring, patindex functions or even your own defined functions. This should be more readable and mantainable.
Using XQuery: https://learn.microsoft.com/en-us/sql/t-sql/xml/query-method-xml-data-type
As for your initial issue The SUBSTRING function in SQL returns the subset from a string starting from a given index for a specific length. For example SELECT SUBSTRING('whatever',5,4) returns 'ever'.
In case of CHARINDEX it gives the index for the first found match of a given pattern within a string. Example SELECT CHARINDEX('ever','whatever') should return 5, as 'ever' starts at the fifth position in 'whatever').
Now in your case you need to add the length of '<nettoeinkommen>' to the starting charindex and substract the length of '</nettoeinkommen>' from the length of the substring:
Also consider using decimal or numeric type instead of float, if you need to precise calculations: https://technet.microsoft.com/en-us/library/ms187912(v=sql.105).aspx

How to substring records with variable length

I have a table which has a column with doc locations, such as AA/BB/CC/EE
I am trying to get only one of these parts, lets say just the CC part (which has variable length). Until now I've tried as follows:
SELECT RIGHT(doclocation,CHARINDEX('/',REVERSE(doclocation),0)-1)
FROM Table
WHERE doclocation LIKE '%CC %'
But I'm not getting the expected result
Use PARSENAME function like this,
DECLARE #s VARCHAR(100) = 'AA/BB/CC/EE'
SELECT PARSENAME(replace(#s, '/', '.'), 2)
This is painful to do in SQL Server. One method is a series of string operations. I find this simplest using outer apply (unless I need subqueries for a different reason):
select *
from t outer apply
(select stuff(t.doclocation, 1, patindex('%/%/%', t.doclocation), '') as doclocation2) t2 outer apply
(select left(tt.doclocation2), charindex('/', tt.doclocation2) as cc
) t3;
The PARSENAME function is used to get the specified part of an object name, and should not used for this purpose, as it will only parse strings with max 4 objects (see SQL Server PARSENAME documentation at MSDN)
SQL Server 2016 has a new function STRING_SPLIT, but if you don't use SQL Server 2016 you have to fallback on the solutions described here: How do I split a string so I can access item x?
The question is not clear I guess. Can you please specify which value you need? If you need the values after CC, then you can do the CHARINDEX on "CC". Also the query does not seem correct as the string you provided is "AA/BB/CC/EE" which does not have a space between it, but in the query you are searching for space WHERE doclocation LIKE '%CC %'
SELECT SUBSTRING(doclocation,CHARINDEX('CC',doclocation)+2,LEN(doclocation))
FROM Table
WHERE doclocation LIKE '%CC %'

How to update text using "regular expressions" in SQL Server?

In a column in a SQL Server database table, the value has a format of X=****;Y=****;Z=5****, where the asterisks represent strings of any lengths and of any values. What I need to do is to change that 5 to a 4 and keep the rest of the string unchanged.
Is there a way to use something like regular expressions to achieve what I want to do? If not using regular expressions, can it be done at all?
MS SQL sadly doesn't have any built in regex support (although it can be added via CLR) but if the format is fixed so that the part you want to change isZ=5toZ=4then usingREPLACEshould work:
REPLACE(your_string,'Z=5','Z=4')
For example:
declare #t table (str varchar(max))
insert #t values
('X=****;Y=****;Z=5****'),
('X=****;Y=**df**;Z=3**sdf**'),
('X=11**;Y=**sdfdf**;Z=5**')
update #t
set str = replace(str,'Z=5','Z=4')
-- or a slightly more ANSI compliant and portable way
update #t
set str = SUBSTRING(str,0, CHARINDEX('Z=5', str)) + 'Z=4' + SUBSTRING(str, CHARINDEX('Z=5', str)+3,LEN(str))
select * from #t
str:
X=****;Y=****;Z=4****
X=****;Y=**df**;Z=3**sdf**
X=11**;Y=**sdfdf**;Z=4**
We need more information. Under what circumstances should 5 be replaced by 4? If it's just where it occurs as the first character after the Z=, then you could simply do...
set Col = Replace(Col,'Z=5','Z=4')
Or, do you just want to replace 5 with 4 anywhere in the column value. In which case you'd obviously just do...
set Col = Replace(Col,'5','4')
Or possibly you mean that 5's should be replaced by 4's anywhere within the value after Z= which would be a lot harder.
update Table set Field = replace(Field, ';Z=5', ';Z=4')
And let's hope that your asterisked data doesn't contain semicolons and equality signs...

Search half the refID in SQL

I have a SQL table which hold unique REFID (int) and many other columns. I wanted to search a row using the half REFID . So if someone just search 0001 then 50001, 00015... comes up.
I have tried:
SELECT TOP 10 REFID
FROM Tablename
where REFID LIKE '%' + cast(0001 as varchar(10)) +'%'
however the problem is, it also giving me 150100 however I wanted 0001 to be in order.
'0001' is passed in as a parameter passed in from my C# application. I know I can convert the '0001' to string/varchar before sending it to the SQL however I was looking for a way to do it within the SQL so I can pass in the int from C# application
Code:
SELECT TOP 10 REFID
FROM Tablename
where REFID LIKE '%0001%'
0001 is a number and when converted to varchar() it will become '1'.
This will work with any number but only if you know beforehand that you will use four characters in your expression.
SELECT TOP 10 REFID
FROM Tablename
where REFID LIKE '%' + RIGHT('0000' + CAST(0001 AS VARCHAR(4)), 4) +'%'
We don't know how are you building your SQL statement, so we may need more information in order to help. Where do you get your ' 0001' value from? Is it a variable? Is it a parameter in a stored procedure? Is it inside a function in a different programming language?
You need to compare the REFID to a string value (not an int: as the comments point out, CAST(0001 AS VARCHAR(10)) returns 1, not 0001.
SELECT TOP 10 REFID
FROM Tablename
where REFID LIKE '%0001%'
EDIT: you have bigger issues too, like how to search for an integer value stored without leading zeroes, but if you are passing in a parameter you need to either make it varchar, or convert it to varchar in your query body, like so (assuming, of course, that you are always searching for a four-digit string):
SET #SearchParam_char = RIGHT('000' + CAST(#searchParam_Int AS VARCHAR(10)), 4)
I have found:
cast('0001' as varchar(10)) as 0001 === 1 thanks to ALEX K.
SQL will strip leading zero and there is no way of keeping the zero if you don't know the length.
My solution: I will send a string from my application and let SQL search it using the string.