2012-07-17 113 views
2

表我有一个XML变量是这样的:这将更新表更新SQL Server与XML

<code> 
<IDs> 
    <ID id="1">a</ID> 
    <ID id="43">d</ID> 
    <ID id="3">b</ID> 

</IDs> 
</code> 

我想使用在存储过程(SQL Server)的。

我的表是这样的:

ID INT, 
a INT, 
b INT, 
c INT, 
d INT 

说明应增加与ID相关联的字母值。

所以它应该是这样的:

Table Row with ID = 1, update column "a" by increasing the current value by 1. 
Table Row with ID = 43 - update column "d" by increasing current value by 1. 
Finally Table row with ID= 3 - update column "b" by increasing value by 1. 

这是我迄今为止 - (第二行是我最需要帮助的。):

Update MyTable 
SET @letter = letterVal +1 
WHERE ID IN(
SELECT x.v.value('@id','INT') 
FROM @xmlIDs.nodes('/IDs/ID') x(v) 
) 
+0

你说** SQL **(结构化查询语言),但你真的指的是** SQL Server **呢? – 2012-07-17 18:16:16

+1

这......设计很笨拙。你有什么能力在这一点上改变数据的形状吗? – 2012-07-17 18:16:59

+0

是的SQL Server。是的,我可以根据需要重新设计。我真正想要做的就是计算每个不同字母的数量。我实际上有一个考试。考试中的每个问题都有一个ID。我想统计每个问题有多少人回答a或b等。我希望用一个存储过程来完成这个任务,而不是为每个问题调用相同的过程。 – jpsnow72 2012-07-17 18:36:12

回答

2

你将不得不沿着这条线做点什么:

DECLARE @input XML = '<code> 
<IDs> 
    <ID id="1">a</ID> 
    <ID id="43">d</ID> 
    <ID id="3">b</ID> 

</IDs> 
</code>' 

;WITH ParsedXML AS 
(
SELECT 
    ID = C.value('(@id)[1]', 'int'), 
    ColumnName = C.value('(.)[1]', 'varchar(10)') 
FROM @Input.nodes('/code/IDs/ID') AS T(C) 
) 
UPDATE MyTable 
SET a = CASE WHEN p.ColumnName = 'a' THEN t.a + 1 ELSE t.a END, 
    b = CASE WHEN p.ColumnName = 'b' THEN t.b + 1 ELSE t.b END, 
    c = CASE WHEN p.ColumnName = 'c' THEN t.c + 1 ELSE t.c END, 
    d = CASE WHEN p.ColumnName = 'd' THEN t.d + 1 ELSE t.d END 
FROM MyTable t 
INNER JOIN ParsedXml p ON t.ID = p.ID 

SELECT * FROM Mytable 

这会做到 - 但它确实很难看。主要的问题是:除非你使用动态SQL路由,它有自己的一系列优点和缺点,并且可能变得相当混乱,你不能从其他地方将列名作为值来使用,以便在UPDATE语句中使用它。

如果你有兴趣的动态SQL - The Curse and Blessings of Dynamic SQL通过厄兰Sommarskog是绝对必读 - 阅读,然后发射到使用动态SQL!

+0

感谢这就是我一直在寻找!你认为最好是有4个不同的程序(每个程序更新一个不同的字母),然后在我的C#代码循环中,并在那个时候选择正确的程序? – jpsnow72 2012-07-17 18:45:14

+0

@JustinPeterson:如果它只是这四列 - 你肯定可以使用这个代码。这不是很光滑或漂亮 - 但它的工作原理。如果你会得到更多的“选择”,那么也许你需要分开处理它们以分开处理 – 2012-07-17 18:52:56

+1

@mar_s:感谢这个完美的作品! – jpsnow72 2012-07-17 19:22:39

0

我建议你改变你的XML类似于此:

<questions> 
    <question id="1" answer="a" /> 
    <question id="43" answer="d" /> 
    <question id="3" answer="b" /> 
</questions> 

通过此格式,您可以编写一个查询,像这样:

with CTE as (
    select 
     x.n.value('@answer', 'char(1)') as answer 
    from 
     @Input.nodes('//questions/question') as x(n) 
) 
select 
    answer, 
    count(answer) as answerCount 
from 
    CTE 
group by 
    answer; 

然后可以将这些到一个表作为添加必要的,但你设计的桌子看起来不会很有用。

0

一个动态SQL解决方案。不太漂亮,但完全动态:

IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp 
GO 
CREATE TABLE #tmp (ID INT, a INT, b INT, c INT, d INT) 
GO 

INSERT INTO #tmp VALUES 
    (1, 0, NULL, NULL, NULL), 
    (43, NULL, NULL, NULL, 0), 
    (3, NULL, 0, NULL, NULL) 
GO 

SELECT 'before' s, * FROM #tmp 

DECLARE @xml XML 

SET @xml = '<code> 
    <IDs> 
    <ID id="1">a</ID> 
    <ID id="43">d</ID> 
    <ID id="3">b</ID> 
    </IDs> 
</code>' 


-- Run a cursor through the XML resultset to update the table 
DECLARE update_cursor CURSOR FAST_FORWARD LOCAL FOR 
SELECT 
    x.y.value('@id', 'INT') id, 
    x.y.value('.', 'VARCHAR(50)') columnName 
FROM @xml.nodes('code/IDs/ID') AS x(y) 

-- Cursor variables 
DECLARE @id INT, @columnName VARCHAR(50) 
DECLARE @sql NVARCHAR(MAX) 

OPEN update_cursor 

FETCH NEXT FROM update_cursor INTO @id, @columnName 
WHILE @@fetch_status = 0 
BEGIN 

    --SELECT @id, @columnName 

    SET @sql = 'UPDATE #tmp SET ' + @columnName + ' += 1 WHERE ID = ' + CAST(@id AS VARCHAR(20)) 

    EXEC(@sql) 
    --SELECT @sql 

    FETCH NEXT FROM update_cursor INTO @id, @columnName 
END 

CLOSE update_cursor 
DEALLOCATE update_cursor 
GO 

SELECT 'after' s, * FROM #tmp