我有一个表格,用XML定义表单的布局。其中有<Control ...>
个节点,它们具有Id(GUID)和DataType(char)等属性......
此XML数据用于在运行时创建表单,当数据保存时,它会在名为<Field ...>
的元素中写入XML,这些元素具有以下属性:Name(与控件中的GUID匹配)节点的Id)和Data(保存输入控件的值)。
我们遇到一个问题,当你.ToString一个Date对象时,它使用机器的日期格式设置。所以日期可以以任何可能的格式保存。我已更新代码以始终以YYYY/MM/DD
格式保存日期,但现在我需要将数据库中的现有数据更新为YYYY/MM/DD
。由于我们无法知道保存日期的格式,我们只是假设它已保存为MM/DD/YYYY
。
所以现在我的问题是尝试更新SQL Server中的XML。使用xquery和CROSS APPLY .nodes()的东西,我能够编写一个查询来查找所有格式不正确的日期,但我无法弄清楚如何更新它们。
此SQL脚本将创建表变量并使用一些测试数据填充它们,并具有将返回incorect日期值的查询。注释掉的最后一部分是我如何尝试将它们更新为新的YYYY/MM/DD
格式,但如果您取消注释,则可以看到它无法正常工作。
有人有什么想法吗?
P.S。这只能在SQL脚本中完成。我知道在.NET中编写一个函数非常容易,它读取所有xml数据并更新属性然后将数据保存回数据库,但在这种情况下这是不可能的,因为:原因。
DECLARE @Form AS Table
(
FormID INT,
FormDataXML XML
);
DECLARE @ScreenData AS Table
(
ScreenDataID INT,
FormID INT,
ScreenDataXML XML
);
DECLARE @ControlsToUpdate AS TABLE
(
FormID INT,
ControlID char(36),
DataType CHAR(1)
);
INSERT INTO @Form
VALUES (1, '<Form><Control Id="00000000-0000-0000-0000-000000000000" DataType="D" /><Control Id="00000000-0000-0000-0000-000000000001" DataType="N" /></Form>');
INSERT INTO @Form
VALUES (2, '<Form><Control Id="00000000-0000-0000-0000-000000000002" DataType="D" /><Control Id="00000000-0000-0000-0000-000000000003" DataType="D" /></Form>');
INSERT INTO @ScreenData
VALUES (1, 1, '<ScreenData><Field Name="00000000-0000-0000-0000-000000000000" Data="01/31/2012" /><Field Name="00000000-0000-0000-0000-000000000001" Data="1234.56" /></ScreenData>');
INSERT INTO @ScreenData
VALUES (2, 1, '<ScreenData><Field Name="00000000-0000-0000-0000-000000000000" Data="02/28/2013" /><Field Name="00000000-0000-0000-0000-000000000001" Data="0" /></ScreenData>');
INSERT INTO @ScreenData
VALUES (3, 2, '<ScreenData><Field Name="00000000-0000-0000-0000-000000000002" Data="03/31/2013" /><Field Name="00000000-0000-0000-0000-000000000003" Data="04/30/2013" /></ScreenData>');
INSERT INTO @ScreenData
VALUES (4, 2, '<ScreenData><Field Name="00000000-0000-0000-0000-000000000002" Data="2013/05/31" /><Field Name="00000000-0000-0000-0000-000000000003" Data="2013/06/30" /></ScreenData>');
--Data treated as scheduled items
INSERT INTO @ScreenData
VALUES (5, 2, '<ScreenData><Item><Field Name="00000000-0000-0000-0000-000000000002" Data="01/01/2012" /><Field Name="00000000-0000-0000-0000-000000000003" Data="02/02/2012" /></Item><Item><Field Name="00000000-0000-0000-0000-000000000002" Data="03/03/2012" /><Field Name="00000000-0000-0000-0000-000000000003" Data="04/04/2012" /></Item></ScreenData>')
INSERT INTO @ControlsToUpdate
SELECT FormID,
data.control.value('@Id', 'char(36)'),
data.control.value('@DataType', 'char(1)')
FROM @Form
CROSS APPLY FormDataXML.nodes('//Control') data(control)
WHERE data.control.value('@DataType', 'char(1)') = 'D';
--This will display the ScreenDataID, FormID, ControlID, and current Date value for dates that are in the old mm/dd/yyyy format
SELECT d.ScreenDataID, d.FormID, c.ControlID,
data.field.value('@Data', 'char(10)') as Date
FROM @ScreenData d
CROSS APPLY d.ScreenDataXML.nodes('//Field') as data(field)
INNER JOIN @ControlsToUpdate c ON c.ControlID = data.field.value('@Name', 'CHAR(36)')
AND d.FormID = c.FormID
WHERE data.field.value('@Data', 'char(10)')
LIKE '[01][0123456789]/[0123][0123456789]/[12][0123456789][0123456789][0123456789]';
--UPDATE d
--SET data.field.modify('replace value of (/@Data) with "' +
-- SUBSTRING(data.field.value('@Data', 'char(10)'), 7, 4) + '/' +
-- SUBSTRING(data.field.value('@Data', 'char(10)'), 1, 2) + '/' +
-- SUBSTRING(data.field.value('@Data', 'char(10)'), 4, 2) + '"')
--FROM @ScreenData d
--CROSS APPLY d.ScreenDataXML.nodes('//Field') as data(field)
--INNER JOIN @ControlsToUpdate c ON c.Id = data.field.value('@Name', 'CHAR(36)')
-- AND d.FormID = c.FormID
--WHERE data.field.value('@Data', 'varchar(MAX)')
--LIKE '[01][0123456789]/[0123][0123456789]/[12][0123456789][0123456789][0123456789]';
GO
- Edit--
@ScreenData中的xml还可以包含<Item>
个包含<Field>
个节点的节点。发生这种情况时,您将拥有多个具有相同Name属性值的<Field>
个节点。当屏幕具有项目的列表视图时,您将拥有多个项目,每个项目都引用相同的控件但具有自己的值。 ScreenDataID 5显示了这一点。
答案 0 :(得分:1)
我不会告诉你你想要什么。我将向您展示如何将所有您的日期更改为yyyy-mm-dd
,因为这应该是XML格式。
到目前为止,你已经把事情搞清楚了,所以如果你决定使用其他日期格式,我认为你不会有任何问题将下面的代码更改为你的需要。
您无法在XML中一次更新多个值,因此必须在需要更新的表单和字段的循环中完成更新。
SET DATEFORMAT ymd;
是从字符串到日期转换同时从yyyy/mm/dd
和mm/yy/dd
同时工作所必需的。
SET DATEFORMAT ymd;
DECLARE @FormID INT;
DECLARE @FieldID UNIQUEIDENTIFIER;
DECLARE @I INT;
DECLARE ControlsToUpdate CURSOR FORWARD_ONLY READ_ONLY FOR
SELECT F.FormID,
T.N.value('@Id', 'uniqueidentifier') AS FieldID
FROM @Form AS F
CROSS APPLY F.FormDataXML.nodes('/Form/Control') AS T(N)
WHERE T.N.value('@DataType', 'char(1)') = 'D';
OPEN ControlsToUpdate;
FETCH NEXT FROM ControlsToUpdate
INTO @FormID, @FieldID;
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @I = MAX(S.ScreenDataXML.value('count(//Field[@Name = sql:variable("@FieldID")])', 'INT'))
FROM @ScreenData AS S
WHERE S.FormID = @FormID;
WHILE @I > 0
BEGIN
UPDATE S
SET ScreenDataXML.modify('replace value of
((//Field[@Name = sql:variable("@FieldID")]
/@Data)[sql:variable("@I")])[1]
with sql:column("T.D")')
FROM @ScreenData AS S
CROSS APPLY
(SELECT S.ScreenDataXML.value('((//Field[@Name = sql:variable("@FieldID")]
/@Data)[sql:variable("@I")])[1]', 'DATE')) AS T(D)
WHERE S.FormID = @FormID;
SET @I -= 1;
END
FETCH NEXT FROM ControlsToUpdate
INTO @FormID, @FieldID;
END
CLOSE ControlsToUpdate;