用于替换节点的XML DML脚本

时间:2016-03-28 17:25:35

标签: xml sql-server-2008 xquery-sql

我有一个表和xml数据,如下所示:

                IF EXISTS(
                  SELECT table_name FROM information_schema.tables 
                  WHERE table_name = 'JobCandidates')
                DROP TABLE JobCandidates;
                -- Create JobCandidates table 
                CREATE TABLE JobCandidates(
                  CandidateId INT PRIMARY KEY,
                -- Create typed XML column
                  CandidateResume XML 
                    (DOCUMENT HumanResources.HRResumeSchemaCollection) NULL,
                -- Create untyped XML column
                  CandidateRating XML NULL);
                -- Insert data into the typed column
                INSERT INTO JobCandidates (CandidateId, CandidateResume)
                  (SELECT JobCandidateId, [Resume]
                  FROM HumanResources.JobCandidate
                  WHERE JobCandidateId = 1);
                UPDATE JobCandidates
                SET CandidateRating =
                  '<Ratings>       
                    <Rating Ratingtype="unknown">       
                      <AppliedKnowledge>3.0</AppliedKnowledge>       
                      <ToolSkills>3.5</ToolSkills>       
                    </Rating>       
                    <Rating Ratingtype="known">       
                      <Experience>9.5</Experience>       
                      <Education>16.0</Education>       
                      <DbDevelopment>4.5</DbDevelopment>       
                    </Rating>
                      <Rating Ratingtype="unknown">       
                      <AppliedKnowledge>4.0</AppliedKnowledge>       
                      <ToolSkills>4.5</ToolSkills>       
                    </Rating>       
                  </Ratings>';
                SELECT * FROM JobCandidates;

我的要求:假设此表有一千条记录,我需要搜索<Rating Ratingtype="unknown"> <data> </Rating>的xml列并将其替换为<SuperRating Ratingtype="unknown"> <data> </SuperRating>

3 个答案:

答案 0 :(得分:0)

修改

遗憾的是,这并不容易(至少据我所知......)

这将首先收集所有节点&#34;替换&#34;。将插入这些节点以及相应节点的内容。然后删除原始节点......

Mikael Eriksson的致谢:https://stackoverflow.com/a/15682327/5089204

SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RowIndex
      ,ROW_NUMBER() OVER(PARTITION BY CandidateId ORDER BY (SELECT NULL)) AS NodeIndex
      ,CandidateId
INTO #tmpNodesToReplace
FROM #JobCandidates
CROSS APPLY CandidateRating.nodes('/Ratings/Rating[@Ratingtype="unknown"]') AS A(B);

DECLARE @I INT=1;
DECLARE @CountNodes INT=(SELECT COUNT(*) FROM #tmpNodesToReplace);
WHILE 1 = 1
BEGIN
    UPDATE #JobCandidates
    SET CandidateRating.modify('insert <SuperRating>{(/Ratings/Rating[@Ratingtype="unknown"])[sql:column("NodeIndex")]/*}</SuperRating> into (/Ratings)[1]')
    FROM #JobCandidates
    INNER JOIN #tmpNodesToReplace ON #JobCandidates.CandidateId = #tmpNodesToReplace.CandidateId
    WHERE #tmpNodesToReplace.RowIndex=@I;

  IF @I>@CountNodes
    BREAK;

  SET @I = @I + 1
END

UPDATE #JobCandidates
SET CandidateRating.modify('delete /Ratings/Rating[@Ratingtype="unknown"]');

我简化了您的示例,向您展示了一般方法。请通过添加与我的示例的XML部分对应的预期输出来解释您要修改的内容:

CREATE TABLE #JobCandidates(
    CandidateId INT PRIMARY KEY,
    CandidateRating XML NULL);

INSERT INTO #JobCandidates (CandidateId, CandidateRating) VALUES
 (1 -- test row with two "unknown" Ratings
  ,'<Ratings>       
    <Rating Ratingtype="unknown">       
        <AppliedKnowledge>3.0</AppliedKnowledge>       
        <ToolSkills>3.5</ToolSkills>       
    </Rating>       
    <Rating Ratingtype="known">       
        <Experience>9.5</Experience>       
        <Education>16.0</Education>       
        <DbDevelopment>4.5</DbDevelopment>       
    </Rating>
        <Rating Ratingtype="unknown">       
        <AppliedKnowledge>4.0</AppliedKnowledge>       
        <ToolSkills>4.5</ToolSkills>       
    </Rating>       
    </Ratings>')
, (2 --test row with one "unknown" Rating
  ,'<Ratings>       
    <Rating Ratingtype="other than unknown">       
        <AppliedKnowledge>3.0</AppliedKnowledge>       
        <ToolSkills>3.5</ToolSkills>       
    </Rating>       
    <Rating Ratingtype="known">       
        <Experience>9.5</Experience>       
        <Education>16.0</Education>       
        <DbDevelopment>4.5</DbDevelopment>       
    </Rating>
        <Rating Ratingtype="unknown">       
        <AppliedKnowledge>4.0</AppliedKnowledge>       
        <ToolSkills>4.5</ToolSkills>       
    </Rating>       
    </Ratings>')
, (3 --test row with n "unknown" Rating
  ,'<Ratings>       
    <Rating Ratingtype="other than unknown">       
        <AppliedKnowledge>3.0</AppliedKnowledge>       
        <ToolSkills>3.5</ToolSkills>       
    </Rating>       
    <Rating Ratingtype="known">       
        <Experience>9.5</Experience>       
        <Education>16.0</Education>       
        <DbDevelopment>4.5</DbDevelopment>       
    </Rating>
        <Rating Ratingtype="other than unknown">       
        <AppliedKnowledge>4.0</AppliedKnowledge>       
        <ToolSkills>4.5</ToolSkills>       
    </Rating>       
    </Ratings>');

--Find rows with any "unknown" ratings
SELECT * FROM #JobCandidates
WHERE CandidateRating.exist('/Ratings/Rating[@Ratingtype="unknown"]')=1;

--Find all rows with "unknown" (ID=1 showing 2 entries
SELECT CandidateId
      ,A.B.query('.') 
FROM #JobCandidates
CROSS APPLY CandidateRating.nodes('/Ratings/Rating[@Ratingtype="unknown"]') AS A(B);

DROP TABLE #JobCandidates;

答案 1 :(得分:0)

@ shnugo..thnks输入。这是我最终完成所需工作的脚本。不确定是否有比这更简单的方法

            CREATE TABLE #JobCandidates(
                CandidateId INT PRIMARY KEY,
                CandidateRating XML NULL);

            INSERT INTO #JobCandidates (CandidateId, CandidateRating) VALUES
             (1 -- test row with two "unknown" Ratings
              ,'<Ratings>       
                <Rating Ratingtype="unknown">       
                    <AppliedKnowledge>3.0</AppliedKnowledge>       
                    <ToolSkills>3.5</ToolSkills>       
                </Rating>       
                <Rating Ratingtype="known">       
                    <Experience>9.5</Experience>       
                    <Education>16.0</Education>       
                    <DbDevelopment>4.5</DbDevelopment>       
                </Rating>
                    <Rating Ratingtype="unknown">       
                    <AppliedKnowledge>4.0</AppliedKnowledge>       
                    <ToolSkills>4.5</ToolSkills>       
                </Rating>       
                </Ratings>')
            , (2 --test row with one "unknown" Rating
              ,'<Ratings>       
                <Rating Ratingtype="other than unknown">       
                    <AppliedKnowledge>3.0</AppliedKnowledge>       
                    <ToolSkills>3.5</ToolSkills>       
                </Rating>       
                <Rating Ratingtype="known">       
                    <Experience>9.5</Experience>       
                    <Education>16.0</Education>       
                    <DbDevelopment>4.5</DbDevelopment>       
                </Rating>
                    <Rating Ratingtype="unknown">       
                    <AppliedKnowledge>4.0</AppliedKnowledge>       
                    <ToolSkills>4.5</ToolSkills>       
                </Rating>       
                </Ratings>')
            , (3 --test row with n "unknown" Rating
              ,'<Ratings>       
                <Rating Ratingtype="other than unknown">       
                    <AppliedKnowledge>3.0</AppliedKnowledge>       
                    <ToolSkills>3.5</ToolSkills>       
                </Rating>       
                <Rating Ratingtype="known">       
                    <Experience>9.5</Experience>       
                    <Education>16.0</Education>       
                    <DbDevelopment>4.5</DbDevelopment>       
                </Rating>
                    <Rating Ratingtype="other than unknown">       
                    <AppliedKnowledge>4.0</AppliedKnowledge>       
                    <ToolSkills>4.5</ToolSkills>       
                </Rating>       
                </Ratings>');

            --Find rows with any "unknown" ratings
            SELECT * FROM #JobCandidates
            WHERE CandidateRating.exist('/Ratings/Rating[@Ratingtype="unknown"]')=1;

            --Find all rows with "unknown" (ID=1 showing 2 entries
            SELECT CandidateId
                  ,A.B.query('.') 
            FROM #JobCandidates
            CROSS APPLY CandidateRating.nodes('/Ratings/Rating[@Ratingtype="unknown"]') AS A(B);

            --change '<Rating Ratingtype="unknown">' to '<SuperRating Ratingtype="unknown">
            SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RowIndex
                  ,ROW_NUMBER() OVER(PARTITION BY CandidateId ORDER BY (SELECT NULL)) AS NodeIndex
                  ,CandidateId
            INTO #tmpNodesToReplace
            FROM #JobCandidates
            CROSS APPLY CandidateRating.nodes('/Ratings/Rating[@Ratingtype="unknown"]') AS A(B);

            DECLARE @I INT=1;
            DECLARE @CountNodes INT=(SELECT COUNT(*) FROM #tmpNodesToReplace);
            WHILE 1 = 1
            BEGIN
             UPDATE #JobCandidates
                SET CandidateRating.modify('insert <SuperRating Ratingtype="unknown">{(/Ratings/Rating[@Ratingtype="unknown"])[sql:column("NodeIndex")]/*}</SuperRating> before (//Rating)[1]')
                FROM #JobCandidates
                INNER JOIN #tmpNodesToReplace ON #JobCandidates.CandidateId = #tmpNodesToReplace.CandidateId
                WHERE #tmpNodesToReplace.RowIndex=@I;



                update #JobCandidates set
                CandidateRating.modify('replace value of (/Ratings/Rating/@Ratingtype)[1] with "delete"')
                FROM #JobCandidates INNER JOIN #tmpNodesToReplace ON #JobCandidates.CandidateId = #tmpNodesToReplace.CandidateId
                WHERE #tmpNodesToReplace.RowIndex=@I;

                UPDATE #JobCandidates
                SET CandidateRating.modify('delete /Ratings/Rating[@Ratingtype="delete"]') FROM #JobCandidates where CandidateRating is not null ;

              IF @I>@CountNodes
                BREAK;

              SET @I = @I + 1
            END

            --drop table #JobCandidates
            --drop table #tmpNodesToReplace
            --select * from #JobCandidates

答案 2 :(得分:0)

只需在新的查询窗口中复制并执行。

没有简单的事情......您想要替换的节点被带入临时表。在WHILE循环中,它们作为新节点插入,同时删除旧节点。 XML-DML一次不允许多个操作...

CREATE TABLE #JobCandidates(
    CandidateId INT PRIMARY KEY,
    CandidateRating XML NULL);

INSERT INTO #JobCandidates (CandidateId, CandidateRating) VALUES
 (1 -- test row with two "unknown" Ratings
  ,'<Ratings>
  <Rating Ratingtype="unknown">
    <AppliedKnowledge>3.0</AppliedKnowledge>
    <ToolSkills>3.5</ToolSkills>
  </Rating>
  <Rating Ratingtype="known">
    <Experience>9.5</Experience>
    <Education>16.0</Education>
    <DbDevelopment>4.5</DbDevelopment>
  </Rating>
  <Rating Ratingtype="unknown">
    <AppliedKnowledge>4.0</AppliedKnowledge>
    <ToolSkills>4.5</ToolSkills>
  </Rating>
</Ratings>');

SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RowIndex
      ,ROW_NUMBER() OVER(PARTITION BY CandidateId ORDER BY (SELECT NULL)) AS NodeIndex
      ,CandidateId
INTO #tmpNodesToReplace
FROM #JobCandidates
CROSS APPLY CandidateRating.nodes('/Ratings/Rating[@Ratingtype="unknown"]') AS A(B);

DECLARE @I INT=1;
DECLARE @CountNodes INT=(SELECT COUNT(*) FROM #tmpNodesToReplace);
WHILE 1 = 1
BEGIN
    UPDATE #JobCandidates
    SET CandidateRating.modify('insert <SuperRating>{(/Ratings/Rating[@Ratingtype="unknown"])[sql:column("NodeIndex")]/*}</SuperRating> into (/Ratings)[1]')
    FROM #JobCandidates
    INNER JOIN #tmpNodesToReplace ON #JobCandidates.CandidateId = #tmpNodesToReplace.CandidateId
    WHERE #tmpNodesToReplace.RowIndex=@I;

  IF @I>@CountNodes
    BREAK;

  SET @I = @I + 1
END

UPDATE #JobCandidates
SET CandidateRating.modify('delete /Ratings/Rating[@Ratingtype="unknown"]');

SELECT * FROM #JobCandidates;

/* Both "Unknown" Ratings are replaced
<Ratings>
  <Rating Ratingtype="known">
    <Experience>9.5</Experience>
    <Education>16.0</Education>
    <DbDevelopment>4.5</DbDevelopment>
  </Rating>
  <SuperRating>
    <AppliedKnowledge>3.0</AppliedKnowledge>
    <ToolSkills>3.5</ToolSkills>
  </SuperRating>
  <SuperRating>
    <AppliedKnowledge>4.0</AppliedKnowledge>
    <ToolSkills>4.5</ToolSkills>
  </SuperRating>
</Ratings>
*/

DROP TABLE #tmpNodesToReplace;
DROP TABLE #JobCandidates;