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

SQL-获取数组中特定元素的值,sql,tsql,Sql,Tsql,几天前我问了这个问题,但它涉及到一个更深层次的答案,所以有人建议我创建一个全新的,所以现在开始 免责声明:我无法创建任何自定义DB对象(函数、SP、视图等),因此所有内容都需要在SQL查询中联机 我正在查询审计表,为了简单起见,该表有以下字段: AttributeMask ChangedData CreatedOn ObjectId 数据库中的每条记录都可能有多条与之关联的审核记录。每次对DB记录进行更改时,它都会在审核表中创建一条记录,其中包含特定的ObjectID,该记录将指向源记录Cre

几天前我问了这个问题,但它涉及到一个更深层次的答案,所以有人建议我创建一个全新的,所以现在开始

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

我正在查询审计表,为了简单起见,该表有以下字段:

AttributeMask
ChangedData
CreatedOn
ObjectId
数据库中的每条记录都可能有多条与之关联的审核记录。每次对DB记录进行更改时,它都会在审核表中创建一条记录,其中包含特定的
ObjectID
,该记录将指向源记录
CreatedOn
,该记录将包含更改的
DateTime
AttributeMask
,其中包含在执行保存时已更改的AttributeId列表(注意,可能一次更改了多个字段,
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
这意味着第一条记录仅更改为字段
10501
,第二条记录仅更改为
10192
,第三条记录同时更改为
10192
10501
字段

AttributeMask字段具有以逗号分隔的已更改的所有字段ID列表(请注意,它以逗号开头和结尾)。 ChangedData字段具有
波浪号
(~)已更改数据的分隔列表。
AttributeMask
中的每个条目对应于
ChangedData
中的条目。例如,如果我想查看第一条记录中10501字段中的数据,我需要确定
AttributeMask
字段中的条目是什么(它在列表中是第3个)然后我需要找出
changedata
字段(它是
TRUE
)中条目3中的数据,如果我想查看字段
10192
的第二条记录中的数据,我会查看它在
AttributeMask
中的索引(它是#6),它在
changedata
字段中的对应值是
200000
。 我需要在同一个查询中以某种方式提取这些数据。我得到了一些关于如何做到这一点的帮助,但我在开始时没有提出正确的问题(认为这比解释所有这些更简单)

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

ChangeData1 AttributeMask1  ChangeData2 AttributeMask2
NULL    NULL    TRUE    10501
200000  10192   FALSE   10501
200001  10192   TRUE    10501

我希望这一点现在已经很清楚了。

正如我在评论中所说的那样,你最好先处理一个集合,然后再处理一个越来越广泛的列表,其中包含名称编号的列

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

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

--查询将把字符串转换为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;
但这会很慢

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

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;
DECLARE@Codes表(CodeID INT);
插入@代码值(10192),(10501);
选择t.SomeIdOfYourMainTable
,c.CodeID
,a1.1更改数据
,a1.AttributeMask
来自表1 t
交叉连接@Codes c--将对@Codes中的每个值重复结果
交叉应用
(
选择Top1 a.ChangeData
,a.AttributeMask
来自[审计]a
其中a.objecttypecode=3
a.objectid=t.objectid
和a.AttributeMask-like-CONCAT(“%,”,c.CodeID,“,%”)

而且a.CreatedOn看起来你在最后两段的写作中真的很无聊。为什么需要在ChangedData中对#3进行解码以获得#6?即使如此,为什么你要以#5作为#6为例?无论如何,这看起来是一个糟糕的设计,我会尝试在客户端处理它,而不是尝试使用T-SQL(可能是CLR函数,但你是说你不能在那里做任何事情-尽管它也是通过t-SQL完成的)Hi@Cetin Basoz,你是对的(不是因为我觉得无聊)。我打错了。我改正了。设计并不是那么糟糕,只是不是为了用t-SQL解析。用任何计算机语言解析都很容易(例如C)。我只能创建一个查询。我无法在此数据库中创建任何其他内容(函数、SP、视图)。如果
AttributeMask1
包含两个值(对于
2
相同),该怎么办?最大连接数是多少?名称编号总是一个错误设计的提示。为什么不在列表中返回
数据
掩码
以及行计数器?如果这个列表可能越来越宽,这真的很奇怪…嗨@Shnugo如果AttributeMask1同时包含这两个值,那没关系,因为我只关心1个joi一次n,所以我将只提取该特定连接的值,而另一个值将是另一个连接的一部分。因此,可以通过不同的连接多次提取相同的审核记录。我不确定最大连接数,但我会说大约15。我不打算在实际查询中命名数字。我打算让名称更具解释性。我“我不确定返回行计数器将如何解决我的问题?嗨@Shnugo,这似乎可行,但我不知道如何解决:-)你到底在哪里指明了要查找的字段ID?它只返回10501和10192,但你的查询中到底有哪些字段ID?我问这个问题是因为我需要再查找大约10个字段ID。你是对的,它可能很慢,但我需要在更大的d上测试它的性能
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;