2012-09-19 56 views
1

我有一個存儲過程,它需要「調查」對象的集合並將它們分解到其相應的表中。緩慢的XML在SQL中粉碎

「Survey」對象有兩個子對象「腐蝕」和「跨度」。

這裏是XML的一個例子,我可以使用:

<Surveys> 
    <Survey> 
    <SurveyID>35</SurveyID> 
    <CulvertID>5</CulvertID> 
    <PRNo>587180</PRNo> 
    <MP>5.243</MP> 
    <RefMiles>1.500</RefMiles> 
    <PtID>ABCGEFGH</PtID> 
    <FWVersion>10</FWVersion> 
    <SurveyDate>2012-08-27</SurveyDate> 
    <FutureComments>7c36fe43-78cb-436e-81cf-f416aa63c8fc</FutureComments> 
    <Erosions> 
     <Erosion> 
     <ID>160</ID> 
     <SurveyID>35</SurveyID> 
     <Location>Test Erosion - 8/27/2012 - 1:19:04 PM</Location> 
     </Erosion> 
     <Erosion> 
     <ID>161</ID> 
     <SurveyID>35</SurveyID> 
     <Location>Test Erosion - 8/27/2012 - 1:19:04 PM</Location> 
     </Erosion> 
    </Erosions> 
    <Spans> 
     <Span> 
     <ID>88</ID> 
     <SurveyID>35</SurveyID> 
     <Material>Test Span - 8/27/2012 - 1:19:04 PM</Material> 
     </Span> 
     <Span> 
     <ID>89</ID> 
     <SurveyID>35</SurveyID> 
     <Material>Test Span - 8/27/2012 - 1:19:04 PM</Material> 
     </Span> 
    </Spans> 
    </Survey> 
</Surveys> 

我已上載的存儲過程定義到以下位置(太大了,貼在這裏)。 Download SQL

當我發送上面提供的XML樣本時,該過程需要大約10秒才能完成,而且只有一個非常小的XML字符串。更新/插入包含任何「VARBINARY(MAX)」列中的值的記錄可能需要幾分鐘的時間。

我確實是一名Xquery新手...不知道我做錯了什麼,但是表現絕對糟糕。有什麼建議麼?

隨溶液編輯:

繼@wBob下面的建議,我已經想出了this SQL我的存儲過程。我最終重新定義了我的桌子上的PK更「準確」。它不是生成的int ID,而是基於實際的數據字段。這使我可以使用merge來插入使用由實際數據值定義的外鍵的子對象,而不是必須檢索的生成數字。

+0

您使用的是什麼SQL系統。無論你的意思是「碎片」? – bmargulies

+0

@bmargulies MS SQL 2008.通過分解我的意思是採用對象的XML表示形式,並將其拆分爲用於定義該對象的各種表(包括任何子對象)。 –

+0

如果性能是一個問題,特別是在處理大型XML數據時,您應該使用流式API,如SAX而不是創建對象。有可能是一個MS SQL的工具,將爲你做這個映射(我似乎記得有一些漂亮的上傳工具)。 –

回答

3

我已經去改變你的(相當大)proc。請記住,這是用於演示原理的示例腳本(例如,帶有OUTPUT子句的MERGE),並由您來完成並測試它。例如,你需要測試所有列被正確插入/更新,檢查你的數據類型,以及在哪裏刪除了NULLIF等。我還會添加TRY/CATCH錯誤和事務處理。請注意,我也根據surveyId完成了MERGE - 不確定這是否正確?

如果我是你,我會花一些時間熟悉MERGEOUTPUT條款之前採取這一點,但祝你好運,讓我們知道你如何繼續。

我還附加了link到我的完整測試裝備。這個腳本是安全的,並且可以在tempdb中完全重新運行。

ALTER PROCEDURE [dbo].[sp_SaveSurvey] 
    @XMLobject XML 
AS 
BEGIN 

    SET NOCOUNT ON 

    CREATE TABLE #tmp_survey (surveyId INT PRIMARY KEY, erosions XML, spans XML) 

    BEGIN TRAN 

------------------------------------------------------------------------------------------------ 
-- MERGE dbo.CULV_StreamCrossingSurvey START 
------------------------------------------------------------------------------------------------ 

    SET IDENTITY_INSERT dbo.CULV_StreamCrossingSurvey ON 

    ;MERGE dbo.CULV_StreamCrossingSurvey AS target 
    USING 
    (
    SELECT 
     CAST(NULLIF(X.survey.value('(SurveyID/text())[1]', 'VARCHAR(200)'), '') AS INT) AS SurveyID, 
     CAST(NULLIF(X.survey.value('(CulvertID/text())[1]', 'VARCHAR(200)'), '') AS INT) AS CulvertID, 
     NULLIF(X.survey.value('(County/text())[1]', 'VARCHAR(50)'), '') AS County, 
     NULLIF(X.survey.value('(RoadName/text())[1]', 'VARCHAR(50)'), '') AS RoadName, 
     CAST(NULLIF(X.survey.value('(PRNo/text())[1]', 'VARCHAR(50)'), '') AS INT) AS PRNo, 
     CAST(NULLIF(X.survey.value('(MP/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,3)) AS MP, 
     CAST(NULLIF(X.survey.value('(RefMiles/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,3)) AS RefMiles, 
     NULLIF(X.survey.value('(PtID/text())[1]', 'VARCHAR(8)'), '') AS PtID, 
     CAST(NULLIF(X.survey.value('(FWVersion/text())[1]', 'VARCHAR(50)'), '') AS SMALLINT) AS FWVersion, 
     CAST(NULLIF(X.survey.value('(Latitude/text())[1]', 'VARCHAR(200)'), '') AS FLOAT) AS Latitude, 
     CAST(NULLIF(X.survey.value('(Longitude/text())[1]', 'VARCHAR(200)'), '') AS FLOAT) AS Longitude, 
     NULLIF(X.survey.value('(Waterway/text())[1]', 'VARCHAR(50)'), '') AS Waterway, 
     NULLIF(X.survey.value('(SiteID/text())[1]', 'VARCHAR(50)'), '') AS SiteID, 
     NULLIF(X.survey.value('(Observers/text())[1]', 'VARCHAR(50)'), '') AS Observers, 
     CAST(NULLIF(X.survey.value('(SurveyDate/text())[1]', 'VARCHAR(200)'), '') AS DATE) AS SurveyDate, 
     NULLIF(X.survey.value('(AdjacentLandowner/text())[1]', 'VARCHAR(MAX)'), '') AS AdjacentLandowner, 
     NULLIF(X.survey.value('(LocationComments/text())[1]', 'VARCHAR(MAX)'), '') AS LocationComments, 
     CAST(NULLIF(X.survey.value('(CrossingType/text())[1]', 'VARCHAR(50)'), '') AS SMALLINT) AS CrossingType, 
     NULLIF(X.survey.value('(StructureShape/text())[1]', 'VARCHAR(50)'), '') AS StructureShape, 
     NULLIF(X.survey.value('(InletStructure/text())[1]', 'VARCHAR(50)'), '') AS InletStructure, 
     NULLIF(X.survey.value('(OutletStructure/text())[1]', 'VARCHAR(50)'), '') AS OutletStructure, 
     NULLIF(X.survey.value('(OutletType/text())[1]', 'VARCHAR(50)'), '') AS OutletType, 
     NULLIF(X.survey.value('(SubstrateInStructure/text())[1]', 'VARCHAR(50)'), '') AS SubstrateInStructure, 
     NULLIF(X.survey.value('(GeneralCondition/text())[1]', 'VARCHAR(50)'), '') AS GeneralCondition, 
     CAST(NULLIF(X.survey.value('(PluggedPercent/text())[1]', 'VARCHAR(MAX)'), '') AS SMALLINT) AS PluggedPercent, 
     NULLIF(X.survey.value('(PluggedLocation/text())[1]', 'VARCHAR(50)'), '') AS PluggedLocation, 
     CAST(NULLIF(X.survey.value('(CrushedPercent/text())[1]', 'VARCHAR(50)'), '') AS SMALLINT) AS CrushedPercent, 
     NULLIF(X.survey.value('(CrushedLocation/text())[1]', 'VARCHAR(50)'), '') AS CrushedLocation, 
     CAST(NULLIF(X.survey.value('(IsRustedThrough/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsRustedThrough, 
     NULLIF(X.survey.value('(StructureInterior/text())[1]', 'VARCHAR(50)'), '') AS StructureInterior, 
     CAST(NULLIF(X.survey.value('(StructureWaterDepthInlet/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS StructureWaterDepthInlet, 
     CAST(NULLIF(X.survey.value('(StructureWaterDepthOutlet/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS StructureWaterDepthOutlet, 
     CAST(NULLIF(X.survey.value('(StructureEmbeddedDepthInlet/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS StructureEmbeddedDepthInlet, 
     CAST(NULLIF(X.survey.value('(StructureEmbeddedDepthOutlet/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS StructureEmbeddedDepthOutlet, 
     CAST(NULLIF(X.survey.value('(StructureWaterVelocityInlet/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS StructureWaterVelocityInlet, 
     CAST(NULLIF(X.survey.value('(StructureWaterVelocityOutlet/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS StructureWaterVelocityOutlet, 
     CAST(NULLIF(X.survey.value('(StructureWaterVelocityMeasured/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS StructureWaterVelocityMeasured, 
     NULLIF(X.survey.value('(StructureWaterVelocityMeasuredWith/text())[1]', 'VARCHAR(50)'), '') AS StructureWaterVelocityMeasuredWith, 
     CAST(NULLIF(X.survey.value('(IsPerched/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsPerched, 
     CAST(NULLIF(X.survey.value('(PerchHeight/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS PerchHeight, 
     NULLIF(X.survey.value('(StreamFlow/text())[1]', 'VARCHAR(50)'), '') AS StreamFlow, 
     CAST(NULLIF(X.survey.value('(IsScourPoolPresent/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsScourPoolPresent, 
     CAST(NULLIF(X.survey.value('(ScourPoolLength/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS ScourPoolLength, 
     CAST(NULLIF(X.survey.value('(ScourPoolWidth/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS ScourPoolWidth, 
     CAST(NULLIF(X.survey.value('(ScourPoolDepth/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS ScourPoolDepth, 
     CAST(NULLIF(X.survey.value('(IsUpstreamPondPresent/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsUpstreamPondPresent, 
     CAST(NULLIF(X.survey.value('(UpstreamPondLength/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS UpstreamPondLength, 
     CAST(NULLIF(X.survey.value('(UpstreamPondWidth/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS UpstreamPondWidth, 
     CAST(NULLIF(X.survey.value('(RiffleWaterDepth/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS RiffleWaterDepth, 
     CAST(NULLIF(X.survey.value('(RiffleBankfullWidth/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS RiffleBankfullWidth, 
     CAST(NULLIF(X.survey.value('(RiffleWettedWidth/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS RiffleWettedWidth, 
     CAST(NULLIF(X.survey.value('(RiffleWaterVelocity/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS RiffleWaterVelocity, 
     NULLIF(X.survey.value('(RiffleMeasuredWith/text())[1]', 'VARCHAR(50)'), '') AS RiffleMeasuredWith, 
     NULLIF(X.survey.value('(RiffleSubstrate/text())[1]', 'VARCHAR(50)'), '') AS RiffleSubstrate, 
     NULLIF(X.survey.value('(RoadSurface/text())[1]', 'VARCHAR(50)'), '') AS RoadSurface, 
     NULLIF(X.survey.value('(RoadCondition/text())[1]', 'VARCHAR(50)'), '') AS RoadCondition, 
     CAST(NULLIF(X.survey.value('(RoadWidth/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS RoadWidth, 
     NULLIF(X.survey.value('(LocationOfLowPoint/text())[1]', 'VARCHAR(50)'), '') AS LocationOfLowPoint, 
     NULLIF(X.survey.value('(RunOffPath/text())[1]', 'VARCHAR(50)'), '') AS RunOffPath, 
     CAST(NULLIF(X.survey.value('(FillDepthUpStream/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS FillDepthUpStream, 
     CAST(NULLIF(X.survey.value('(FillDepthDownStream/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS FillDepthDownStream, 
     NULLIF(X.survey.value('(SlopeUpStream/text())[1]', 'VARCHAR(50)'), '') AS SlopeUpStream, 
     NULLIF(X.survey.value('(SlopeDownStream/text())[1]', 'VARCHAR(50)'), '') AS SlopeDownStream, 
     CAST(NULLIF(X.survey.value('(ApproachLengthLeft/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS ApproachLengthLeft, 
     CAST(NULLIF(X.survey.value('(ApproachLengthRight/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(6,1)) AS ApproachLengthRight, 
     NULLIF(X.survey.value('(ApproachSlopeLeft/text())[1]', 'VARCHAR(50)'), '') AS ApproachSlopeLeft, 
     NULLIF(X.survey.value('(ApproachSlopeRight/text())[1]', 'VARCHAR(50)'), '') AS ApproachSlopeRight, 
     NULLIF(X.survey.value('(VegetationDitchLeft/text())[1]', 'VARCHAR(50)'), '') AS VegetationDitchLeft, 
     NULLIF(X.survey.value('(VegetationDitchRight/text())[1]', 'VARCHAR(50)'), '') AS VegetationDitchRight, 
     CAST(NULLIF(X.survey.value('(IsErosionPresent/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsErosionPresent, 
     CAST(NULLIF(X.survey.value('(IsErosionCorrectable/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsErosionCorrectable, 
     NULLIF(X.survey.value('(ErosionExtent/text())[1]', 'VARCHAR(50)'), '') AS ErosionExtent, 
     NULLIF(X.survey.value('(ErosionNotes/text())[1]', 'VARCHAR(MAX)'), '') AS ErosionNotes, 
     CAST(NULLIF(X.survey.value('(IsPrioritySite/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsPrioritySite, 
     NULLIF(X.survey.value('(PriorityReason/text())[1]', 'VARCHAR(50)'), '') AS PriorityReason, 
     NULLIF(X.survey.value('(PriorityComments/text())[1]', 'VARCHAR(MAX)'), '') AS PriorityComments, 
     CAST(NULLIF(X.survey.value('(FutureVisit/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS FutureVisit, 
     NULLIF(X.survey.value('(FutureComments/text())[1]', 'VARCHAR(MAX)'), '') AS FutureComments, 
     CAST(NULLIF(X.survey.value('(NonNativeInvasiveSpecies/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS NonNativeInvasiveSpecies, 
     NULLIF(X.survey.value('(SpeciesObserved/text())[1]', 'VARCHAR(MAX)'), '') AS SpeciesObserved, 
     CAST(NULLIF(X.survey.value('(IsHeadChanged/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsHeadChanged, 
     NULLIF(X.survey.value('(HeadChangeComments/text())[1]', 'VARCHAR(MAX)'), '') AS HeadChangeComments, 
     CAST(NULLIF(X.survey.value('(IsBackwatered/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsBackwatered, 
     NULLIF(X.survey.value('(BackwaterComments/text())[1]', 'VARCHAR(MAX)'), '') AS BackwaterComments, 
     CAST(NULLIF(X.survey.value('(IsOvertopping/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsOvertopping, 
     NULLIF(X.survey.value('(OvertoppingComments/text())[1]', 'VARCHAR(MAX)'), '') AS OvertoppingComments, 
     CAST(NULLIF(X.survey.value('(IsSubstrateEntireLength/text())[1]', 'VARCHAR(10)'), '') AS BIT) AS IsSubstrateEntireLength, 
     CAST(NULLIF(X.survey.value('(CalculatedPassability/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(4,1)) AS CalculatedPassability, 
     CAST(NULLIF(X.survey.value('(DefinedPassability/text())[1]', 'VARCHAR(50)'), '') AS NUMERIC(4,1)) AS DefinedPassability, 
     NULLIF(X.survey.value('(PassabilityComments/text())[1]', 'VARCHAR(MAX)'), '') AS PassabilityComments, 
     NULLIF(X.survey.value('(PhotoInlet/text())[1]', 'VARBINARY(MAX)'), '') AS PhotoInlet, 
     NULLIF(X.survey.value('(PhotoInletFileName/text())[1]', 'VARCHAR(200)'), '') AS PhotoInletFileName, 
     NULLIF(X.survey.value('(PhotoOutlet/text())[1]', 'VARBINARY(MAX)'), '') AS PhotoOutlet, 
     NULLIF(X.survey.value('(PhotoOutletFileName/text())[1]', 'VARCHAR(200)'), '') AS PhotoOutletFileName, 
     NULLIF(X.survey.value('(PhotoUpstream/text())[1]', 'VARBINARY(MAX)'), '') AS PhotoUpstream, 
     NULLIF(X.survey.value('(PhotoUpstreamFileName/text())[1]', 'VARCHAR(200)'), '') AS PhotoUpstreamFileName, 
     NULLIF(X.survey.value('(PhotoDownstream/text())[1]', 'VARBINARY(MAX)'), '') AS PhotoDownstream, 
     NULLIF(X.survey.value('(PhotoDownstreamFileName/text())[1]', 'VARCHAR(200)'), '') AS PhotoDownstreamFileName, 
     NULLIF(X.survey.value('(PhotoRoadApproachLeft/text())[1]', 'VARBINARY(MAX)'), '') AS PhotoRoadApproachLeft, 
     NULLIF(X.survey.value('(PhotoRoadApproachLeftFileName/text())[1]', 'VARCHAR(200)'), '') AS PhotoRoadApproachLeftFileName, 
     NULLIF(X.survey.value('(PhotoRoadApproachRight/text())[1]', 'VARBINARY(MAX)'), '') AS PhotoRoadApproachRight, 
     NULLIF(X.survey.value('(PhotoRoadApproachRightFileName/text())[1]', 'VARCHAR(200)'), '') AS PhotoRoadApproachRightFileName, 
     Erosions = (CASE WHEN CAST(X.survey.query('Erosions') AS VARCHAR(MAX)) = '' THEN NULL 
     ELSE X.survey.query('Erosions') END), 
     Spans = (CASE WHEN CAST(X.survey.query('Spans') AS VARCHAR(MAX)) = '' THEN NULL 
     ELSE X.survey.query('Spans') END) 
    FROM @XMLobject.nodes('Surveys/Survey') AS X(survey) 
    ) AS source ON source.SurveyId = target.SurveyId 

    WHEN NOT MATCHED BY TARGET 
     THEN 
     INSERT (surveyId, CulvertID, County, RoadName, PRNo, MP, RefMiles, PtID, FWVersion, Latitude, Longitude, Waterway, SiteID, Observers, SurveyDate, AdjacentLandowner, LocationComments, CrossingType, StructureShape, InletStructure, OutletStructure, OutletType, SubstrateInStructure, GeneralCondition, PluggedPercent, PluggedLocation, CrushedPercent, CrushedLocation, IsRustedThrough, StructureInterior, StructureWaterDepthInlet, StructureWaterDepthOutlet, StructureEmbeddedDepthInlet, StructureEmbeddedDepthOutlet, StructureWaterVelocityInlet, StructureWaterVelocityOutlet, StructureWaterVelocityMeasured, StructureWaterVelocityMeasuredWith, IsPerched, PerchHeight, StreamFlow, IsScourPoolPresent, ScourPoolLength, ScourPoolWidth, ScourPoolDepth, IsUpstreamPondPresent, UpstreamPondLength, UpstreamPondWidth, RiffleWaterDepth, RiffleBankfullWidth, RiffleWettedWidth, RiffleWaterVelocity, RiffleMeasuredWith, RiffleSubstrate, RoadSurface, RoadCondition, RoadWidth, LocationOfLowPoint, RunOffPath, FillDepthUpStream, FillDepthDownStream, SlopeUpStream, SlopeDownStream, ApproachLengthLeft, ApproachLengthRight, ApproachSlopeLeft, ApproachSlopeRight, VegetationDitchLeft, VegetationDitchRight, IsErosionPresent, IsErosionCorrectable, ErosionExtent, ErosionNotes, IsPrioritySite, PriorityReason, PriorityComments, FutureVisit, FutureComments, NonNativeInvasiveSpecies, SpeciesObserved, IsHeadChanged, HeadChangeComments, IsBackwatered, BackwaterComments, IsOvertopping, OvertoppingComments, IsSubstrateEntireLength, CalculatedPassability, DefinedPassability, PassabilityComments, PhotoInlet, PhotoInletFileName, PhotoOutlet, PhotoOutletFileName, PhotoUpstream, PhotoUpstreamFileName, PhotoDownstream, PhotoDownstreamFileName, PhotoRoadApproachLeft, PhotoRoadApproachLeftFileName, PhotoRoadApproachRight, PhotoRoadApproachRightFileName) 
     VALUES (surveyId, CulvertID, County, RoadName, PRNo, MP, RefMiles, PtID, FWVersion, Latitude, Longitude, Waterway, SiteID, Observers, SurveyDate, AdjacentLandowner, LocationComments, CrossingType, StructureShape, InletStructure, OutletStructure, OutletType, SubstrateInStructure, GeneralCondition, PluggedPercent, PluggedLocation, CrushedPercent, CrushedLocation, IsRustedThrough, StructureInterior, StructureWaterDepthInlet, StructureWaterDepthOutlet, StructureEmbeddedDepthInlet, StructureEmbeddedDepthOutlet, StructureWaterVelocityInlet, StructureWaterVelocityOutlet, StructureWaterVelocityMeasured, StructureWaterVelocityMeasuredWith, IsPerched, PerchHeight, StreamFlow, IsScourPoolPresent, ScourPoolLength, ScourPoolWidth, ScourPoolDepth, IsUpstreamPondPresent, UpstreamPondLength, UpstreamPondWidth, RiffleWaterDepth, RiffleBankfullWidth, RiffleWettedWidth, RiffleWaterVelocity, RiffleMeasuredWith, RiffleSubstrate, RoadSurface, RoadCondition, RoadWidth, LocationOfLowPoint, RunOffPath, FillDepthUpStream, FillDepthDownStream, SlopeUpStream, SlopeDownStream, ApproachLengthLeft, ApproachLengthRight, ApproachSlopeLeft, ApproachSlopeRight, VegetationDitchLeft, VegetationDitchRight, IsErosionPresent, IsErosionCorrectable, ErosionExtent, ErosionNotes, IsPrioritySite, PriorityReason, PriorityComments, FutureVisit, FutureComments, NonNativeInvasiveSpecies, SpeciesObserved, IsHeadChanged, HeadChangeComments, IsBackwatered, BackwaterComments, IsOvertopping, OvertoppingComments, IsSubstrateEntireLength, CalculatedPassability, DefinedPassability, PassabilityComments, PhotoInlet, PhotoInletFileName, PhotoOutlet, PhotoOutletFileName, PhotoUpstream, PhotoUpstreamFileName, PhotoDownstream, PhotoDownstreamFileName, PhotoRoadApproachLeft, PhotoRoadApproachLeftFileName, PhotoRoadApproachRight, PhotoRoadApproachRightFileName) 

    WHEN MATCHED 
     THEN UPDATE 
      SET target.CulvertID = source.CulvertID 

    -- DELETE section 
    --WHEN NOT MATCHED BY SOURCE 
    -- THEN DELETE 

    OUTPUT source.surveyId, source.Erosions, source.Spans INTO #tmp_survey 
    --OUTPUT $action 
    ; 


-- MERGE dbo.CULV_StreamCrossingSurvey END 
------------------------------------------------------------------------------------------------ 



------------------------------------------------------------------------------------------------ 
-- DELETEs START 
-- Remove existing erosion and span records 
------------------------------------------------------------------------------------------------ 

DELETE e 
FROM dbo.CULV_StreamCrossingErosion e 
    INNER JOIN #tmp_survey s ON e.SurveyID = s.surveyId 

DELETE e 
FROM dbo.Culvert_StreamCrossingSpan e 
    INNER JOIN #tmp_survey s ON e.SurveyID = s.surveyId 

-- DELETEs END 
------------------------------------------------------------------------------------------------ 



------------------------------------------------------------------------------------------------ 
-- MERGE dbo.CULV_StreamCrossingErosion START 
------------------------------------------------------------------------------------------------ 

;MERGE dbo.CULV_StreamCrossingErosion AS target 
USING 
(
    SELECT 
      t.surveyId 
     , NULLIF(E.erosion.value('(Location/text())[1]', 'VARCHAR(50)'), '') AS Location 
     , E.erosion.value('(Length/text())[1]', 'NUMERIC(6,1)') AS Length 
     , E.erosion.value('(Width/text())[1]', 'NUMERIC(6,1)') AS Width 
     , E.erosion.value('(Depth/text())[1]', 'NUMERIC(6,1)') AS Depth 
     , E.erosion.value('(IsReachingStream/text())[1]', 'BIT') AS IsReachingStream 
     , NULLIF(E.erosion.value('(MaterialEroded/text())[1]', 'VARCHAR(50)'), '') AS MaterialEroded 
    FROM #tmp_survey t 
     CROSS APPLY erosions.nodes('Erosions/Erosion') AS E(erosion) 
) AS source ON source.SurveyId = target.SurveyId 

WHEN NOT MATCHED BY TARGET 
    THEN 
    INSERT (surveyId, Location, Length, Width, Depth, IsReachingStream, MaterialEroded) 
    VALUES (surveyId, Location, Length, Width, Depth, IsReachingStream, MaterialEroded) 

WHEN MATCHED 
    THEN UPDATE 
     SET 
     target.Location = source.Location, 
     target.Length = source.Length, 
     target.Width = source.Width, 
     target.Depth = source.Depth, 
     target.IsReachingStream = source.IsReachingStream, 
     target.MaterialEroded = source.MaterialEroded 
; 

;MERGE dbo.Culvert_StreamCrossingSpan AS target 
USING 
(
    SELECT 
     t.surveyId, 
     S.span.value('(SpanNumber/text())[1]', 'SMALLINT') AS SpanNumber, 
     S.span.value('(Length/text())[1]', 'NUMERIC(6,1)') AS Length, 
     S.span.value('(Width/text())[1]', 'NUMERIC(6,1)') AS Width, 
     S.span.value('(Height/text())[1]', 'NUMERIC(6,1)') AS Height, 
     NULLIF(S.span.value('(Material/text())[1]', 'VARCHAR(50)'), '') AS Material 
    FROM #tmp_survey t 
     CROSS APPLY spans.nodes('Spans/Span') AS S(span) 

) AS source ON source.SurveyId = target.SurveyId 

WHEN NOT MATCHED BY TARGET 
    THEN 
    INSERT (surveyID, SpanNumber, Length, Width, Height, Material) 
    VALUES (surveyID, SpanNumber, Length, Width, Height, Material) 

WHEN MATCHED 
    THEN UPDATE 
     SET 
     target.SpanNumber = source.SpanNumber, 
     target.Length = source.Length, 
     target.Width = source.Width, 
     target.Height = source.Height, 
     target.Material = source.Material 
; 

-- MERGE dbo.CULV_StreamCrossingErosion END 
------------------------------------------------------------------------------------------------ 


-- !!TODO Add TRY/CATCH error and transaction handling 

COMMIT 

RETURN 

END 
GO 
+0

希望我能給你這個答案的額外功勞!有趣的是,在你之前的回答之後,我已經在這條路上走了。我一定是在同一時間忙於工作。我將在我的原始問題上發佈我的SQL副本,以便您可以看到我最終做了什麼。 –

+0

很高興這是阿曼達的幫助。你看到什麼樣的性能改進? – wBob

+0

那麼,當我通過一個Survey對象發送一個二進制字段,其中一個填充了大小約爲1.5 MB的文件時,它過去需要超過5分鐘才能執行。現在大約需要1.2秒。當我發送多個文件時,它仍然不到2秒。所以這是一個非常大的改進!感謝你的幫助。 =) –

1

我可以推薦一些更改。首先,你不需要.query和.value。 .query返回XML,所以如果你想要XML,那麼你使用.query。 .value的返回標量值,所以這就是你需要在這裏使用一個,例如

SELECT 
    CAST(NULLIF(X.survey.value('(SurveyID/text())[1]', 'VARCHAR(200)'), '') AS INT) AS SurveyID, 
    CAST(NULLIF(X.survey.value('(CulvertID/text())[1]', 'VARCHAR(200)'), '') AS INT) AS CulvertID, 

此外,添加文本()構造函數和1個序號(如上),這應該是按照here更有效。

其次,我不認爲你需要光標。 .nodes返回一個結果集,以便您可以直接切入臨時表,然後將INSERT/UPDATE/DELETE插入到主表中。如果您使用的是SQL 2008以上版本,則可以使用MERGE,它可以在一個操作中執行所有操作。你甚至可以,如果你感覺很勇敢,MERGE直接從.nodes結果集中進入主表,如果這有意義的話?

如果您沒有從這些更改中獲得所需的性能,那麼可以考慮使用OPENXML,對於較大的XML文檔有時可能會更快。請注意,OPENXML有一個衆所周知的特性,在調用時它可以自動佔用服務器內存的1/8,因此您必須始終記得調用sp_xml_removedocument。

試着做出這些改變,讓我們知道你是如何得到的。

PS您正在使用正確的術語「碎化」,請忽略關於SAX的說明。 SQL Server內置了用於粉碎XML的功能強大且快速的組件。

+0

我已經刪除了.query,並按照您在文章中描述的那樣僅替換了.value。性能大大提高!謝謝!我想看看是否可以進一步提高性能,所以我試圖找到一些顯示您提到的臨時表和合並方法的示例。由於我有三個表,一個父表和兩個子表,並且主鍵在表中生成(XML中未提供),所以我不確定如何繼續。我需要在插入後爲父母提供父母的PK。那有意義嗎? –