我在表格中有一个名为RecentlyViewedXml
的XML列,结构如下:
<RecentlyViewedEntityData etc="2">
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business 1</Title>
<LastAccessed>1/1/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/5/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/3/2010</LastAccessed>
</RecentlyViewedItem>
</RecentlyViewedEntityData>
我正在尝试从LastAccessed
元素获取最大日期(理想情况下,与该节点中的数据对应的其余节点项。
我尝试了几个选项,但我的主要问题是我不知道[Last]节点是否始终具有最大日期。我正在使用它,但它没有通过QA
Cast(RecentlyViewedXml as xml).query('data(/RecentlyViewedEntityData/RecentlyViewedItem[last()]/LastAccessed[last()])')
我愿意接受任何想法。
谢谢!
答案 0 :(得分:2)
首先:使用与文化相关的日期格式非常危险。试试这个:
SET LANGUAGE GERMAN;
SELECT CAST('1/3/2010' AS DATE);
SET LANGUAGE ENGLISH;
SELECT CAST('1/3/2010' AS DATE);
您可以将CONVERT()
与第三个参数(在您的情况下为103
)一起使用以避免这种情况,但任何隐式演员都会使用您的系统&#39; s设置。
另一点是XML中的数据。您的'1/1/2010'
日期未正确序列化。这应该是ISO8601
。
一些背景知识:您肯定知道,许多数据类型不会以存储方式显示。像3
这样的数字实际上是二进制模式。每当你想要一个人阅读它时,它必须翻译到一个字符串表示。每当数据必须嵌入到基于字符串的容器中时,它们必须被序列化。只要在序列化和反序列化中应用相同的规则,这就可以正常工作。但阅读方必须依赖适当的价值观。
试试这个:
DECLARE @Xml XML='N<root>
<data>
<SomeInt>1</SomeInt>
<SomeDate>2017-01-01</SomeDate>
<BadDate>1/3/2010</BadDate>
</data>
<data>
<SomeInt>5</SomeInt>
<SomeDate>2017-01-05</SomeDate>
<BadDate>4/3/2010</BadDate>
</data>
<data>
<SomeInt>3</SomeInt>
<SomeDate>2017-01-03</SomeDate>
<BadDate>5/1/2010</BadDate>
</data>
</root>';
简单的XQuery
函数max()
将返回最高int
值
SELECT @xml.value(N'max(//SomeInt)','int') MaxInt;
但这对于(正确的ISO8601)日期不起作用(即使the Remarks-section of the function's docu听起来不同):
SELECT @xml.value(N'max(//SomeDate)','date') MaxDate; --returns NULL
您可以使用嵌入式FLWOR query
逐个转换所有值:
SELECT @xml.value(N'max(for $d in //SomeDate return $d cast as xs:date?)','date') MaxDate;
但这对您的非ISO8601日期不起作用:
SELECT @xml.value(N'max(for $d in //BadDate return $d cast as xs:date?)','date') MaxDate;
您可以获取一个派生表来读取您未显示的值(类似于导入数据的临时表)并使用T-SQL的能力来处理:
DECLARE @mockup TABLE (Id INT IDENTITY , YourXml XML)
INSERT INTO @mockup VALUES
(N'<RecentlyViewedEntityData etc="2">
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business 1</Title>
<LastAccessed>1/1/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/5/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/3/2010</LastAccessed>
</RecentlyViewedItem>
</RecentlyViewedEntityData>');
- 将派生表检索为CTE:
WITH DerivedTable AS
(
SELECT itm.value(N'(Type/text())[1]',N'nvarchar(max)') AS [Type]
,itm.value(N'(DisplayName/text())[1]',N'nvarchar(max)') AS [DisplayName]
,itm.value(N'(Title/text())[1]',N'nvarchar(max)') AS [Title]
,CONVERT(DATE,itm.value(N'(LastAccessed/text())[1]',N'nvarchar(max)'),103) AS [LastAccessed]
FROM @mockup AS m
OUTER APPLY m.YourXml.nodes(N'/RecentlyViewedEntityData/RecentlyViewedItem') AS A(itm)
)
SELECT TOP 1 *
FROM DerivedTable
ORDER BY LastAccessed DESC;
在正确转换(转换)为原始DATE
类型后,您可以使用与ORDER BY
相关联的TOP 1
来获取最大值。
您的评论是正确的,但这比自助加入更容易:
DECLARE @mockup TABLE (Id INT IDENTITY , YourXml XML)
INSERT INTO @mockup VALUES
(N'<RecentlyViewedEntityData etc="2">
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business 1</Title>
<LastAccessed>1/1/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/5/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/3/2010</LastAccessed>
</RecentlyViewedItem>
</RecentlyViewedEntityData>')
,(N'<RecentlyViewedEntityData etc="2">
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business 1</Title>
<LastAccessed>1/1/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/5/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/3/2010</LastAccessed>
</RecentlyViewedItem>
</RecentlyViewedEntityData>');
- 将派生表检索为CTE,包括行的id:
WITH DerivedTable AS
(
SELECT Id
,itm.value(N'(Type/text())[1]',N'nvarchar(max)') AS [Type]
,itm.value(N'(DisplayName/text())[1]',N'nvarchar(max)') AS [DisplayName]
,itm.value(N'(Title/text())[1]',N'nvarchar(max)') AS [Title]
,CONVERT(DATE,itm.value(N'(LastAccessed/text())[1]',N'nvarchar(max)'),103) AS [LastAccessed]
FROM @mockup AS m
OUTER APPLY m.YourXml.nodes(N'/RecentlyViewedEntityData/RecentlyViewedItem') AS A(itm)
)
SELECT TOP 1 WITH TIES *
FROM DerivedTable
ORDER BY ROW_NUMBER() OVER(PARTITION BY ID ORDER BY LastAccessed DESC);
ORDER BY
会使用ROW_NUMBER()
子句调用OVER()
。这将在日期中添加排名,按行的ID进行分区。 TOP 1 WITH TIES
将返回1
中ROW_NUMBER
的所有行。这将是每个表格行的最顶层结果。
答案 1 :(得分:0)
这是我的第一个想法:
declare @xdoc xml = '<RecentlyViewedEntityData etc="2">
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business 1</Title>
<LastAccessed>1/1/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/5/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/3/2010</LastAccessed>
</RecentlyViewedItem>
</RecentlyViewedEntityData>';
DECLARE @xhandle INT
EXEC sp_xml_preparedocument @xhandle OUTPUT, @xdoc
;WITH tmp
AS
(
SELECT *
FROM OPENXML(@xhandle, '//RecentlyViewedItem', 2)
WITH (
DisplayName NVARCHAR(50) 'DisplayName',
Title NVARCHAR(50) 'Title',
LastAccessed DATE 'LastAccessed'
)
)
SELECT * FROM tmp WHERE tmp.LastAccessed = (SELECT MAX(LastAccessed) FROM tmp)
EXEC sp_xml_removedocument @xhandle
答案 2 :(得分:0)
也许这个?
DECLARE @XML xml = '
<RecentlyViewedEntityData etc="2">
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business 1</Title>
<LastAccessed>1/1/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/5/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/3/2010</LastAccessed>
</RecentlyViewedItem>
</RecentlyViewedEntityData>';
WITH XMLData AS (
SELECT X.RR.value('(Type/text())[1]','int') AS [Type], --Guessed the datatype here
X.RR.value('(DisplayName/text())[1]','varchar(50)') AS DisplayName,
X.RR.value('(Title/text())[1]','varchar(50)') AS Title,
X.RR.value('(LastAccessed/text())[1]','date') AS LastAccessed
FROM @xml.nodes('RecentlyViewedEntityData/RecentlyViewedItem') X(RR)),
RNs AS(
SELECT *,
ROW_NUMBER() OVER (ORDER BY LastAccessed DESC) AS RN
FROM XMLData)
SELECT [Type],
DisplayName,
Title,
LastAccessed
FROM RNs
WHERE RN = 1;
答案 3 :(得分:0)
使用Xquery可以轻松解析
DECLARE @TB TABLE (Id int , XCol XML)
INSERT INTO @TB
VALUES (1, '<RecentlyViewedEntityData etc="2">
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business 1</Title>
<LastAccessed>1/1/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/5/2010</LastAccessed>
</RecentlyViewedItem>
<RecentlyViewedItem>
<Type></Type>
<DisplayName>Contact</DisplayName>
<Title>My Book of Business</Title>
<LastAccessed>1/3/2010</LastAccessed>
</RecentlyViewedItem>
</RecentlyViewedEntityData>')
SELECT MAX(LastAccessed) LastAccessed , DisplayName , Title
FROM
(
SELECT TRY_CONVERT(DATE,x.c.value('./LastAccessed[1]', 'varchar(100)')) LastAccessed,
x.c.value('./DisplayName[1]', 'varchar(100)') DisplayName,
x.c.value('./Title[1]', 'varchar(100)') Title
FROM @tb
CROSS APPLY Xcol.nodes ('/RecentlyViewedEntityData/RecentlyViewedItem') x(c)
) P
GROUP BY DisplayName , Title
注意强> 如果您的结构如上所述,则此查询将生成您需要的结果。
NOTE2 我使用了自SQL 2012以来可用的TRY_CONVERT,如果您的版本较旧,例如2008年你需要使用常规的转换或转换,但是你需要使用case语句来处理将日期的字符串值转换为日期数据类型的失败。