我在XYZ表中有一个列名称 MasterCode ,其中数据以下面的形式存储。
.105248.105250.104150.111004.
现在我首先要将数据拆分为:
105248
105250
104150
111004
之后,仅从上面检索倒数第二个值。
因此,在上述给定的数组中,返回的值应为104150
。
答案 0 :(得分:2)
使用分割字符串函数,但不要一次使用内置函数,因为它将仅返回值,并且您将丢失位置数据。
您可以使用Jeff Moden的DelimitedSplit8K
来返回商品和商品索引:
CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
(@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) 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 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
;
然后您可以使用它来分割字符串,它将返回一个如下表:
DECLARE @string varchar(100) = '.105248.105250.104150.111004.';
SELECT *
FROM [dbo].[DelimitedSplit8K](@string, '.')
ItemNumber Item
1
2 105248
3 105250
4 104150
5 111004
6
您只需要实际存在项目的部分,因此添加一个where子句,并且要从倒数第二个开始,所以添加row_number()
,并希望整个表都在一个公共表表达式中,以便您可以查询它:
DECLARE @string varchar(100) = '.105248.105250.104150.111004.';
WITH CTE AS
(
SELECT Item, ROW_NUMBER() OVER(ORDER BY ItemNumber DESC) As rn
FROM [dbo].[DelimitedSplit8K](@string, '.')
WHERE Item <> ''
)
查询:
SELECT Item
FROM CTE
WHERE rn = 2
结果:104150
答案 1 :(得分:1)
根据您的SQL SERVER版本,您还可以使用STRING_SPLIT
函数。
DECLARE @string varchar(100) = '.105248.105250.104150.111004.';
SELECT value,
ROW_NUMBER() OVER (ORDER BY CHARINDEX('.' + value + '.', '.' + @string + '.')) AS Pos
FROM STRING_SPLIT(@string,'.')
WHERE RTRIM(value) <> '';
它不会像Jeff的拆分器那样返回原始位置,但是如果您查看Aaron Bertrand的Article,它的确非常有利:
Performance Surprises and Assumptions : STRING_SPLIT()
编辑:
添加了位置,但尽管在这种情况下可行,但可能存在重复值的问题
答案 2 :(得分:1)
如果总是有四个部分,则可以使用PARSENAME()
:
DECLARE @s varchar(64) = '.105248.105250.104150.111004.';
SELECT PARSENAME(SUBSTRING(@s, 2, LEN(@s)-2),2);
答案 3 :(得分:0)
您可以使用参数stringvalue和delemeter创建一个SQL Server表值函数,并按预期的结果调用该函数。
ALTER function [dbo].[SplitString]
(
@str nvarchar(4000),
@separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
select
1,
1,
charindex(@separator, @str)
union all
select
p + 1,
b + 1,
charindex(@separator, @str, b + 1)
from tokens
where b > 0
)
select
p-1 ID,
substring(
@str,
a,
case when b > 0 then b-a ELSE 4000 end)
AS s
from tokens
)
调用该函数
SELECT * FROM [DBO].[SPLITSTRING] ('.105248.105250.104150.111004.', '.') WHERE ISNULL(S,'') <> ''
输出
ID s
1 105248
2 105250
3 104150
4 111004
要仅获取第二个值,可以如下所示编写查询
DECLARE @MaxID INT
SELECT @MaxID = MAX (ID) FROM (SELECT * FROM [DBO].[SPLITSTRING] ('.105248.105250.104150.111004.', '.') WHERE ISNULL(S,'') <> '') A
SELECT TOP 1 @MaxID = MAX (ID) FROM (
SELECT * FROM [DBO].[SPLITSTRING] ('.105248.105250.104150.111004.', '.') WHERE ISNULL(S,'') <> ''
)a where ID < @MaxID
SELECT * FROM [DBO].[SPLITSTRING] ('.105248.105250.104150.111004.', '.') WHERE ISNULL(S,'') <> '' AND ID = @MaxID
输出
ID s
3 104150
如果您希望ID为1,则可以在查询的最后一行中编写查询,如下所示。
SELECT 1 AS ID , S FROM [DBO].[SPLITSTRING] ('.105248.105250.104150.111004.', '.') WHERE ISNULL(S,'') <> '' AND ID = @MaxID
然后输出将为
ID S
1 104150
希望这会对您有所帮助。
答案 4 :(得分:0)
尝试一下
DECLARE @DATA AS TABLE (Data nvarchar(1000))
INSERT INTO @DATA
SELECT '.105248.105250.104150.111004.'
;WITH CTE
AS
(
SELECT Data,ROW_NUMBER()OVER(ORDER BY Data DESC) AS Rnk
FROM
(
SELECT Split.a.value('.','nvarchar(100)') Data
FROM(
SELECT CAST('<S>'+REPLACE(Data,'.','</S><S>')+'</S>' AS XML ) As Data
FROM @DATA
)DT
CROSS APPLY Data.nodes('S') AS Split(a)
) AS Fnl
WHERE Fnl.Data <>''
)
SELECT Data FROM CTE
WHERE Rnk=2
结果
Data
-----
105248
105250
104150
111004
答案 5 :(得分:0)
也可以仅使用字符串函数来实现:
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
SELECT '.105248.105250.104150.111004.' code INTO #temp UNION ALL
SELECT '.205248.205250.204150.211004.'
SELECT
REVERSE(LEFT(
REVERSE(LEFT(code, LEN(code) - CHARINDEX('.', REVERSE(code), 2)))
, CHARINDEX('.',REVERSE(LEFT(code, LEN(code) - CHARINDEX('.', REVERSE(code), 2)))) -1
)
) second_last_value
FROM #temp
结果:
second_last_value
-----------------------------
104150
204150