SELECT REPLACE('<strong>100</strong><b>.00 GB', '%^(^-?\d*\.{0,1}\d+$)%', '');
我想用上面的正则表达式替换数字两部分之间的任何标记,但是似乎不起作用。我不确定是不是正则表达式语法错误,因为我尝试了一种更简单的方法(例如'%[^0-9]%'
只是为了进行测试),但是它也不起作用。有人知道我该怎么做到吗?
SELECT REPLACE('<strong>100</strong><b>.00 GB', '%^(^-?\d*\.{0,1}\d+$)%', '');
我想用上面的正则表达式替换数字两部分之间的任何标记,但是似乎不起作用。我不确定是不是正则表达式语法错误,因为我尝试了一种更简单的方法(例如'%[^0-9]%'
只是为了进行测试),但是它也不起作用。有人知道我该怎么做到吗?
100.00
还是100.00 GB
?还有其他一些格式化数字的示例,它们仅位于小数点左侧的部分附近,而不适合标记的样式吗?标记可以是整数100<i>.00</i> GB
吗?右边总是有2个字符的货币代码吗?
Answers:
您可以使用PATINDEX 查找模式(字符串)出现的第一个索引。然后使用STUFF将另一个字符串填充到匹配的模式(字符串)中。
遍历每一行。用您想要的替换每个非法字符。在您的情况下,将非数字替换为空白。内循环是当前循环中是否有多个非法字符。
DECLARE @counter int
SET @counter = 0
WHILE(@counter < (SELECT MAX(ID_COLUMN) FROM Table))
BEGIN
WHILE 1 = 1
BEGIN
DECLARE @RetVal varchar(50)
SET @RetVal = (SELECT Column = STUFF(Column, PATINDEX('%[^0-9.]%', Column),1, '')
FROM Table
WHERE ID_COLUMN = @counter)
IF(@RetVal IS NOT NULL)
UPDATE Table SET
Column = @RetVal
WHERE ID_COLUMN = @counter
ELSE
break
END
SET @counter = @counter + 1
END
注意:这很慢!拥有varchar列可能会产生影响。因此,使用LTRIM RTRIM可能会有所帮助。无论如何,它很慢。
功劳归于这个StackOverFlow答案。
编辑信用也去@srutzky
编辑(通过@Tmdean)而不是一次执行一行,此答案可以适应于基于更多集合的解决方案。它仍然会迭代单行中非数字字符的最大数量,因此它不是理想的,但我认为在大多数情况下应该可以接受。
WHILE 1 = 1 BEGIN
WITH q AS
(SELECT ID_Column, PATINDEX('%[^0-9.]%', Column) AS n
FROM Table)
UPDATE Table
SET Column = STUFF(Column, q.n, 1, '')
FROM q
WHERE Table.ID_Column = q.ID_Column AND q.n != 0;
IF @@ROWCOUNT = 0 BREAK;
END;
如果您在表中保留一个指示该字段是否已清理的位列,还可以大大提高效率。(在我的示例中,NULL表示“未知”,并且应为默认列。)
DECLARE @done bit = 0;
WHILE @done = 0 BEGIN
WITH q AS
(SELECT ID_Column, PATINDEX('%[^0-9.]%', Column) AS n
FROM Table
WHERE COALESCE(Scrubbed_Column, 0) = 0)
UPDATE Table
SET Column = STUFF(Column, q.n, 1, ''),
Scrubbed_Column = 0
FROM q
WHERE Table.ID_Column = q.ID_Column AND q.n != 0;
IF @@ROWCOUNT = 0 SET @done = 1;
-- if Scrubbed_Column is still NULL, then the PATINDEX
-- must have given 0
UPDATE table
SET Scrubbed_Column = CASE
WHEN Scrubbed_Column IS NULL THEN 1
ELSE NULLIF(Scrubbed_Column, 0)
END;
END;
如果您不想更改架构,可以轻松地将中间结果存储在表值变量中,该变量最终将应用于实际表。
[^0-9.]
。如果不是,则将小数点去掉,然后将其100.00
变成10000
。
与其通过唯一位置剥离找到的角色,不如使用它Replace(Column, BadFoundCharacter, '')
可能会更快。另外,这不仅替换了在每一列中找到的一个坏字符,还将替换所有找到的坏字符。
WHILE 1 = 1 BEGIN
UPDATE dbo.YourTable
SET Column = Replace(Column, Substring(Column, PatIndex('%[^0-9.-]%', Column), 1), '')
WHERE Column LIKE '%[^0-9.-]%'
If @@RowCount = 0 BREAK;
END;
我坚信这将比接受的答案更好,即使仅仅是因为它执行较少的操作。还有其他方法可能也更快,但是我现在没有时间探索这些方法。
Replace(Column, Substring(Column, PatIndex('%[^0-9.-]%', Column), 1), '')
的选择查询。那谢谢啦!
我偶然发现了这篇文章,寻找了其他东西,但以为我会提到我使用的解决方案效率更高-与基于集合的查询一起使用时,实际上应该是任何函数的默认实现-使用交叉应用表功能。似乎该主题仍在进行中,因此希望对某人有用。
到目前为止,基于运行递归基于查询集或标量函数的一些答案的示例运行时示例,基于1m行测试集从随机newid中删除字符,WHILE循环示例的范围为34s至2m05s,范围为1m3s至{功能示例。
使用具有交叉应用功能的表功能可以在10秒钟内达到相同的目标。您可能需要对其进行调整以适应您的需要,例如处理的最大长度。
功能:
CREATE FUNCTION [dbo].[RemoveChars](@InputUnit VARCHAR(40))
RETURNS TABLE
AS
RETURN
(
WITH Numbers_prep(Number) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,Numbers(Number) AS
(
SELECT TOP (ISNULL(LEN(@InputUnit),0))
row_number() OVER (ORDER BY (SELECT NULL))
FROM Numbers_prep a
CROSS JOIN Numbers_prep b
)
SELECT
OutputUnit
FROM
(
SELECT
substring(@InputUnit,Number,1)
FROM Numbers
WHERE substring(@InputUnit,Number,1) like '%[0-9]%'
ORDER BY Number
FOR XML PATH('')
) Sub(OutputUnit)
)
用法:
UPDATE t
SET column = o.OutputUnit
FROM ##t t
CROSS APPLY [dbo].[RemoveChars](t.column) o
这是我根据先前的答案编写的用于完成此功能的函数。
CREATE FUNCTION dbo.RepetitiveReplace
(
@P_String VARCHAR(MAX),
@P_Pattern VARCHAR(MAX),
@P_ReplaceString VARCHAR(MAX),
@P_ReplaceLength INT = 1
)
RETURNS VARCHAR(MAX)
BEGIN
DECLARE @Index INT;
-- Get starting point of pattern
SET @Index = PATINDEX(@P_Pattern, @P_String);
while @Index > 0
begin
--replace matching charactger at index
SET @P_String = STUFF(@P_String, PATINDEX(@P_Pattern, @P_String), @P_ReplaceLength, @P_ReplaceString);
SET @Index = PATINDEX(@P_Pattern, @P_String);
end
RETURN @P_String;
END;
最初,我在这里有一个递归函数,该函数在sql server中不能很好地发挥作用,因为它具有32个嵌套级别限制,每次尝试用该函数进行32次以上替换时,都会导致类似以下的错误。与其尝试进行服务器级别的更改以允许更多的嵌套(这可能是危险的,如永不结束循环),倒不如切换为while循环更有意义。
超出最大存储过程,函数,触发器或视图嵌套级别(限制32)。
如果要重用,将解决方案包装在SQL函数中可能会很有用。我什至在单元级别上执行此操作,因此将其作为不同的答案:
CREATE FUNCTION [dbo].[fnReplaceInvalidChars] (@string VARCHAR(300))
RETURNS VARCHAR(300)
BEGIN
DECLARE @str VARCHAR(300) = @string;
DECLARE @Pattern VARCHAR (20) = '%[^a-zA-Z0-9]%';
DECLARE @Len INT;
SELECT @Len = LEN(@String);
WHILE @Len > 0
BEGIN
SET @Len = @Len - 1;
IF (PATINDEX(@Pattern,@str) > 0)
BEGIN
SELECT @str = STUFF(@str, PATINDEX(@Pattern,@str),1,'');
END
ELSE
BEGIN
BREAK;
END
END
RETURN @str
END
我创建了此函数来清理在时间字段中包含非数字字符的字符串。他们未添加分钟数时,时间中包含问号,类似于20:??。函数循环遍历每个字符并替换?0:
CREATE FUNCTION [dbo].[CleanTime]
(
-- Add the parameters for the function here
@intime nvarchar(10)
)
RETURNS nvarchar(5)
AS
BEGIN
-- Declare the return variable here
DECLARE @ResultVar nvarchar(5)
DECLARE @char char(1)
-- Add the T-SQL statements to compute the return value here
DECLARE @i int = 1
WHILE @i <= LEN(@intime)
BEGIN
SELECT @char = CASE WHEN substring(@intime,@i,1) like '%[0-9:]%' THEN substring(@intime,@i,1) ELSE '0' END
SELECT @ResultVar = concat(@ResultVar,@char)
set @i = @i + 1
END;
-- Return the result of the function
RETURN @ResultVar
END
如果仅针对进入存储过程的参数执行此操作,则可以使用以下命令:
declare @badIndex int
set @badIndex = PatIndex('%[^0-9]%', @Param)
while @badIndex > 0
set @Param = Replace(@Param, Substring(@Param, @badIndex, 1), '')
set @badIndex = PatIndex('%[^0-9]%', @Param)
我认为一种更简单,更快速的方法是对字母的每个字符进行迭代:
DECLARE @i int
SET @i = 0
WHILE(@i < 256)
BEGIN
IF char(@i) NOT IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.')
UPDATE Table SET Column = replace(Column, char(@i), '')
SET @i = @i + 1
END