回答:
SQL Serverは複数の文字のパターンの置換をサポートしていません。そのため、これを行うにREPLACEは10の操作が必要になる可能性があります。
このことを念頭に置くと、1つの方法として、0〜9の数字を順番に処理する再帰CTEになります。
置換を行い、前後の文字列の長さをチェックして、その数字の文字数と合計に何を追加する必要があるかを確認します。
DECLARE @Input VARCHAR(8000) = 'GR35hc7vdH35';
WITH R(Level,Input,Accumulator,StringLength)
AS (SELECT 0,
Input,
0,
DATALENGTH(Input)
FROM (SELECT REPLACE(@Input, '0', '')) D(Input)
UNION ALL
SELECT NewLevel,
NewInput,
Accumulator + NewLevel * ( StringLength - NewStringLength ),
NewStringLength
FROM R
CROSS APPLY (SELECT Level + 1) C(NewLevel)
CROSS APPLY (SELECT REPLACE(Input, NewLevel, '')) C2(NewInput)
CROSS APPLY (SELECT DATALENGTH(NewInput)) C3(NewStringLength)
WHERE NewLevel <= 9)
SELECT Input AS Col1,
Accumulator AS Col2
FROM R
WHERE Level = 9;
または、CLRと正規表現(SQL Server 2012互換バージョン)を使用できます。
using System;
using System.Data.SqlTypes;
using System.Collections;
using System.Text.RegularExpressions;
public partial class UserDefinedFunctions
{
private static readonly Regex digitRegex = new Regex(@"[\d]", RegexOptions.Compiled);
[Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "FillRow",
TableDefinition = @"Stripped NVARCHAR(MAX),
Total INT")]
public static IEnumerable ReplaceAndTotalise(SqlString input)
{
if (!input.IsNull)
{
int total = 0;
string stripped = digitRegex.Replace((string)input, match =>
{
total += int.Parse(match.Value);
return string.Empty;
});
yield return new Tuple<string, int>(stripped, total);
}
}
public static void FillRow(object resultObject, out SqlString stripped, out SqlInt32 total)
{
var result = (Tuple<string, int>)resultObject;
stripped = result.Item1;
total = result.Item2;
}
}
使用例
SELECT Stripped,
Total
FROM [dbo].[ReplaceAndTotalise]('GR35hc7vdH35')
以下を試してください:
CREATE FUNCTION dbo.AlphaNumericSplitter
(
@string varchar(8000)
)
RETURNS TABLE
AS
RETURN (
WITH Alphanumeric (col1)
AS (
-- Put out string into a cte table
SELECT @string
),
Nmbrs (n)
AS (
-- Numbers so we can split the string
SELECT TOP(LEN(@string))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.all_objects AS o1
CROSS
JOIN sys.all_objects AS o2
),
y
AS (
SELECT N.n
a.col1,
N.x,
-- Get the numbers only
Numbers = TRY_CONVERT(int, N.x)
FROM Alphanumeric AS a
CROSS
APPLY (SELECT [x] = SUBSTRING(a.col1, n, 1), Nmbrs.n FROM Nmbrs) AS N
)
SELECT z.Col1,
Col2 = SUM(y.Numbers)
FROM y
-- Get the letters only
CROSS
APPLY (SELECT (SELECT x + '' FROM y WHERE Numbers IS NULL ORDER BY y.n FOR XML PATH(''))) AS z (Col1)
GROUP BY
z.Col1);
GO
SELECT * FROM AlphaNumericSplitter('GR35hc7vdH35');
結果:
文字列からアルファベットを抽出するには、次の関数を使用します
CREATE FUNCTION dbo.udf_GetAlphabets
(@strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256)
AS
BEGIN
DECLARE @intAlpha INT
SET @intAlpha = PATINDEX('%[a-zA-Z]%', @strAlphaNumeric)
BEGIN
WHILE @intAlpha > 0
BEGIN
SET @strAlphaNumeric = STUFF(@strAlphaNumeric, @intAlpha, 1, '' )
SET @intAlpha = PATINDEX('%[a-zA-Z]%', @strAlphaNumeric )
END
END
RETURN ISNULL(@strAlphaNumeric,0)
END
GO
次の関数を使用して文字列から数値を抽出します
CREATE FUNCTION dbo.udf_GetNumeric
(@strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256)
AS
BEGIN
DECLARE @intAlpha INT
SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric)
BEGIN
WHILE @intAlpha > 0
BEGIN
SET @strAlphaNumeric = STUFF(@strAlphaNumeric, @intAlpha, 1, '' )
SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric )
END
END
RETURN ISNULL(@strAlphaNumeric,0)
END
GO
次のクエリを使用して、両方を1つのコマンドで抽出します。
SELECT dbo.udf_GetNumeric(column_name) Number, dbo.udf_GetAlphabets(column_name) Chars
from table_name