SQL Server 如何在SQL中将自由文本varchar值中的值提取为单独的记录

relj7zay  于 2023-01-20  发布在  其他
关注(0)|答案(3)|浏览(135)

在SQL Server中,从具有自由文本varchar值的表中提取值作为记录的最佳方法是什么?
我有一个带有varchar列的表,其中包含一些实验室值的自由文本,因此它包含非结构化文本,但幸运的是实验室值可以用括号识别,例如[NA/bl: 137]
对于每个LabId,我想将实验室值提取到一个新的单独的表中以供进一步处理。问题是varchar文本中实验室值的数量是变化的,对于一些记录是3个值,对于另一些记录是5个值,有时根本没有值。
我创建了一个代表性数据集来重现该问题,请参见下面的示例

CREATE TABLE #TestTable123(
    [_record_number] int IDENTITY(1,1) PRIMARY KEY,
    [LabId] Integer,
    [LabDate] datetime,
    LabDescription varchar(200)
);

insert into #TestTable123(
    LabId,
    LabDate,
    LabDescription
) values
('1001', '2022-02-13', 'questionnaire completed, labvalues [NA/bl: 141] [HCT/blo: 0.39] [HGB: 8.2] [WBC: 7.0], cardiotest completed'),
('1002', '2021-04-10', 'noshow'),
('1003', '2021-10-18', 'questionnaire completed, lab [NA/bl: 138] [HCT/blo: 0.29] [HGB: 4.7]'),
('1004', '2022-06-07', 'labresults [NA/bl: 140] [HCT/blo: 0.31] [HGB: 5.5] [WBC: 3.2], questionnaire completed'),
('1005', '2021-11-26', 'lab [NA/bl: 136] [HCT/blo: 0.38] [HGB: 6.8]')

我尝试了下面的SQL语句,但是这只会在每个varchar文本中产生第一个实验结果,所以在示例数据中,这只是NA/bl值。

select
    LabId,
    substring(LabDescription, charindex('[', LabDescription), charindex(']', LabDescription)-charindex('[', LabDescription) + 1)
from
    #TestTable123
where
    LabDescription like '%\[%' escape '\'

我要查找的是以下结果数据集

LabId  LabResult_extracted
1001   [NA/bl: 141]
1001   [HCT/blo: 0.39]
1001   [HGB: 8.2]
1001   [WBC: 7.0]
1003   [NA/bl: 138]
1003   [HCT/blo: 0.29]
1003   [HGB: 4.7]
1004   [NA/bl: 140]
1004   [HCT/blo: 0.31]
1004   [HGB: 5.5]
1004   [WBC: 3.2]
1005   [NA/bl: 140]
1005   [HCT/blo: 0.38]
1005   [HGB: 6.8]

得到这个结果的最好方法是什么?

e1xvtsh3

e1xvtsh31#

得到这个结果的最好方法是什么?
INSERT首先以标准化形式存储数据。(或多个表),并且您的数据具有 * n * 对许多关系。看起来您在该列中有3条不同的信息,并且这些值中的(一些)也可以表示多个值。取值'questionnaire completed, labvalues [NA/bl: 141] [HCT/blo: 0.39] [HGB: 8.2] [WBC: 7.0], cardiotest completed',例如,你有字符串questionnaire completedlabvalues,然后你有多个实验室结果,还有一个cardiotest completed值,这应该是 * 至少 * 3列,可能至少2个额外的表。
对于你想要的值,你可以在SQL Server中这样做,但这并不理想。如果你将来的计划是能够搜索具有特定值的行(如'[HGB: 5.5]'),那么规范化是必须的。
然而,对于示例数据和您给出的预期结果,这是一种方法,但不是"最佳"方法:

SELECT TT123.LabId,
       TRIM(SS.[value]) + ']' AS Description
FROM #TestTable123 TT123
     CROSS APPLY (VALUES(STUFF(TT123.LabDescription,1,NULLIF(CHARINDEX('[',TT123.LabDescription),0)-1,'')))V(Descriptions)
     CROSS APPLY STRING_SPLIT(V.Descriptions,']') SS
WHERE TRIM(SS.[value]) LIKE '\[%' ESCAPE '\';
epggiuax

epggiuax2#

另一个选项是使用帮助器函数
现在,这个函数去掉了[],但是如果需要的话,可以将它们添加回去

    • 示例**
Select A.LabID 
      ,B.*
 From #TestTable123 A
 Cross Apply [dbo].[tvf-Str-Extract-JSON](LabDescription,'[',']') B
    • 结果**

    • 表值函数(如果感兴趣)**
CREATE FUNCTION [dbo].[tvf-Str-Extract-JSON] (@String nvarchar(max),@Delim1 nvarchar(100),@Delim2 nvarchar(100))
Returns Table 
As
Return (  

    Select RetSeq = row_number() over (order by RetSeq)
          ,RetVal = left(RetVal,charindex(@Delim2,RetVal)-1)
    From  (
            Select RetSeq = [Key]+1
                  ,RetVal = trim(Value)
             From  OpenJSON( N'["'+replace(string_escape(@String,'json'),@Delim1,'","')+N'"]' )

          ) C1
    Where charindex(@Delim2,RetVal)>1

)
    • 编辑:如果您不想要TVF**
Select A.LabID 
      ,B.*
 From #TestTable123 A
 Cross Apply (
                Select RetSeq = row_number() over (order by RetSeq)
                      ,RetVal = left(RetVal,charindex(']',RetVal)-1)
                From  (
                        Select RetSeq = [Key]+1
                              ,RetVal = trim(Value)
                         From  OpenJSON( N'["'+replace(string_escape(LabDescription,'json'),'[','","')+N'"]' )
                      ) C1
                Where charindex(']',RetVal)>1
 ) B
ylamdve6

ylamdve63#

为了完整起见,我将在这里发布这篇文章。这是我们能想到的最好的T-SQL。虽然没有STRING_SPLIT/STUFF的答案那么优雅,但它也能正常工作。

CREATE TABLE #tmp_result(
    _record_number INT IDENTITY(1,1) PRIMARY KEY,
    LabID INTEGER,
    LabResult_extracted VARCHAR(200)
);

-- variables
DECLARE @lab_id INTEGER
DECLARE @lab_desc VARCHAR(200)

DECLARE @idx1 INTEGER
DECLARE @idx2 INTEGER
DECLARE @lab_val VARCHAR(200)

-- cursor
DECLARE db_cursor CURSOR FOR 
SELECT LabId, LabDescription
FROM #TestTable123
WHERE LabDescription LIKE '%\[%' ESCAPE '\'

-- start the cursor
OPEN db_cursor  
FETCH NEXT FROM db_cursor INTO @lab_id, @lab_desc

-- iterate all records
WHILE @@FETCH_STATUS = 0  
BEGIN 
    -- iterate lab values in varchar value
    SET @idx1 = 1
    WHILE @idx1 <> 0
    BEGIN
        SET @idx1 = CHARINDEX('[', @lab_desc)
        SET @idx2 = CHARINDEX(']', @lab_desc)
        IF @idx1 > 0 AND @idx2 > @idx1
        BEGIN
            SET @lab_val = SUBSTRING(@lab_desc, @idx1, @idx2-@idx1+1)

            INSERT INTO #tmp_result(LabID, LabResult_extracted)
            VALUES(@lab_id, @lab_val)

            SET @lab_desc = SUBSTRING(@lab_desc, @idx2+1, LEN(@lab_desc) - @idx2)
        END
    END

    FETCH NEXT FROM db_cursor INTO @lab_id, @lab_desc
END 

CLOSE db_cursor  
DEALLOCATE db_cursor

-- result in tmp table
SELECT * FROM #tmp_result

相关问题