SQL Case Decimal Between - sql

In my database I have an entry like: 'V2.0.0.0 VersionData'
I need my case statement to look at this value, trim off from the space onwards and remove the V at the start leaving '2.0.0.0, the case statement should then return 'Pass' if its greater than 2.0.1.0 else 'Fail'
This is what I have so far but it doesnt work:
Select
CASE
When (Select
Convert(Decimal(4,4),
REPLACE(
LEFT(EntryValue,
CHARINDEX(' ', EntryValue) - 1),'v',''))
from table where entrytype = 'VERSION') BETWEEN 0 and 2.1 THEN 'Pass'
Else 'Fail'
END [V-Check]

Your problem is that
declare #EntryValue varchar(250)='V2.0.0.0 VersionData'
Select REPLACE(LEFT(#EntryValue,CHARINDEX(' ', #EntryValue) - 1),'v','')
--2.0.0.0
You do not have a string that can be intepreted like a decimal.
Without knowing how your version numbering Works, a solution can not be found,
For instance is V2.11.0.0 possible? If so, then is V2.5.0.0 a a lower or higher version?
If you convert 2.11.0.0 to the decimal 2.11 then it will be lower than 2.5
In what I think would be the default intepretation of the version number, you do not have a single number, but 4 numbers you have to compare consecutively to get a result.
One of many ways to parse out the numbers in the version would then be:
declare #EntryValue varchar(250)='V2.0.0.0 VersionData'
;with cte as
(
select charindex('.',#EntryValue) p1
,charindex('.',#EntryValue,charindex('.',#EntryValue)+1) p2
,charindex('.',#EntryValue,charindex('.',#EntryValue,charindex('.',#EntryValue)+1)+1) p3
,charindex(' ',#EntryValue) p4
)
select substring(#EntryValue,2,p1-2)+0 [MajorVersion]
,SUBSTRING(#EntryValue,p1+1,p2-p1-1)+0 [MinorVersion]
,SUBSTRING(#EntryValue,p2+1,p3-p2-1)+0 [BugFixRelease]
,SUBSTRING(#EntryValue,p3+1,p4-p3-1)+0 [Build]
from cte
If the numbers cannot be larger than 10, then you have an easy task, then you can just use a simple compare
select case when 'V2.1.0.0 VersionData'>= 'V2.0.1.0 VersionData' then 1 else 0 end
Since the lexicographical ordering of the characters would ensure that the result is correct.
-- Added in edit
If you have up to 4 numbers in your version, then you can abuse the internal function PARSENAME to get the numbers in an easier fashion:
declare #EntryValue varchar(250)='V2.0.0.0 VersionData'
select
parsename(stuff(left(#EntryValue,charindex(' ',#EntryValue)),1,1,''),4)+0 MajorVersion
, parsename(stuff(left(#EntryValue,charindex(' ',#EntryValue)),1,1,''),3)+0 MinorVersion
, parsename(stuff(left(#EntryValue,charindex(' ',#EntryValue)),1,1,''),2)+0 Release
, parsename(stuff(left(#EntryValue,charindex(' ',#EntryValue)),1,1,''),1)+0 Build

One problem is that converting a string '2.0.1.0', which contains several .-characters will not properly convert into 2010 (as you might intend).
However, if you can guarantee that you always have exactly 4 version parts and each version part is between 0..9, i.e. there is no version like, for example, 2.0.11.0' or2.0.1.0.9', then the following solution should work. It simply relies on the order of string values, which - above mentioned restriction concerning version parts guaranteed - is just as if you compared integral values:
with versions as
( select entryValue, Substring(LEFT(EntryValue,
CHARINDEX(' ', EntryValue)),2,100) as versionStr
from (values
('V2.0.0.0 some older version'),
('V2.0.1.0 sufficient version'),
('V2.0.1.9 newer version')
) as versionTable(entryValue)
)
select *, (case when versionStr >= '2.0.1.0' then 'Pass' else 'Fail' end) as "check"
from versions;

Related

How to ignore specific string value when using pattern and patindex function in SQL Server Query?

I have this query here.
WITH Cte_Reverse
AS (
SELECT CASE PATINDEX('%[^0-9.- ]%', REVERSE(EmailName))
WHEN 0
THEN REVERSE(EmailName)
ELSE left(REVERSE(EmailName), PATINDEX('%[^0-9.- ]%', REVERSE(EmailName)) - 1)
END AS Platform_Campaign_ID,
EmailName
FROM [Arrakis].[xtemp].[Stage_SendJobs_Marketing]
)
SELECT REVERSE(Platform_Campaign_ID) AS Platform_Campaign_ID, EmailName
FROM Cte_Reverse
WHERE REVERSE(Platform_Campaign_ID) <> '2020'
AND REVERSE(Platform_Campaign_ID) <> ''
AND LEN(REVERSE(Platform_Campaign_ID)) = 4;
It is working for the most part, below is a screenshot of the result set.
The query I posted above extracts the 4 numbers to the right out of the initial value that is set for the column I am extracting out of. But I am unable to figure out how I can also have the query ignore cases when the right most value is -v2, -v1, etc. essentially anything with -v and whatever number version it is.
If you want four digits, then one method is:
select substring(emailname, patindex('%[0-9][0-9][0-9][0-9]%', emailname), 4)

Remove non-numeric value from SQL Select statement along with ending decimal point

I have an application version column in one of my tables. Most of the values are standard versions, such as 2020.2.0, but some have pre-release tags on them (2020.2.0.PR).
The following returns the latest entry in the table:
SELECT version
from dbo.server_log_entry
where server_log_entry_id = (SELECT MAX(server_log_entry_id) from dbo.server_log_entry)
In this case, it returns:
2020.2.0.PR
I'm working on a script that will compare the latest version in the DB table to a version in a file to make sure the two are compatible. The script takes the version in the file and converts it to a [version] object. I'd like to have powershell query against the database and pull the value to a version object, however I'm having some trouble removing the '.' before the PR tag. Currently I have:
DECLARE #databaseVersion NVARCHAR(30)
SET #databaseVersion = (SELECT version from dbo.server_log_entry where server_log_entry_id = (SELECT MAX(server_log_entry_id) from dbo.server_log_entry))
SELECT LEFT(SUBSTRING(#databaseVersion, PATINDEX('%[0-9.-]%', #databaseVersion), 8000),
PATINDEX('%[^0-9.-]%', SUBSTRING(#databaseVersion, PATINDEX('%[0-9.-]%', #databaseVersion), 8000) + 'X') -1)
This will give me a value of
2020.2.0.
I'd like to remove the ending period so the query returns
2020.2.0
however I'm not quite sure how to do that, or if I'm going about this the incorrect way.
After some trial/error, I found that the following works how I need it to
"SELECT DISTINCT SUBSTRING([version], 0, CASE WHEN PATINDEX('%.[^0-9.]%', [version]) <> 0 THEN PATINDEX('%.[^0-9.]%', [version]) ELSE 100 END) FROM dbo.server_log_entry
perhaps REPLACE( ver, '.PR','')
SELECT replace( version, '.PR','')
from dbo.server_log_entry
where server_log_entry_id = (SELECT MAX(server_log_entry_id) from dbo.server_log_entry)
You can use REVERSE with CHARINDEX to get the result.
declare #databaseVersion VARCHAR(100) = '2020.2.0.PR'
SELECT REVERSE(RIGHT(REVERSE(#databaseVersion),
LEN(#databaseVersion)-CHARINDEX('.',REVERSE(#databaseVersion))))
2020.2.0

Invalid argument for function integer IBM DB2

I need to filter out rows in table where numer_lini column has number in it and it is between 100 and 999, below code works just fine when i comment out line where i cast marsnr to integer. However when i try to use it i get error: Invalid character found in a character string argument of the function "INTEGER". when looking at the list seems like replace and translate filters only numbers just fine and select only contains legit numbers (list of unique values is not long so its easy to scan by eye). So why does it fail to cast something? I also tried using integer(marsnr), but it produces the same error. I need casting because i need numeric range, otherwise i get results like 7,80 and so on. As I mentioned Im using IBM DB2 database.
select numer_lini, war_trasy, id_prz1, id_prz2
from alaska.trasa
where numer_lini in (
select marsnr
from (
select
distinct numer_lini marsnr
from alaska.trasa
where case
when replace(translate(numer_lini, '0','123456789','0'),'0','') = ''
then numer_lini
else 'no'
end <> 'no'
)
where cast(marsnr as integer) between 100 and 999
)
fetch first 300 rows only
If you look at the optimized SQL from the Db2 explain, you will see that Db2 has collapsed your code into a single select.
SELECT DISTINCT Q2.NUMER_LINI AS "NUMER_LINI",
Q2.WAR_TRASY AS "WAR_TRASY",
Q2.ID_PRZ1 AS "ID_PRZ1",
Q2.ID_PRZ2 AS "ID_PRZ2",
Q1.NUMER_LINI
FROM ALASKA.TRASA AS Q1,
ALASKA.TRASA AS Q2
WHERE (Q2.NUMER_LINI = Q1.NUMER_LINI)
AND (100 <= INTEGER(Q1.NUMER_LINI))
AND (INTEGER(Q1.NUMER_LINI) <= 999)
AND (CASE WHEN (REPLACE(TRANSLATE(Q1.NUMER_LINI,
'0',
'123456789',
'0'),
'0',
'') = '') THEN Q1.NUMER_LINI
ELSE 'no' END <> 'no')
Use a CASE to force Db2 to do the "is integer" check first. Also, you don't check for the empty string.
E.g. with this table and data
‪create‬‎ ‪TABLE‬‎ ‪alaska‬‎.‪trasa‬‎ ‪‬‎(‪numer_lini‬‎ ‪VARCHAR‬‎(‪10‬‎)‪‬‎,‪‬‎ ‪war_trasy‬‎ ‪INT‬‎ ‪‬‎,‪‬‎ ‪id_prz1‬‎ ‪INT‬‎,‪‬‎ ‪id_prz2‬‎ ‪INT‬‎)‪;
insert into alaska.trasa values ('',1,1,1),('99',1,1,1),('500',1,1,1),('3000',1,1,1),('00300',1,1,1),('AXS',1,1,1);
This SQL works
select numer_lini, war_trasy, id_prz1, id_prz2
from alaska.trasa
where case when translate(numer_lini, '','0123456789') = ''
and numer_lini <> ''
then integer(numer_lini) else 0 end
between 100 and 999
Although that does fail if there is an embedded space in the input. E.g. '30 0'. To cater for that, a regular expressing is probably preferred. E.g.
select numer_lini, war_trasy, id_prz1, id_prz2
from alaska.trasa
where case when regexp_like(numer_lini, '^\s*[+-]?\s*((\d+\.?\d*)|(\d*\.?\d+))\s*$'))
then integer(numer_lini) else 0 end
between 100 and 999

Insert into sql Max +1 only numbers (prevent characters)

I'm using this code
(SELECT (MAX(CODE) +1 WHERE ISNUMERIC([code]) = 1)
I want to max +1 only my numbers of my column preventing characters characters.
NOTE: THIS QUESTION WAS TAGGED MYSQL WHEN THIS ANSWER WAS POSTED.
You can use substring_index() to split the values and then re-unite them:
(SELECT CONCAT(SUBSTRING_INDEX(MAX(Code), '-', 1), '-',
SUBSTRING_INDEX(MAX(CODE), '-', -1) + 1
)
FROM . . .
WHERE code LIKE '%NEW-1%'
)
This assumes that the wildcards do not have hyphens in them, and that the values after the "1" are all numbers.
Also, this doesn't pad the number is zeroes, but that is a good idea for such codes -- it ensures that they are always the same length and that they sort correctly.
The MAX() function accepts expressions, not just column names:
SELECT MAX(CASE ISNUMERIC(code) WHEN 1 THEN code END)+1 as next_code
FROM (
SELECT '15' AS code
UNION ALL SELECT ' 98 ' AS code
UNION ALL SELECT 'New-45' AS code
) foo
WHERE ISNUMERIC(code)=1;
16
(Link is to SQL Server 2005, docs for SQL Server 2000 are apparently no longer on line, but MAX() belongs to SQL standard anyway.)

How can I ORDER anything that looks like a number, as a number in T-SQL?

I have a column named Code that is varchar(3).
It contains numbers and strings as well. For example: ' 1', '234', 'Xxx', '9 ','Aa ' etc.
Is there way -just like in MS EXCEL- ORDER anything that looks like a number, as a number?
So that output for the given example above will be:
1. 1
2. 234
3. 9
4. Aa
5. Xxx
ORDER BY CASE WHEN ISNUMERIC(YourField) = 1 THEN CONVERT(INT, YourField) - 500 ELSE ASCII(LOWER(YourField)) END
If the field can be converted to a number it is sorted by number otherwise it uses ASCII coding to sort. I have used "- 500" just so there is no cross over in the sort, and to ensure numbers are sorted ahead of text.
ADDENDUM:
Brian Arsuaga has posted a more robust solution to this which I actually prefer, but since this has already been marked as the answer I am adding his solution to this for the benefit of anyone reading this in the future.
ORDER BY
ISNUMERIC(YourField) DESC,
CASE WHEN ISNUMERIC(YourField) = 1 THEN CONVERT(INT, YourField) ELSE 0 END,
YourField
If you don't like using an arbitrary sentinel (500), which might cause sorting issues depending on the range of numbers you expect, you can use multiple expressions for the ordering.
-- put the numbers at the top
ORDER BY ISNUMERIC(YourField) DESC,
-- sort the numbers as numbers, sort the strings as nothing
CONVERT(INT, CASE WHEN ISNUMERIC(YourField) = 1 THEN YourField ELSE '0' END),
-- sort the strings
YourField
The last term is only a tiebreaker when either two terms are both numbers with the same value ('01', '1') or two terms are both non-numbers. For non-numbers, their first and second terms will always be 0.
More complicated, but maybe a little more safe.
Edited to add a nice comparison with the help of the guy below
create table #t
(
YourField varchar(4)
)
insert into #t(YourField) Values('1'), ('3'), ('234'), ('0'), ('00'),
('09'), ('9'), ('1a'), ('aaa'), ('aba'), ('-500')
Select YourField from #t
ORDER BY ISNUMERIC(YourField) DESC,
CONVERT(INT, CASE WHEN ISNUMERIC(YourField) = 1 THEN YourField ELSE '0' END),
YourField
drop table #t