我在 stackOverflow 和其他网站上进行了大量搜索,但没有找到适合我情况的解决方案。我们的数据位于 SQL Server 16.0.1000.6 数据库中。 我正在使用 SSMS v 20.2。 当用户对记录进行更改时,该更改将作为称为 LogText 的字符串字段捕获在日志表中。 每次进行更改时,更改都会附加到此字段。更改日期在不同的字段中捕获。 我的问题是尝试解析出 LogText 字段中字符串末尾的最新更改。 以下是零件库存记录的示例。 DemandQty 和 OnHandQty 已更改多次,最新更改位于字符串末尾。 我想我需要从字符串的末尾开始,找到第一次出现的“DemandQty”并提取“->”字符后的值。 与 OnHandQty 相同。
我的问题有两个:
示例字符串:
12:01:07 OnHandQty: 10000.00000000 -> 9743.00000000 12:01:07 OnHandQty: 9743.00000000 -> 10000.00000000 15:24:25 DemandQty: 0 -> 257.00000000 15:31:09 OnHandQty: 10000.00000000 -> 9743.00000000 15:31:09 DemandQty: 257.00000000 -> 127.00000000
预期结果:
DemandQty = 127, OnHandQty = 9743
实际结果:NULL
这是我为 DemandQty 提出的想法,但是 @arrowPos 变量返回 <- (in the reversed string) AFTER the first DemandQty rather than before.
的位置DECLARE @inputString NVARCHAR(MAX) = '12:01:07 OnHandQty: 10000.00000000 -> 9743.00000000 12:01:07 OnHandQty: 9743.00000000 -> 10000.00000000 15:24:25 DemandQty: 0 -> 257.00000000 15:31:09 OnHandQty: 10000.00000000 -> 9743.00000000 15:31:09 DemandQty: 257.00000000 -> 127.00000000'
-- Reverse the input string to find the last occurrence of 'DemandQty' from the end.
DECLARE @reversedString NVARCHAR(MAX) = REVERSE(@inputString);
-- Find the position of the first 'DemandQty' in the reversed string.
DECLARE @demandQtyPos INT = CHARINDEX('ytqdnamed', @reversedString) - 1; -- 'ytqdnamed' is 'DemandQty' reversed.
-- Find the position of the '>-' symbol that comes before the first 'DemandQty' in the reversed string.
DECLARE @arrowPos INT = CHARINDEX('>-', @reversedString, @demandQtyPos);
-- Extract the value after '>-' and the space.
DECLARE @result NVARCHAR(50) =
CASE
WHEN @arrowPos > 0 THEN
-- Extract the substring after '>-' (ignoring the space), and reverse it back to get the correct order.
REVERSE(SUBSTRING(@reversedString, @arrowPos + 2, CHARINDEX(' ', @reversedString + ' ', @arrowPos + 2) - @arrowPos - 2))
ELSE
NULL
END;
------Convert the extracted value to an integer.
SELECT CAST(@result AS INT) AS ExtractedIntegerValue;
老实说,我会尽最大努力标准化您的数据,然后使用该数据。这会尽力做到这一点,并且适用于您拥有的数据。您很可能希望在该结果集中找到每个组中的前 1 个。
DECLARE @YourString varchar(8000) = '12:01:07 OnHandQty: 10000.00000000 -> 9743.00000000 12:01:07 OnHandQty: 9743.00000000 -> 10000.00000000 15:24:25 DemandQty: 0 -> 257.00000000 15:31:09 OnHandQty: 10000.00000000 -> 9743.00000000 15:31:09 DemandQty: 257.00000000 -> 127.00000000';
WITH Split AS(
SELECT TRIM(SS.value) AS value,
SS.ordinal,
(ss.ordinal+1) / 2 AS item
FROM STRING_SPLIT(REPLACE(@YourString,' ','|'),'|',1) SS),
Pivoted AS(
SELECT S.item,
MAX(CASE WHEN S.value LIKE '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' THEN S.value END) AS EventTime,
MAX(CASE WHEN S.value NOT LIKE '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' THEN S.value END) AS EventValue
FROM Split S
GROUP BY S.item) --Will also need to group on a unique value if this is coming from a table, not a variable
SELECT P.EventTime,
V.EventName,
MAX(CASE SS.ordinal WHEN 1 THEN TRIM(SS.value) END) AS FromValue,
MAX(CASE SS.ordinal WHEN 2 THEN TRIM(SS.value) END) AS ToValue
FROM Pivoted P
CROSS APPLY (VALUES(NULLIF(CHARINDEX(':',P.EventValue),0)))CI(I)
CROSS APPLY (VALUES(LEFT(P.EventValue,CI.I-1),TRIM(STUFF(P.EventValue,1,CI.I,''))))V(EventName,EventValue)
CROSS APPLY STRING_SPLIT(REPLACE(V.EventValue,'->','|'),'|',1) SS
GROUP BY P.EventTime,
P.item,
V.EventName;--Will also need to group on a unique value if this is coming from a table, not a variable
并且,如上所述,如果您需要获取“最后”值:
WITH Split AS(
SELECT TRIM(SS.value) AS value,
SS.ordinal,
(ss.ordinal+1) / 2 AS item
FROM STRING_SPLIT(REPLACE(@YourString,' ','|'),'|',1) SS),
Pivoted AS(
SELECT S.item,
MAX(CASE WHEN S.value LIKE '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' THEN S.value END) AS EventTime,
MAX(CASE WHEN S.value NOT LIKE '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' THEN S.value END) AS EventValue
FROM Split S
GROUP BY S.item), --Will also need to group on a unique value if this is coming from a table, not a variable
RNs AS(
SELECT P.EventTime,
V.EventName,
MAX(CASE SS.ordinal WHEN 1 THEN TRIM(SS.value) END) AS FromValue,
MAX(CASE SS.ordinal WHEN 2 THEN TRIM(SS.value) END) AS ToValue,
ROW_NUMBER() OVER (PARTITION BY V.EventName ORDER BY P.EventTime DESC, P.item DESC) AS RN --May need to aklter PARTITION BY is using a column
FROM Pivoted P
CROSS APPLY (VALUES(NULLIF(CHARINDEX(':',P.EventValue),0)))CI(I)
CROSS APPLY (VALUES(LEFT(P.EventValue,CI.I-1),TRIM(STUFF(P.EventValue,1,CI.I,''))))V(EventName,EventValue)
CROSS APPLY STRING_SPLIT(REPLACE(V.EventValue,'->','|'),'|',1) SS
GROUP BY P.EventTime,
P.item,
V.EventName) --Will also need to group on a unique value if this is coming from a table, not a variable
SELECT R.EventName,
R.ToValue
FROM RNs R
WHERE R.RN = 1;
但是,当然,正如我在评论中所说,这里的数据永远不应该进入数据库。 修复设计;不要这样存储你的数据。正如您所发现的,使用它是一种痛苦...作为临时解决方案,您可以使用
VIEW
,但长期目标应该是修复问题。未来你会感谢你的。