SQL-获取数组中特定元素的值

时间:2018-08-16 17:28:52

标签: sql tsql

几天前我问了这个问题,但是它涉及到更深的答案,因此有人建议我创建一个全新的问题,所以就这样...

免责声明:我无法创建任何自定义DB对象(函数,SP,视图等),因此所有内容都需要在SQL查询中内联。

我正在查询Audit表,为简化此问题,该表具有以下字段:

AttributeMask
ChangedData
CreatedOn
ObjectId

一个数据库中的每个记录可能有多个与之关联的审核记录。每次对数据库记录进行更改时,它将在Audit表中创建一条记录,其中包含特定的ObjectID指向源记录,CreatedOn的{​​{1}}为更改,DateTime带有在执行SAVE时已更改的AttributeId的列表(注意,一次可能有多个字段被更改),而AttributeMask实际上将具有已更改(预先更改)的数据值)。一个字段当然可以多次更改,并且在这种情况下,将存在该字段的多个审核记录(不同的ChangedData值)。我需要找到源记录中特定日期的某些(不是全部)字段。

我可以在下面运行查询

CreatedOn

此查询查找对select a1.ChangeData as ChangedData1, a1.AttributeMask as AttributeMask2, a2.ChangeData as ChangedData2, a2.AttributeMask as AttributeMask2 from Table1 t join audit a1 on a1.AuditId = (select top 1 a.auditid from audit a where a.objecttypecode = 3 and a.objectid = T.ObjectId and a.AttributeMask like '%,10192,%' and a.CreatedOn <= '8-16-2018' order by a1.CreatedOn desc) join audit a2 on a2.AuditId = (select top 1 a.auditid from audit a where a.objecttypecode = 3 and a.objectid = T.ObjectId and a.AttributeMask like '%,10501,%' and a.CreatedOn <= '8-16-2018' order by a1.CreatedOn desc) where t.ObjectID = SomeGuidValue 之前发生的2个字段(1019210501)的最新更改。它返回以下数据(我添加了第3条记录以说明所有可能的情况):

8-16-2018

这意味着第一条记录仅更改为字段ChangeData1 AttributeMask1 ChangeData2 AttributeMask2 NULL NULL True~~True~1904~~~15.8700000000~4760~30000~590~12000~0~390~1904~False~200~ ,10499,10604,10501,10436,10491,10490,10459,10099,10319,10253,10433,10031,10091,10020,10265,10008,10509, ~True~5.56~~House~~200000~ ,10030,10432,10435,197,10099,10192,198, False~1170~600~0~Complete~True~1770~ ,10501,10091,10008,10020,10570,10499,10253,10715, ~~~~200001~ ,10432,10435,197,10099,10192,198, True~2~True~~0~~~100.0000000000~1~business,96838c4f-e63c-e011-9a14-78e7d1644f78~~0~~~~0~False~~1~ ,10499,10509,10501,10203,10436,10491,10490,10459,10099,10157,10253,10433,10715,10031,10091,10020,10265,10008,10319,10699, ,第二条记录仅更改为10501,第三条记录同时更改了1019210192字段。

AttributeMask字段包含所有已更改的FieldID的逗号分隔列表(请注意,它以逗号开头和结尾)。 ChangedData字段具有10501(〜)分隔的已更改数据列表。 tilde中的每个条目都对应于AttributeMask中的条目。例如,如果我想查看第一条记录中10501字段中的数据,则需要确定#ChangedData字段中的条目10501(列表中的#3),然后我将需要找出AttributeMask字段(ChangedData)中条目#3中的数据,如果我想查看字段TRUE的第二条记录中的内容,我会看到什么它在10192中的索引(它是#6),在AttributeMask字段中的对应值是ChangedData。 我需要以某种方式在同一查询中提取此数据。 samples帮助我解决了如何完成此工作,但一开始我没有提出正确的问题(这比解释所有这些问题要容易得多)。

我需要此查询返回的内容如下:

2000000

我希望现在已经清楚了。

1 个答案:

答案 0 :(得分:1)

如我的评论所述,您最好处理一个集合,然后使用具有 name-numbered 列的越来越多的列表进行处理。

尝试以如下模型表的格式提供初始输入集:

有一个正在运行的ID,您的ObjectID,您要查找的代码以及两个字符串。我插入了您提供的数据,但没有并排

DECLARE @tbl TABLE(ID INT IDENTITY, CodeId INT,ObjectId INT,  ChangeData VARCHAR(1000), AttributeMask VARCHAR(1000));
INSERT INTO @tbl VALUES
 (10192,1,NULL,NULL)
    ,(10501,1,'True~~True~1904~~~15.8700000000~4760~30000~590~12000~0~390~1904~False~200~',',10499,10604,10501,10436,10491,10490,10459,10099,10319,10253,10433,10031,10091,10020,10265,10008,10509,')
,(10192,2,'~True~5.56~~House~~200000~',',10030,10432,10435,197,10099,10192,198,')
   ,(10501,2,'False~1170~600~0~Complete~True~1770~',',10501,10091,10008,10020,10570,10499,10253,10715,')
,(10192,3, '~~~~200001~',',10432,10435,197,10099,10192,198,')
   ,(10501,3,'True~2~True~~0~~~100.0000000000~1~business,96838c4f-e63c-e011-9a14-78e7d1644f78~~0~~~~0~False~~1~',',10499,10509,10501,10203,10436,10491,10490,10459,10099,10157,10253,10433,10715,10031,10091,10020,10265,10008,10319,10699,');

-查询会将字符串转换为XML,以便通过其位置索引将其插入
-然后将所有代码提取并编号为派生列表。
-根据找到的位置取相应的值

SELECT t.ID 
      ,t.ObjectId
      ,t.CodeId
      ,t.ChangeData
      ,t.AttributeMask
      ,Casted.ValueXml.value('/x[sql:column("PartIndex")][1]','nvarchar(max)') ValueAtCode  
FROM @tbl t
CROSS APPLY
(
    SELECT CAST('<x>' + REPLACE(t.AttributeMask,',','</x><x>') + '</x>' AS XML).query('/x[text()]') AS CodeXml
          ,CAST('<x>' + REPLACE(t.ChangeData,'~','</x><x>') + '</x>' AS XML) AS ValueXml
) Casted
CROSS APPLY(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PartIndex
          ,x.value('text()[1]','nvarchar(max)') AS CodePart
    FROM Casted.CodeXml.nodes('/x') A(x)
) CodeDerived
WHERE CodeDerived.CodePart=t.CodeId;

结果

ID  ObjectId    CodeId  ValueAtCode
2   1           10501   True
3   2           10192   
4   2           10501   False
5   3           10192   200001
6   3           10501   True

但是这会很糟糕...

更新

您的整个方法不是基于集合的。以下内容未经测试,我没有您的数据库,但将指向基于集合的解决方案。

DECLARE @Codes TABLE(CodeID INT);
INSERT INTO @Codes VALUES(10192),(10501);

select t.SomeIdOfYourMainTable
      ,c.CodeID
      ,a1.ChangeData
      ,a1.AttributeMask
from Table1 t
CROSS JOIN @Codes c --will repeat the result for each value in @Codes
CROSS APPLY
(
    select top 1 a.ChangeData
                ,a.AttributeMask             
    from [audit] a 
    where a.objecttypecode = 3
    and a.objectid = t.ObjectId
    and a.AttributeMask like CONCAT('%,',c.CodeID,',%')
    and a.CreatedOn <= '20180816' --use culture independant format!!!
    order by a.CreatedOn desc
) a1;

这允许您插入任意数量的代码(无需重复任何连接),并且将返回与我上面的示例类似的集合。

如果您需要进一步的帮助:请关闭此问题,然后使用完全正常运行的独立MCVE来开始新的问题,以重制您的案子。