I have column sql variant, which has the following meanings: 100, 150, D1
I'm trying to convert all numbers in the columns into letters (such as D1) according to specific logic in case when. But 150 has spaces and the CASE WHEN doesn't work.
Here's the query I'm using:
Select *,
Case When LTrim(Cast(AttributeValue As NVarchar(Max))) Between 0 And 200 Then 'D1'
Else 'Other'
End
From MyTable As SCR With (NoLock);
I tried the following
LTRIM(CAST column AS VARCHAR(MAX))
but now I get error:
Conversion failed when converting the nvarchar value 'D1' to data type int
How can I remove spaces from sql_variant?
As per your comments edited to use BIGINT due to having larger numbers and leave the column the same if it is not BETWEEN 0 and 400
SELECT *
,CASE
WHEN ISNUMERIC(LTRIM(CAST(AttributeValue AS NVARCHAR(MAX)))) = 1
AND CAST(LTRIM(CAST(AttributeValue AS NVARCHAR(MAX))) AS BIGINT) BETWEEN 0 AND 400 THEN 'D1'
ELSE AttributeValue
END
FROM
MyTable AS SCR WITH (NOLOCK)
You can use the ISNUMERIC() function to determine which of your sql_variants are integers and which are not.
The reason your code is failing isn't because of the trim it is because you are comparing a VARCHAR with an INTEGER so SQL is trying to automatically re cast your final string as an integer which in the case of D1 is not numeric so it causes a conversion error.
Also note that you cannot use sql_variant directly in the ISNUMERIC() function so cast to a varchar first.
Here is an entire example of you to show you how it works:
DECLARE #MyTable AS TABLE (AttributeValue SQL_VARIANT)
INSERT INTO #MyTable VALUES
(CAST(' 150' AS VARCHAR(100)))
,(CAST('D1' AS VARCHAR(100)))
SELECT *
,CASE
WHEN ISNUMERIC(LTRIM(CAST(AttributeValue AS NVARCHAR(MAX)))) = 1
AND CAST(LTRIM(CAST(AttributeValue AS NVARCHAR(MAX))) AS INT) BETWEEN 0 AND 200 THEN 'D1'
ELSE 'Other'
END
FROM
#MyTable AS SCR
use sql replace function
select replace(columnnName, ' ', '')
Related
I’m fairly used to using MySQL, but not particularly familiar with SQL Server. Tough luck, the database I’m dealing with here is on SQL Server 2014.
I have a table with a column whose values are all integers with leading, separating, and trailing semicolons, like these three fictitious rows:
;905;1493;384;13387;29;933;467;28732;
;905;138;3084;1387;290;9353;4767;2732;
;9085;14493;3864;130387;289;933;4767;28732;
What I am trying to do now is to select all rows where more than one number taken from a list of numbers appears in this column. So for example, given the three rows above, if I have the group 905,467,4767, the statement I’m trying to figure out how to construct should return the first two rows: the first row contains 905 and 467; the second row contains 905 and 4767. The third row contains only 4767, so that row should not be returned.
As far as I can tell, SQL Server does not actually support regex directly (and I don’t even know what managed code is), which doesn’t help. Even with regex, I wouldn’t know where to begin. Oracle seems to have a function that would be very useful, but that’s Oracle.
Most similar questions on here deal with finding multiple instances of the same character (usually singular) and solve the problem by replacing the string to match with nothing and counting the difference in length. I suppose that would technically work here, too, but given a ‘filter’ group of 15 numbers, the SELECT statement would become ridiculously long and convoluted and utterly unreadable. Additionally, I only want to match entire numbers (so if one of the numbers to match is 29, the value 29 would match in the first row, but the value 290 in the second row should not match), which means I’d have to include the semicolons in the REPLACE clause and then discount them when calculating the length. A complete mess.
What I would ideally like to do is something like this:
SELECT * FROM table WHERE REGEXP_COUNT(column, ';(905|467|4767);') > 1
– but that will obviously not work, for all kinds of reasons (the most obvious one being the nonexistence of REGEXP_COUNT outside Oracle).
Is there some sane, manageable way of doing this?
You can do
SELECT *
FROM Mess
CROSS APPLY (SELECT COUNT(*)
FROM (VALUES (905),
(467),
(4767)) V(Num)
WHERE Col LIKE CONCAT('%;', Num, ';%')) ca(count)
WHERE count > 1
SQL Fiddle
Or alternatively
WITH Nums
AS (SELECT Num
FROM (VALUES (905),
(467),
(4767)) V(Num))
SELECT Mess.*
FROM Mess
CROSS APPLY (VALUES(CAST(CONCAT('<x>', REPLACE(Col, ';', '</x><x>'), '</x>') AS XML))) x(x)
CROSS APPLY (SELECT COUNT(*)
FROM (SELECT n.value('.', 'int')
FROM x.x.nodes('/x') n(n)
WHERE n.value('.', 'varchar') <> ''
INTERSECT
SELECT Num
FROM Nums) T(count)
HAVING COUNT(*) > 1) ca2(count)
Could you put your arguments into a table (perhaps using a table-valued function accepting a string (of comma-separated integers) as a parameter) and use something like this?
DECLARE #T table (String varchar(255))
INSERT INTO #T
VALUES
(';905;1493;384;13387;29;933;467;28732;')
, (';905;138;3084;1387;290;9353;4767;2732;')
, (';9085;14493;3864;130387;289;933;4767;28732;')
DECLARE #Arguments table (Arg int)
INSERT INTO #Arguments
VALUES
(905)
, (467)
, (4767)
SELECT String
FROM
#T
CROSS JOIN #Arguments
GROUP BY String
HAVING SUM(CASE WHEN PATINDEX('%;' + CAST(Arg AS varchar) + ';%', String) > 0 THEN 1 ELSE 0 END) > 1
And example of using this with a function to generate the arguments:
CREATE FUNCTION GenerateArguments (#Integers varchar(255))
RETURNS #Arguments table (Arg int)
AS
BEGIN
WITH cte
AS
(
SELECT
PATINDEX('%,%', #Integers) p
, LEFT(#Integers, PATINDEX('%,%', #Integers) - 1) n
UNION ALL
SELECT
CASE WHEN PATINDEX('%,%', SUBSTRING(#Integers, p + 1, LEN(#Integers))) + p = p THEN 0 ELSE PATINDEX('%,%', SUBSTRING(#Integers, p + 1, LEN(#Integers))) + p END
, CASE WHEN PATINDEX('%,%', SUBSTRING(#Integers, p + 1, LEN(#Integers))) = 0 THEN RIGHT(#Integers, PATINDEX('%,%', REVERSE(#Integers)) - 1) ELSE LEFT(SUBSTRING(#Integers, p + 1, LEN(#Integers)), PATINDEX('%,%', SUBSTRING(#Integers, p + 1, LEN(#Integers))) - 1) END
FROM cte
WHERE p <> 0
)
INSERT INTO #Arguments (Arg)
SELECT n
FROM cte
RETURN
END
GO
DECLARE #T table (String varchar(255))
INSERT INTO #T
VALUES
(';905;1493;384;13387;29;933;467;28732;')
, (';905;138;3084;1387;290;9353;4767;2732;')
, (';9085;14493;3864;130387;289;933;4767;28732;')
;
SELECT String
FROM
#T
CROSS JOIN GenerateArguments('905,467,4767')
GROUP BY String
HAVING SUM(CASE WHEN PATINDEX('%;' + CAST(Arg AS varchar) + ';%', String) > 0 THEN 1 ELSE 0 END) > 1
You can achieve this using the like function for the regex and row_number to determine the number of matches.
Here we declare the column values for testing:
DECLARE #tbl TABLE (
string NVARCHAR(MAX)
)
INSERT #tbl VALUES
(';905;1493;384;13387;29;933;467;28732;'),
(';905;138;3084;1387;290;9353;4767;2732;'),
(';9085;14493;3864;130387;289;933;4767;28732;')
Then we pass your search parameters into a table variable to be joined on:
DECLARE #search_tbl TABLE (
search_value INT
)
INSERT #search_tbl VALUES
(905),
(467),
(4767)
Finally we join the table with the column to search for onto the search table. We apply the row_number function to determine the number of times it matches. We select from this subquery where the row_number = 2 meaning that it joined at least twice.
SELECT
string
FROM (
SELECT
tbl.string,
ROW_NUMBER() OVER (PARTITION BY tbl.string ORDER BY tbl.string) AS rn
FROM #tbl tbl
JOIN #search_tbl search_tbl ON
tbl.string LIKE '%;' + CAST(search_tbl.search_value AS NVARCHAR(MAX)) + ';%'
) tbl
WHERE rn = 2
You could build a where clause like this :
WHERE
case when column like '%;905;%' then 1 else 0 end +
case when column like '%;467;%' then 1 else 0 end +
case when column like '%;4767;%' then 1 else 0 end >= 2
The advantage is that you do not need a helper table. I don't know how you build the query, but the following also works, and is useful if the numbers are in a tsql variable.
case when column like ('%;' + #n + ';%') then 1 else 0 end
I have this query :
SUBSTRING (
dbo.Table.RNumber,
1,
CHARINDEX(
'+',
dbo.Table.RNumber
) - 1
) AS RoomNumber,
SUBSTRING (
dbo.Table.R.Number,
CHARINDEX(
'+',
dbo.Table.R.Number
) + 1,
LEN(
dbo.Table.R.Number
)
) AS HallNumber,
My Table RNumber is mostly like 2+3 or 3+5, but sometimes it is like x+5 or y+0. I want to convert fields to int, but I want to convert strings like "x" or "y" to 0. I googled it but I couldn't find a solution. How can I do that? Thanks.
You can use case statement try this
Edited to use isnumeric() method
CASE
WHEN isnumeric(SUBSTRING(dbo.Table.RNumber,1,CHARINDEX('+',dbo.Table.RNumber) - 1)) = 1
THEN SUBSTRING(dbo.Table.RNumber,1,CHARINDEX('+',dbo.Table.RNumber) - 1)
else 0
end AS RoomNumber,
CASE
WHEN isnumeric(SUBSTRING(dbo.Table.R.Number,CHARINDEX('+',dbo.Table.RNumber) + 1,LEN(dbo.Table.R.Number))) = 1
THEN SUBSTRING(dbo.Table.R.Number,CHARINDEX('+',dbo.Table.RNumber) + 1,LEN(dbo.Table.R.Number))
else 0
end AS HallNumber,
Hope this should solve your problem
Perhaps you can use ParseName() and Try_Convert()
Declare #YourTable table (SomeField varchar(50))
Insert Into #YourTable values
('2+3'),('3+5'),('x+5'),('y+0')
Select *
,RoomNumber = IsNull(Try_Convert(int,ParseName(Replace(SomeField,'+','.'),2)),0)
,HallNumber = IsNull(Try_Convert(int,ParseName(Replace(SomeField,'+','.'),1)),0)
From #YourTable
Returns
SomeField RoomNumber HallNumber
2+3 2 3
3+5 3 5
x+5 0 5
y+0 0 0
For versions prior to 2012, you can do it like this:
CASE
WHEN NOT columnName like '%[^0-9]%' -- Contains no non-digits
AND columnName like '%[0-9]%' -- contains at least one digit
THEN CAST(columnName as INT) ELSE NULL
END
(Note that this will reject negative numbers, but you can easily adapt it if you need to support them)
Alternatively using IsNumeric, you must first cast to float because Isnumeric accepts some strings that Cast(EXPRESSION as INT) does not accept:
CASE WHEN ISNUMERIC(columnName)=1
THEN CAST(CAST(columnName as float) as int) END
How can i select a range of items from a VARCHAR type column in sql server?
I want to make something like:
SELECT TE.DESC
FROM PRODUCT P, ETYPE TE WHERE ( P.IDTYPE = TE.IDTYPE )
AND P.NUMBER BETWEEN '619' AND '623'
The 'P.NUMBER' column can contain numbers and letters together like 'abc123', then sql can't select correctly what i want.
There's some way to do it?
Thanks
First, you need to create a function which will strip all non-numeric characters from your NUMBER and return a float (or int), like so:
create function dbo.RemoveAlpha(#str varchar(1000))
returns float
AS
begin
while patindex('%[^0-9]%', #str) > 0
begin
set #strText = stuff(#str, patindex('%[^0-9]%', #str), 1, '')
end
return convert(float, #str)
end
Then your can rewrite your query like so:
SELECT TE.DESC
FROM PRODUCT P, ETYPE TE
WHERE ( P.IDTYPE = TE.IDTYPE )
AND dbo.RemoveAlpha(P.NUMBER) BETWEEN 619 AND 623
You can get only numbers using this double CASE
SELECT TE.DESC
FROM PRODUCT P, ETYPE TE WHERE ( P.IDTYPE = TE.IDTYPE )
WHERE 1 = CASE ISNUMERIC(P.NUMBER)
WHEN 1 THEN
CASE WHEN CAST(P.NUMBER AS INT) BETWEEN 619 AND 623 THEN 1 ELSE 0 END
ELSE 0
END
How to get the SUM of values of one column in table. Now I want get the SUM of 8 and 0 if I use query write below.
SELECT SUM(Items) FROM dbo.CustomSplit(#AssigneeProgress,',')
this gives an error
Operand data type varchar is invalid for sum operator.
My code is:
DECLARE #AssigneeProgress AS VARCHAR(100)
SELECT
#AssigneeProgress = STUFF((SELECT ',' + CAST(ISNULL((((TblAssignments.PlannedDuration * 100) / 300) * TblAssignments.Progress) / 100,0) AS VARCHAR(100))
FROM TblTasks,TblAssignments
WHERE TblTasks.TaskId = TblAssignments.AssignmentEntityId
AND TblAssignments.AssignmentEntity = 'Task'
AND AssignmentStatus NOT IN ('Deleted','Cancelled')
AND TblTasks.TaskId = 63 FOR XML PATH('')), 1, 1,'')
SELECT * FROM dbo.CustomSplit(#AssigneeProgress,',')
This query gives me the result in table like
Items
8
0
Does your method CustomSplit return a table having the column Items of type varchar?
If yes, you can convert the column:
SELECT SUM(cast(Items as int)) FROM dbo.CustomSplit(#AssigneeProgress,',')
Try to cast the Items that you get back from your split function to an INT type before summing:
SELECT
SUM(CAST(Items AS INT))
FROM
dbo.CustomSplit(#AssigneeProgress,',')
The CustomSplit function most likely returns varchar(x) items....
I have a nvarchar column BigMacs in table McTable in my MS SQL 2005 database with alfanumeric and numeric values. For example:
132
432adfad
sfs54543
5256
And now i would like to do something like this:
select Convert(BigMacs, int) from McTable
where IsNumerc(BigMacs) = 1 AND Convert(BigMacs, int) > 6
But when I do this i get a error:
Msg 245, Level 16, State 1, Line 41
Conversion failed when converting the nvarchar value '.' to data type int.
On line select.
How to fix this?
This is probably because the IsNumeric function returns true for any value that COULD be converted to a number. Try the following example:
create table McTable (BigMac varchar(255))
insert into McTable select '1p927'
insert into McTable select '1927'
insert into McTable select '1,927'
insert into McTable select '1.927'
select BigMac, isnumeric(BigMac)
from McTable
select BigMac, CAST(BigMac AS DECIMAL)
from McTable
where isnumeric(BigMac) = 1
Even though all rows except the '1p927' are numeric, the cast will fail! This is because '1,927' cannot be converted to a Decimal (on my machine)
IsNumeric doesn't work exactly as specified. As found here, you could use
IsNumeric (data + 'e0')
-Edo
Try this:
SELECT *
FROM (
SELECT REPLACE(BigMacs, ',', '.') AS BigMacs
FROM McTable m
WHERE IsNumerc(BigMacs) = 1
) q
WHERE CAST(BigMacs AS DECIMAL) > 6
IsNumeric will return TRUE on decimal fractions like 1234.1232, but they cannot be converted to INT.
Checking:
WITH McTable AS
(
SELECT '123124,123123' AS BigMacs
)
SELECT *
FROM (
SELECT REPLACE(BigMacs, ',', '.') AS BigMacs
FROM McTable
WHERE IsNumeric(BigMacs) = 1
) q
WHERE CAST(BigMacs AS DECIMAL) > 6
-----------
123124.123123
There are many ways to accomplish this. Both of these work the same. It is best not to use replace in this situation as there are too many unknowns to account for to replace. It is best to filter everything that is NOT what your after.
SELECT
CONVERT(INT,BigMacs) AS BigMacs
FROM
McTable
WHERE
ISNUMERIC(BigMacs) = 1
AND PATINDEX('%[^0-9]%', BigMacs) = 0
SELECT
CONVERT(INT,BigMacs) AS BigMacs
FROM
McTable
WHERE
ISNUMERIC(BigMacs) = 1
AND BigMacs NOT LIKE ('%[^0-9]%')
Note: It helps if people spell ISNUMERIC() correctly. It also helps if you use the correct syntax order on CONVERT()