MS SQL 查询在关键字之前查找字符串中的位置并提取该位置之前的数据

问题描述 投票:0回答:1

我在 stackOverflow 和其他网站上进行了大量搜索,但没有找到适合我情况的解决方案。我们的数据位于 SQL Server 16.0.1000.6 数据库中。 我正在使用 SSMS v 20.2。 当用户对记录进行更改时,该更改将作为称为 LogText 的字符串字段捕获在日志表中。 每次进行更改时,更改都会附加到此字段。更改日期在不同的字段中捕获。 我的问题是尝试解析出 LogText 字段中字符串末尾的最新更改。 以下是零件库存记录的示例。 DemandQty 和 OnHandQty 已更改多次,最新更改位于字符串末尾。 我想我需要从字符串的末尾开始,找到第一次出现的“DemandQty”并提取“->”字符后的值。 与 OnHandQty 相同。

我的问题有两个:

  1. 如何找到反转字符串中位于 DemandQty 或 OnHandQty 之前的 '->' 的位置?
  2. 如何提取反转字符串中箭头之前的整数值?

示例字符串:

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;
sql sql-server charindex
1个回答
0
投票

老实说,我会尽最大努力标准化您的数据,然后使用该数据。这会尽力做到这一点,并且适用于您拥有的数据。您很可能希望在该结果集中找到每个组中的前 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
,但长期目标应该是修复问题。未来你会感谢你的。

© www.soinside.com 2019 - 2024. All rights reserved.