1

我已经查看了与此类似的〜8个线程,但没有一个能满足我的确切需求(此处有问题的列中缺乏分隔符一致性),因此请不要在未完全阅读和理解我的情况下将其标记为可能重复在问。

Azure SQL Server 2019:

我继承了一个名为 dbo.Table 的表,其中包含数百万条记录,如下所示:

Id   Body
1    Status: Completed
     Successful actions count: 106
     Page load count: 105

2    Status: Failed
     Successful actions count: 91
     Page load count: 90

3    Status: Completed
     Successful actions count: 44
     Page load count: 32

我知道(并对事实感到恼火)这种结构不是最佳的。我需要修复它,并感谢任何朝着正确方向的建议:

我在表格中添加了三列:StatusSuccessful_Actions_CountPage_Load_Count

解析数据的最佳方法是什么Body对于现有数据和未来插入,将列中的数据解析到三个新列

我不是在找人为我编写存储过程。而是类似于我可以使用哪些 SQL Server 函数来促进这一点,最好通过现有的存储过程和未来的触发器来实现这一点?

我在看STRING_SPLIT,但这似乎是针对逗号分隔的字符串(或其他一些分隔符)。以我有限的 SQL 技能对我来说,这里的挑战是没有一致的分隔符可以使用 - 空间显然不起作用。来自 SQL 专家的任何想法将不胜感激。

编辑 1: 我使用下面的@Zhorov 解决方案以我们需要查看结果的方式显示结果,但这在 Trigger 内不起作用,因此我们实际上可以将结果写入记录中。

这是触发器创建语句 - 我仅使用其中一列对其进行测试:

CREATE TRIGGER [dbo].[BodyParseTrigger] on [dbo].[MailArchive]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.MailArchive (
 Status
)
SELECT 
j.*
FROM INSERTED
CROSS APPLY OPENJSON (CONCAT('{"', REPLACE(REPLACE(Mail_Body, ': ', '":"'), CHAR(10), '","'), '"}')) 
WITH (
   Status varchar(100) '$.Status'
     ) j
SET NOCOUNT OFF
END

创建命令成功完成。但是,每当INSERT对表进行操作时,我都会收到一条错误消息,该消息完全阻止了该操作INSERT。消息是:

JSON 文本格式不正确。在位置 10 发现意外字符“'”。

这是触发器要克服的第一个问题——一旦我弄清楚了,我还需要了解如何将多个这些OPENJSON语句放入以涵盖所有 3 列。将其写入@Zhorov 答案使其仅作为值插入的一项,其中触发器需要 3 项。

4

2 回答 2

2

原答案:

另一种可能的方法是 JSON 转换(正如评论中提到的@PanagiotisKanavos)。您需要将Body数据转换为有效的 JSON 对象,OPENJSON()并使用显式模式解析该对象:

桌子:

CREATE TABLE Data (
   Id int,
   Body varchar(max) 
)
INSERT INTO Data
   (Id, Body)
VALUES
   (1, 'Status: Completed' + CHAR(13) + CHAR(10) + 'Successful actions count: 106' + CHAR(13) + CHAR(10) + 'Page load count: 105'),
   (2, 'Status: Failed' + CHAR(13) + CHAR(10) + 'Successful actions count: 91' + CHAR(13) + CHAR(10) + 'Page load count: 90'),
   (3, 'Status: Completed' + CHAR(13) + CHAR(10) + 'Successful actions count: 40' + CHAR(13) + CHAR(10) + 'Page load count: 44')

陈述:

SELECT j.*
FROM Data d
CROSS APPLY OPENJSON (CONCAT('{"', REPLACE(REPLACE(d.Body, ': ', '":"'), CHAR(13) + CHAR(10), '","'), '"}')) WITH (
   Status varchar(100) '$.Status',
   Successful_Actions_Count int '$."Successful actions count"',
   Page_Load_Count int '$."Page load count"'
) j

结果:

-------------------------------------------------------
Status      Successful_Actions_Count    Page_Load_Count
-------------------------------------------------------
Completed   106                         105
Failed      91                          90
Completed   40                          44         

如果列中有NULL值,则Body可以尝试使用以下方法:

SELECT d.Id, j.*
FROM Data d
OUTER APPLY OPENJSON (
   CASE 
      WHEN d.Body IS NULL THEN '{}'
      ELSE CONCAT('{"', REPLACE(REPLACE(d.Body, ': ', '":"'), CHAR(13) + CHAR(10), '","'), '"}')
   END   
) WITH (
   Status varchar(100) '$.Status',
   Successful_Actions_Count int '$."Successful actions count"',
   Page_Load_Count int '$."Page load count"'
) j

如果Body列中的数据以新行结尾,则需要添加一个额外的key:value对 ( "x": "0") 以使 JSON 有效:

SELECT d.Id, j.*
FROM Data d
OUTER APPLY OPENJSON (
   CASE 
      WHEN d.Body IS NULL THEN '{}'
      ELSE CONCAT('{"', REPLACE(REPLACE(d.Body, ': ', '":"'), CHAR(13) + CHAR(10), '","'), 'x": "0"}')
   END   
) WITH (
   Status varchar(100) '$.Status',
   Successful_Actions_Count int '$."Successful actions count"',
   Page_Load_Count int '$."Page load count"'
) j

更新:

如果您想实现触发器(我认为您需要一种不同类型的触发器),接下来的代码行可能会有所帮助。

表和触发器:

CREATE TABLE MailArchive (
   Id int,
   Mail_Body varchar(max),
   Status varchar(100),
   Successful_actions_count int,
   Page_load_count int
);
CREATE TRIGGER BodyParseTrigger ON MailArchive INSTEAD OF INSERT
AS BEGIN
   INSERT INTO MailArchive (ID, Mail_Body, Status, Successful_Actions_Count, Page_Load_Count)
   SELECT i.ID, i.Mail_Body, j.Status, j.Successful_Actions_Count, j.Page_Load_Count
   FROM Inserted i
   OUTER APPLY OPENJSON (CONCAT('{"', REPLACE(REPLACE(i.Mail_Body, ': ', '":"'), CHAR(13) + CHAR(10), '","'), '"}')) 
   WITH (
      Status varchar(100) '$.Status',
      Successful_Actions_Count int '$."Successful actions count"',
      Page_Load_Count int '$."Page load count"'
   ) j
END

陈述:

INSERT INTO MailArchive
   (Id, Mail_Body)
VALUES
   (1, 'Status: Completed' + CHAR(13) + CHAR(10) + 'Successful actions count: 106' + CHAR(13) + CHAR(10) + 'Page load count: 105')

SELECT *
FROM MailArchive

结果:

---------------------------------------------------------------------------------------
Id  Mail_Body                     Status      Successful_actions_count  Page_load_count
---------------------------------------------------------------------------------------
1   Status: Completed             Completed   106                         105
    Successful actions count: 106
    Page load count: 105    

如何删除额外的换行符:

如果您的Mail_Body列包含额外的换行符,您可以尝试更改转换以消除可能的 JSON 解析错误。现在,转换的结果将是 JSON 数组 ( ["Status: Completed", ...]),而不是 JSON 对象 ( {"Status":"Completed", ...})。在这种情况下,您应该使用OPENJSON()默认模式(不带WITH子句)并使用MAX()来获得预期的结果:

带有额外换行符的表格和数据:

DECLARE @text1 varchar(max) = 
   'Status: Completed' + CHAR(13) + CHAR(10) + 
   'Successful actions count: 106' + CHAR(13) + CHAR(10) + 
   'Page load count: 105' + CHAR(13) + CHAR(10) +
   CHAR(13) + CHAR(10) +
   CHAR(13) + CHAR(10) +
   CHAR(13) + CHAR(10)
DECLARE @text2 varchar(max) = 
   'Agent did not meet defined success criteria on this run.' + CHAR(13) + CHAR(10) +
   CHAR(13) + CHAR(10) +
   'Status: Completed' + CHAR(13) + CHAR(10) + 
   'Successful actions count: 106' + CHAR(13) + CHAR(10) + 
   'Page load count: 105' + CHAR(13) + CHAR(10) +
   CHAR(13) + CHAR(10) +
   CHAR(13) + CHAR(10)

CREATE TABLE Data (
   Id int,
   Mail_Body varchar(max) 
)
INSERT INTO Data 
   (Id, Mail_Body)
VALUES 
   (1, @text1),
   (2, @text2)

声明:

SELECT d.Id, j.[Status], j.Successful_actions_count, j.Page_load_count
FROM Data d
OUTER APPLY (
   SELECT
      MAX(CASE WHEN CHARINDEX('Status:', [value]) = 1 THEN REPLACE([value], 'Status:', '') END) AS [Status],
      MAX(CASE WHEN CHARINDEX('Successful actions count:', [value]) = 1 THEN REPLACE([value], 'Successful actions count:', '') END) AS [Successful_actions_count],
      MAX(CASE WHEN CHARINDEX('Page load count:', [value]) = 1 THEN REPLACE([value], 'Page load count:', '') END) AS [Page_load_count]
   FROM OPENJSON(CONCAT('["', REPLACE(d.Mail_Body, CHAR(13) + CHAR(10), '","'), '"]'))
) j

结果:

-----------------------------------------------------------
Id   Status      Successful_actions_count   Page_load_count
-----------------------------------------------------------
1    Completed   106                        105
2    Completed   106                        105
于 2020-01-09T17:43:20.037 回答
2

而不是 string_split() 和条件聚合和结构是一致的,还有另一种选择......一点 XML

例子

Select A.ID 
      ,Status  = stuff(Pos1,1,charindex(':',Pos1),'')
      ,Action  = try_convert(int,stuff(Pos2,1,charindex(':',Pos2),''))
      ,PageCnt = try_convert(int,stuff(Pos3,1,charindex(':',Pos3),''))
 From YourTable A
 Cross Apply (
                Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
                      ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
                      ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
                From  ( values (cast('<x>' + replace((Select replace(Body,char(13)+char(10),'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml)))  A(xDim)
             ) B 

退货

ID   Status     Action  PageCnt
1    Completed  106     105
于 2020-01-09T16:46:33.283 回答