Equivalent strconv in Advantage SQL DB - sql

I am converting an access query that updates fields from ALL CAPS to Basically normal First Letter Capital, rest lower case. In access ive used strconv
Ive havent found a similar function in advantage sql db. Ive found upper and lcase, but those dont seem to work for me.
StrConv([City],3)
An import process brings in all caps to a field called City. So City Comes in as CHICAGO, my end result would be Chicago, and this works in access using strconv,3

This statement should work: SELECT Upper( Left( [field], 1 ) ) + Lower( SubString( [field], 2, Length( [field] )-1 ) ) FROM CustomersTbl

Never used advantage but a quick Google suggests something like the following should work.
CONCAT(SUBSTRING(field, 1, 1), LOWER(SUBSTRING(field, 2, LENGTH(field) - 1)))

Advantage doesn't have a StrConv function, but there are a few ways to do the same thing.
I've used a scripting variable in this code segment to emulate the field. You can replace all instances of cityname with the name of your database column (field). Pick the one you like. system.iota is a one-row system database that can be used in Advantage when you don't want to use a real table for testing functions. Of course, you'd replace it with your actual table name in your own code. You can run each of the sample select statements (individually) in the Advantage Database Utility to test them; pick the one that seems the cleanest to you, as they're all the same as far as performance goes. (NOTE: UCase() and UpperCase() are the exact same functions internally; they both exist simply because some users expect to find UCase() and others UpperCase() depending on the programming language they're using. The same is true of LCase() and LowerCase().)
I've intentionally used a mixed bag of UPPER/lower case letters in the variable cityname below to demonstrate that the actual case doesn't matter; the statements provided will produce a proper-cased (first letter upper, rest lower) result.
declare cityname string;
set cityname = 'cHIcAGo';
-- Method 1
select
UCase(Substring(cityname, 1, 1)) + LCASE(SubString(cityname, 2, Length(cityname))) as city
from system.iota
-- Method 2
select
UpperCase(Left(cityname, 1)) + LowerCase(SubString(cityname, 2, Length(cityname)) as city
from system.iota
-- Method 3
select
UCase(Left(cityname, 1)) + LCase(Right(cityname, Length(city) - 1)) as city
from system.iota

Related

Error checking in T-SQL Script

I am self taught in T-SQL, so I am sure that I can gain efficiency in my code writing, so any pointers are welcomed, even if unrelated to this specific problem.
I am having a problem during a nightly routine I wrote. The database program that is creating the initial data is out of my control and is loosely written, so I have bad data that can blow up my script from time to time. I am looking for assistance in adding error checking into my script so I lose one record instead of the whole thing blowing up.
The code looks like this:
SELECT convert(bigint,(SUBSTRING(pin, 1, 2)+ SUBSTRING(pin, 3, 4)+ SUBSTRING(pin, 7, 5) + SUBSTRING(pin, 13, 3))) AS PARCEL, taxyear, subdivisn, township, propclass, paddress1, paddress2, pcity
INTO [ASSESS].[dbo].[vpams_temp]
FROM [ASSESS].[dbo].[Property]
WHERE parcelstat='F'
GO
The problem is in the first part of this where the concatenation occurs. I am attempting to convert this string (11-1111-11111.000) into this number (11111111111000). If they put their data in correctly, there is punctuation in exactly the correct spots and numbers in the right spots. If they make a mistake, then I end up with punctuation in the wrong spots and it creates a string that cannot be converted into a number.
How about simply replacing "-" and "." with "" before CONVERT to BIGINT?
To do that you would simply replace part of your code with
SELECT CONVERT(BIGINT,REPLACE(REPLACE(pin,"-",""), ".","")) AS PARCEL, ...
Hope it helps.
First, I would use replace() (twice). Second, I would use try_convert():
SELECT try_convert(bigint,
replace(replace(pin, '-', ''), '.', '')
) as PARCEL,
taxyear, subdivisn, township, propclass, paddress1, paddress2, pcity
INTO [ASSESS].[dbo].[vpams_temp]
FROM [ASSESS].[dbo].[Property]
WHERE parcelstat = 'F' ;
You might want to check if there are other characters in the value:
select pin
from [ASSESS].[dbo].[Property]
where pin like '%[^-0-9.]%';
Why not just:
select cast(replace(replace('11-1111-11111.000','-',''),'.','') as bigint)
simply, use the next code:-
declare #var varchar(100)
set #var = '11-1111-11111.000'
select convert(bigint, replace(replace(#var,'-',''),'.',''))
Result:-
11111111111000

Can 2 character length variables cause SQL injection vulnerability?

I am taking a text input from the user, then converting it into 2 character length strings (2-Grams)
For example
RX480 becomes
"rx","x4","48","80"
Now if I directly query server like below can they somehow make SQL injection?
select *
from myTable
where myVariable in ('rx', 'x4', '48', '80')
SQL injection is not a matter of length of anything.
It happens when someone adds code to your existing query. They do this by sending in the malicious extra code as a form submission (or something). When your SQL code executes, it doesn't realize that there are more than one thing to do. It just executes what it's told.
You could start with a simple query like:
select *
from thisTable
where something=$something
So you could end up with a query that looks like:
select *
from thisTable
where something=; DROP TABLE employees;
This is an odd example. But it does more or less show why it's dangerous. The first query will fail, but who cares? The second one will actually work. And if you have a table named "employees", well, you don't anymore.
Two characters in this case are sufficient to make an error in query and possibly reveal some information about it. For example try to use string ')480 and watch how your application will behave.
Although not much of an answer, this really doesn't fit in a comment.
Your code scans a table checking to see if a column value matches any pair of consecutive characters from a user supplied string. Expressed in another way:
declare #SearchString as VarChar(10) = 'Voot';
select Buffer, case
when DataLength( Buffer ) != 2 then 0 -- NB: Len() right trims.
when PatIndex( '%' + Buffer + '%', #SearchString ) != 0 then 1
else 0 end as Match
from ( values
( 'vo' ), ( 'go' ), ( 'n ' ), ( 'po' ), ( 'et' ), ( 'ry' ),
( 'oo' ) ) as Samples( Buffer );
In this case you could simply pass the value of #SearchString as a parameter and avoid the issue of the IN clause.
Alternatively, the character pairs could be passed as a table parameter and used with IN: where Buffer in ( select CharacterPair from #CharacterPairs ).
As far as SQL injection goes, limiting the text to character pairs does preclude adding complete statements. It does, as others have noted, allow for corrupting the query and causing it to fail. That, in my mind, constitutes a problem.
I'm still trying to imagine a use-case for this rather odd pattern matching. It won't match a column value longer (or shorter) than two characters against a search string.
There definitely should be a canonical answer to all these innumerable "if I have [some special kind of data treatment] will be my query still vulnerable?" questions.
First of all you should ask yourself - why you are looking to buy yourself such an indulgence? What is the reason? Why do you want add an exception to your data processing? Why separate your data into the sheep and the goats, telling yourself "this data is "safe", I won't process it properly and that data is unsafe, I'll have to do something?
The only reason why such a question could even appear is your application architecture. Or, rather, lack of architecture. Because only in spaghetti code, where user input is added directly to the query, such a question can be ever occur. Otherwise, your database layer should be able to process any kind of data, being totally ignorant of its nature, origin or alleged "safety".

DB2 Get value based on 5 character of field

I am working with a pretty badly designed table. I have a field called optional fields which for some reason has been used as a catch-all for someone who didn't want to create the table correctly.
I need to make a query where I look at this optional_fields value and do a comparison on the fifth value of the string in optional_fields.
The value from this field is something like NN14YN...N
My query would be something like:
SELECT COMPANY_NUMBER
FROM table
WHERE fifth character of OPtional Fields = 'Y'
Looking at the supported string functions in DB2 (according to the documentation for DB2 for Linux UNIX and Windows 9.7.0) it would seem that substr could be used:
SELECT COMPANY_NUMBER
FROM table
WHERE substr(optional_Fields,5,1) = 'Y'
In addition to the great answer from #jpw, if you for some reason need to check multiple positions within the string (which I have unfortunately had to do at one time), you can use an IN, and invert the "normal" order, like so:
...
WHERE 'Y' in (
substr(t.flags_field, 123, 1)
,substr(t.flags_field, 19, 1)
,substr(t.flags_field, 128, 1)
,substr(t.flags_field, 1, 1)
)
Just thought I would share. It surprised me the first time I used it!

Parse a string before the Last Index Of a character in SQL Server

I started with this but is it the best way to perform the task?
select
reverse(
substring(reverse(some_field),
charindex('-', reverse(some_field)) + 1,
len(some_field) - charindex('-', reverse(some_field))))
from SomeTable
How does SQL Server treat the
multiple calls to
reverse(some_field)?
Besides a UDF and iterating through
the string looking for charindex
of the '-' and storing the last
index of it, is there a more
efficient way to perform this task in T-SQL?
Note that what I have works, I just am really wondering if it is the best way about it.
Below are some sample values for some_field.
s2-st, s1-st, s3-st, s3-sss-zzz, s4-sss-zzzz
EDIT:
Sample output for this would be...
s1, s2, s3-sss, s3, s4-sss
The solution ErikE wrote is actually getting the end of the string so everything after the last hyphen. I just modified his version to get everything before it instead using a similar method with the left function. Thanks for all of your your help.
select left(some_field, abs(charindex('-', reverse(some_field)) - len(some_field)))
from (select 's2-st' as some_field
union select 's1-st'
union select 's3-st'
union select 's3-sss-zzz'
union select 's4-sss-zzzz') as SomeTable
May I suggest this simplification of your expression:
select right(some_field, charindex('-', reverse(some_field)) - 1)
from SomeTable
Also, there's no harm, as far as I know, in specifying 8000 characters in length with the substring function when you want the rest of the string. As long as it's not varchar(max), it works just fine.
If this is something you have to do all the time, over and over, how about #1 splitting out the data into separate columns and storing it that way, or #2 adding a calculated column with an index on it, which will perform the calculation once at update/insert time and not again later.
Last, I don't know if SQL Server is smart enough to reverse(some_field) only once and inject it into the other instance. When I get some time I'll try to figure it out.
Update
Oops, somehow I got backwards what you wanted. Sorry about that. The new expression you showed can still be simplified a little:
select left(some_field, len(some_field) - charindex('-', reverse(some_field)))
from (
select 's2-st'
union all select 's1-st'
union all select 's3-st'
union all select 's3-sss-zzz'
union all select 's4-sss-zzzz'
union all select 's5'
) X (some_field)
The abs() in your expression was just reversing the sign. So I put + len - charindex instead of + charindex - len and all is well now. It even works for strings without dashes.
One more thing to mention: your UNION SELECTs should be UNION ALL SELECT because without the ALL, the engine has to remove duplicates just as if you'd indicated SELECT DISTINCT. Simply get in the habit of using ALL and you'll be much better off. :)
Not sure about #1, but I would say that you might be better off doing this in code. Is there a reason you have to do it in the database?
Are you experiencing performance problems because of some similar code or is this purely hypothetical.
I am also not sure how SQL Server handles the multiple calls to REVERSE and CHARINDEX.
You can eliminate the last call to CHARINDEX since you want to take everything to the end of the string:
select
reverse(
substring(reverse(some_field),
charindex('-', reverse(some_field)) + 1,
len(some_field)))
from SomeTable
Although I would recommend against it, you could also replace the LEN function call with the size of the column:
select
reverse(
substring(reverse(some_field),
charindex('-', reverse(some_field)) + 1,
1024))
from SomeTable
I am curious how much of a difference either of these changes would make.
The 3 inner reverses are discrete from each other. The outer reverse will reverse anything that is already reversed by the inner ones.
ErikE's approach is best as a pure TSQL solution. You don't need LEN

SQL query - LEFT 1 = char, RIGHT 3-5 = numbers in Name

I need to filter out junk data in SQL (SQL Server 2008) table. I need to identify these records, and pull them out.
Char[0] = A..Z, a..z
Char[1] = 0..9
Char[2] = 0..9
Char[3] = 0..9
Char[4] = 0..9
{No blanks allowed}
Basically, a clean record will look like this:
T1234, U2468, K123, P50054 (4 record examples)
Junk data looks like this:
T12.., .T12, MARK, TP1, SP2, BFGL, BFPL (7 record examples)
Can someone please assist with a SQL query to do a LEFT and RIGHT method and extract those characters, and do a LIKE IN or something?
A function would be great though!
The following should work in a few different systems:
SELECT *
FROM TheTable
WHERE Data LIKE '[A-Za-z][0-9][0-9][0-9][0-9]%'
AND Data NOT LIKE '% %'
This approach will indeed match P2343, P23423JUNK, and other similar text but requires that the format is A0000*.
Now, if the OP implies a format of 1st position is a character and all succeeding positions are numeric, as in A0+, then use the following (in SQL Server and a good deal of other database systems):
SELECT *
FROM TheTable
WHERE SUBSTRING(Data, 1, 1) LIKE '[A-Za-z]'
AND SUBSTRING(Data, 2, LEN(Data) - 1) NOT LIKE '%[^0-9]%'
AND LEN(Data) >= 5
To incorporate this into a SQL Server 2008 function, since this appears to be what you'd like most, you can write:
CREATE FUNCTION ufn_IsProperFormat(#data VARCHAR(50))
RETURNS BIT
AS
BEGIN
RETURN
CASE
WHEN SUBSTRING(#Data, 1, 1) LIKE '[A-Za-z]'
AND SUBSTRING(#Data, 2, LEN(#Data) - 1) NOT LIKE '%[^0-9]%'
AND LEN(#Data) >= 5 THEN 1
ELSE 0
END
END
...and call into it like so:
SELECT *
FROM TheTable
WHERE dbo.ufn_IsProperFormat(Data) = 1
...this query needs to change for Oracle queries because Oracle doesn't appear to support bracket notation in LIKE clauses:
SELECT *
FROM TheTable
WHERE REGEXP_LIKE(Data, '^[A-za-z]\d{4,}$')
This is the expansion gbn is doing in his answer, but these versions allow for varying string lengths without the OR conditions.
EDIT: Updated to support examples in SQL Server and Oracle for ensuring the format A0+, so that A1324, A2342388, and P2342 match but A2342JUNK and A234 do not.
The Oracle REGEXP_LIKE code was borrowed from Mark's post but updated to support 4 or more numeric digits.
Added a custom SQL Server 2008 approach which implements these techniques.
Depends on your database. Many have regex functions (note examples not tested so check)
e.g. Oracle
SELECT x
FROM table
WHERE REGEXP_LIKE(x, '^[A-za-z][:digit:]{4}$')
Sybase uses LIKE
Given that you're allowing between 3 and 6 digits for the number in your examples then it's probably better to use the ISNUMERIC() function on the 2nd character onwards:
SELECT *
FROM TheTable
-- start with a letter
WHERE Data LIKE '[A-Za-z]%'
-- everything from 2nd character onwards is a number
AND ISNUMERIC( SUBSTRING( Data, 2, 50 ) ) = 1
-- number doesn't have a decimal place
AND Data NOT LIKE '%.%'
For more information look at the ISNUMERIC function on MSDN.
Also note that:
I've limited the 2nd part with the number to 50 characters maximum, change this to suit your needs.
Strictly speaking you should check for currency symbols etc, as ISNUMERIC allows them, as well as +/- and some others
A better option might be to create a function that checks that each character after the first is between 0 and 9 (or 1 and 0 if you're using ASCII codes).
You can't use Regular Expressions in SQL Server, so you have to use OR. Correcting David Andres' answer...
WHERE
(
Data LIKE '[A-Za-z][0-9][0-9][0-9]'
OR
Data LIKE '[A-Za-z][0-9][0-9][0-9][0-9]'
OR
Data LIKE '[A-Za-z][0-9][0-9][0-9][0-9][0-9]'
)
David's answer allows "D1234junk" through
You also only need "[A-Z]" if you don't have case sensitivity