亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > 文庫 > 正文

執行Insert Exec時的隱藏開銷 分析

2020-10-29 21:54:11
字體:
來源:轉載
供稿:網友

The Hidden Costs of INSERT EXEC

INSERT and EXEC: relational brothers in arms, helping you get your data and then put it somewhere. But like all brothers, their relationship has its ups and downs and sometimes you need to look just below the surface to see the real issues. In this post I will cover hidden penalties that a decision--seemingly purely aesthetic--can bring to the fore.

To illustrate the first of these examples I will be using the following table:

USE tempdb
GO


CREATE TABLE dbo.MyTable
(
    [SalesOrderID] [int] NOT NULL,
    [SalesOrderDetailID] [int] NOT NULL,
    [CarrierTrackingNumber] [nvarchar](25) NULL,
    [OrderQty] [smallint] NOT NULL,
    [ProductID] [int] NOT NULL,
    [SpecialOfferID] [int] NOT NULL,
    [UnitPrice] [money] NOT NULL,
    [UnitPriceDiscount] [money] NOT NULL,
    [LineTotal] [money] NOT NULL,
    [rowguid] [uniqueidentifier]  NOT NULL,
    [ModifiedDate] [datetime] NOT NULL
)
GO

Consider the following queries, disregarding the fact that this isn't a good dynamic SQL example (I'm trying to keep it simple), and take a moment to think about the differences between them from a behavioral point of view:

EXEC
('
    INSERT dbo.MyTable
    SELECT *
    FROM AdventureWorks.Sales.SalesOrderDetail
')
GO

INSERT dbo.MyTable
EXEC
('
    SELECT *
    FROM AdventureWorks.Sales.SalesOrderDetail
')
GO

It's quite common to insert some data into a table from dynamic SQL, and for as long as I can remember, I've believed the choice between these two forms of the query to be primarily an issue of style. My preference has been for the latter style, as I feel that it's a bit more readable. It also has the benefit of better supporting ownership chains, but that's something that, in most cases, we don't have to worry about any longer in SQL Server 2005 or 2008. But let's move beyond style and potential security issues and get to the substance. What actually happens when we run these two queries?

In the first case--insert done inside of the EXEC--the first step is that the outer statement--the EXEC itself--is parsed and validated. Now we jump down one level of context, into the string that was passed to EXEC, and that string is parsed and validated. The referenced objects are resolved, and the plan cache is checked. If there is no plan, the query is compiled. And then it's executed. Data is streamed from SalesOrderDetail into MyTable and when the query inside of the EXEC is done control returns to the calling context--EXEC--and assuming there were no errors, the EXEC is now completed.

In the second case, something quite a bit different occurs. The insert is parsed and validated, and an INSERT EXEC plan is generated. This plan does not involve SalesOrderDetail, as the query optimizer doesn't know at this point in the process where the data will be inserted from, since that step is dynamic. So the plan references something called the "Parameter Table" (plan truncated for simplicity):

|--Table Insert(OBJECT:([tempdb].[dbo].[MyTable]))
   |--Top(ROWCOUNT est 0)
        |--Parameter Table Scan

Once this plan has been generated, we again jump down one level of context, and the inner string is parsed and validated, the referenced object resolved, the cache checked, and the compiled plan executed. But what is this Parameter Table thing?

Here's where things start really diverging. Data is not, at this point, streamed from SalesOrderDetail directly into MyTable. Rather, it is streamed from SalesOrderDetail into the Parameter Table. And the Parameter Table, as it turns out, is in actuality a hidden temporary table. And not until all of the data has streamed into that hidden temporary table is control resumed by the EXEC context, and only then does the data start moving into its final home.

The natural question you might be asking yourself at this point is, just how much overhead does this Parameter Table introduce into the equation? The answer might just send you racing to check your stored procedures: the additional cost for the Parameter Table is well over 100% as compared with doing the insert inside of the EXEC. The fact that all of the data must be spooled to the Parameter Table before the insert can even begin tells us that this must be true, and it can be verified using a simple check against the sys.dm_exec_requests DMV, as in the following example:

EXEC
('
    INSERT dbo.MyTable
    SELECT *
    FROM AdventureWorks.Sales.SalesOrderDetail
')

SELECT
    r.writes AS [INSERT inside EXEC writes]
FROM sys.dm_exec_requests r
WHERE
    r.session_id = @@SPID
GO

INSERT dbo.MyTable
EXEC
('
    SELECT *
    FROM AdventureWorks.Sales.SalesOrderDetail
')

SELECT
    r.writes AS [INSERT EXEC writes]
FROM sys.dm_exec_requests r
WHERE
    r.session_id = @@SPID
GO

So that's that. We should avoid INSERT EXEC and try to do our inserts in the same context in which the SELECT is running--right?

Well, yes and no. There is another element at play here which I haven't yet mentioned. What if we were only inserting a few rows, and the table we were inserting into looked something like the following:

CREATE TABLE #MyTable
(
    [SalesOrderID] [int] NOT NULL,
    [SalesOrderDetailID] [int] NOT NULL,
    [CarrierTrackingNumber] [nvarchar](25) NULL,
    [OrderQty] [smallint] NOT NULL,
    [ProductID] [int] NOT NULL,
    [SpecialOfferID] [int] NOT NULL,
    [UnitPrice] [money] NOT NULL,
    [UnitPriceDiscount] [money] NOT NULL,
    [LineTotal] [money] NOT NULL,
    [rowguid] [uniqueidentifier]  NOT NULL,
    [ModifiedDate] [datetime] NOT NULL
)
GO

The only difference between this table and the previous one is that this is a temporary table and the other is not. But temporary tables have their own interesting little twists, especially when it comes down to one of the key enemies in a highly-transactional system: recompilation. As it turns out, doing the insert inside the EXEC will cause the internal statement to recompile every time a new temp table is encountered. This means that if you have a stored procedure that creates a temp table, puts together a bit of dynamic SQL, and does an insert inside of that dynamic SQL, you'll now have yourself a recompilation problem.

To see this illustrated, try the following script. Here only a single row is inserted as a result of the dynamic query, but it's complex enough that the compile time more than overshadows the overhead of the Parameter Table:

USE tempdb
GO

DBCC FREEPROCCACHE
GO

CREATE TABLE #AvgTimes
(
    CPU_time DECIMAL(19,4) NOT NULL,
    insert_type VARCHAR(25) NOT NULL
)
GO

CREATE TABLE #MyTable
(
    [SalesOrderID] [int] NOT NULL,
    [SalesOrderDetailID] [int] NOT NULL,
    [CarrierTrackingNumber] [nvarchar](25) NULL,
    [OrderQty] [smallint] NOT NULL,
    [ProductID] [int] NOT NULL,
    [SpecialOfferID] [int] NOT NULL,
    [UnitPrice] [money] NOT NULL,
    [UnitPriceDiscount] [money] NOT NULL,
    [LineTotal] [money] NOT NULL,
    [rowguid] [uniqueidentifier]  NOT NULL,
    [ModifiedDate] [datetime] NOT NULL
)

EXEC
('
    INSERT #MyTable
    SELECT TOP(1)
        sod.*
    FROM AdventureWorks.Sales.SalesOrderDetail sod
    WHERE
        sod.UnitPrice > 10
        AND sod.LineTotal > 100
        AND EXISTS
        (
            SELECT *
            FROM AdventureWorks.Sales.SalesOrderHeader soh
            JOIN AdventureWorks.Person.Contact c ON
                c.ContactID = soh.CustomerID
            WHERE
                soh.SalesOrderID = sod.SalesOrderID
                AND c.LastName LIKE ''Ab%''
        )
        AND EXISTS
        (
            SELECT *
            FROM AdventureWorks.Production.Product p
            WHERE
                p.ProductID = sod.ProductID
                AND p.Color IS NULL
        )
        AND NOT EXISTS
        (
            SELECT *
            FROM AdventureWorks.Sales.SalesOrderHeader soh
            JOIN AdventureWorks.Sales.SalesPerson sp ON
                soh.SalesPersonID = sp.SalesPersonID
            WHERE
                soh.SalesOrderID = sod.SalesOrderID
                AND sp.CommissionPct > 50
        )
')

INSERT #AvgTimes
SELECT
    r.cpu_time,
    'INSERT inside EXEC'
FROM sys.dm_exec_requests r
WHERE
    r.session_id = @@SPID

DROP TABLE #MyTable
GO 5

CREATE TABLE #MyTable
(
    [SalesOrderID] [int] NOT NULL,
    [SalesOrderDetailID] [int] NOT NULL,
    [CarrierTrackingNumber] [nvarchar](25) NULL,
    [OrderQty] [smallint] NOT NULL,
    [ProductID] [int] NOT NULL,
    [SpecialOfferID] [int] NOT NULL,
    [UnitPrice] [money] NOT NULL,
    [UnitPriceDiscount] [money] NOT NULL,
    [LineTotal] [money] NOT NULL,
    [rowguid] [uniqueidentifier]  NOT NULL,
    [ModifiedDate] [datetime] NOT NULL
)

INSERT #MyTable
EXEC
('
    SELECT TOP(1)
        sod.*
    FROM AdventureWorks.Sales.SalesOrderDetail sod
    WHERE
        sod.UnitPrice > 10
        AND sod.LineTotal > 100
        AND EXISTS
        (
            SELECT *
            FROM AdventureWorks.Sales.SalesOrderHeader soh
            JOIN AdventureWorks.Person.Contact c ON
                c.ContactID = soh.CustomerID
            WHERE
                soh.SalesOrderID = sod.SalesOrderID
                AND c.LastName LIKE ''Ab%''
        )
        AND EXISTS
        (
            SELECT *
            FROM AdventureWorks.Production.Product p
            WHERE
                p.ProductID = sod.ProductID
                AND p.Color IS NULL
        )
        AND NOT EXISTS
        (
            SELECT *
            FROM AdventureWorks.Sales.SalesOrderHeader soh
            JOIN AdventureWorks.Sales.SalesPerson sp ON
                soh.SalesPersonID = sp.SalesPersonID
            WHERE
                soh.SalesOrderID = sod.SalesOrderID
                AND sp.CommissionPct > 50
        )
')

INSERT #AvgTimes
SELECT
    r.cpu_time,
    'INSERT EXEC'
FROM sys.dm_exec_requests r
WHERE
    r.session_id = @@SPID

DROP TABLE #MyTable
GO 5

SELECT
    AVG
    (
        CASE a.insert_type
            WHEN 'INSERT inside EXEC' THEN a.CPU_time
            ELSE NULL
        END
    ) AS [CPU time - INSERT inside EXEC],
    AVG
    (
        CASE a.insert_type
            WHEN 'INSERT EXEC' THEN a.CPU_time
            ELSE NULL
        END
    ) AS [CPU time - INSERT EXEC]
FROM #AvgTimes a
GO

DROP TABLE #AvgTimes
GO

So what have we learned today? The choice between INSERT EXEC and INSERT inside of EXEC is not purely stylistic and has definite performance implications. Here are the rules I'll be following from now on:

  • When working with permanent tables, always avoid INSERT EXEC if possible. There are some cases where it won't be possible to avoid. I only showed dynamic SQL in this post, but INSERT EXEC also applies to stored procedures. Can you safely rip apart all of the stored procedures in your system in order to avoid this issue? Maybe not quite as easily as you can rip apart the dynamic SQL within stored procedures.
  • When working with temporary tables, evaluate the complexity of the operations, the amount of data to be inserted, and most importantly, test every solution! The more rows that are inserted as a result of the INSERT EXEC, the more the overhead you'll get from the Parameter Table. On the flip side, the more complex the dynamic statement, the more overhead you'll get from recompilations. Every scenario is bound to be different and you may just learn something about your processes by doing this extra bit of analysis.
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
7777kkkk成人观看| 国产剧情久久久久久| 亚洲国产欧美一区二区三区久久| 国产精品久久久久久久一区探花| 久久久日本电影| 国产精品视频不卡| 久久人91精品久久久久久不卡| 欧美中文字幕在线播放| 欧美日韩国产精品一区二区三区四区| 国产自摸综合网| 亚洲精美色品网站| 成人国产精品免费视频| 欧美亚洲第一页| 久久视频在线免费观看| 亚洲天堂av综合网| 欧美亚洲伦理www| 日韩欧美亚洲一二三区| 少妇激情综合网| 亚洲精品成人久久电影| 成人福利在线观看| 亚洲视频网站在线观看| 在线观看精品自拍私拍| 91免费福利视频| 91亚洲一区精品| 亚洲欧美中文日韩在线| 欧美理论在线观看| 国产精品久久久一区| 欧美在线视频网站| 免费成人高清视频| 亚洲人a成www在线影院| 成人国产精品一区| 亚洲男人的天堂在线播放| 久久精品视频在线观看| 一二美女精品欧洲| 亚洲欧洲一区二区三区在线观看| 午夜精品在线视频| 国产精品日韩电影| 久久久久久美女| 午夜精品免费视频| 欧美日韩在线视频首页| 亚洲综合社区网| 亚洲天堂色网站| 成人黄色午夜影院| 久久久久久有精品国产| 国产日韩欧美在线视频观看| 综合国产在线观看| 国产精品爽黄69天堂a| 亚洲精品不卡在线| 正在播放欧美视频| 亚洲无av在线中文字幕| 国产午夜精品美女视频明星a级| 久久成年人视频| 亚洲国产97在线精品一区| 亚洲天堂免费观看| 在线精品视频视频中文字幕| 日韩高清电影好看的电视剧电影| 亚洲精品国偷自产在线99热| 精品久久在线播放| 激情久久av一区av二区av三区| 亚洲一区国产精品| 91免费国产视频| 91精品久久久久久久久久久久久久| 欧美性jizz18性欧美| 久久99国产精品久久久久久久久| 搡老女人一区二区三区视频tv| 91亚洲精品视频| 欧美一区二区三区免费观看| 国产精品视频99| 欧美在线影院在线视频| 国产成人福利网站| 欧美巨乳在线观看| 国产精品美女免费| 国产成人精品在线| 欧美视频中文字幕在线| 中国china体内裑精亚洲片| 国产精品夜间视频香蕉| 九九精品在线播放| 欧美与黑人午夜性猛交久久久| 国产成人精品久久亚洲高清不卡| 38少妇精品导航| 国产视频精品在线| 97精品国产97久久久久久免费| 国产日韩精品综合网站| 久久精品在线视频| 亚洲国产精品电影在线观看| 亚洲国产欧美一区二区丝袜黑人| 色综合久久久久久中文网| 成人a级免费视频| 亚洲第一视频网站| 色天天综合狠狠色| 国产69精品99久久久久久宅男| 欧美精品一区二区免费| 中文字幕国产精品| 日本中文字幕不卡免费| 91啪国产在线| 亚洲精品日韩久久久| 中文字幕欧美国内| 国产视频精品一区二区三区| 国产日韩在线观看av| 久久成人精品一区二区三区| 国产亚洲aⅴaaaaaa毛片| 国产精品久久久久秋霞鲁丝| 国产精品视频午夜| 国产亚洲欧美日韩精品| 国产成人精品久久二区二区91| 91极品视频在线| 日韩精品一区二区三区第95| 亚洲男人天堂2024| 最新中文字幕亚洲| 中文字幕视频在线免费欧美日韩综合在线看| 欧美电影院免费观看| 国产精品久久久久久久久久尿| 国产精品视频导航| 亚洲性xxxx| 超碰精品一区二区三区乱码| 97色在线视频| 日韩欧美中文字幕在线播放| 成人免费福利视频| 日韩av最新在线| 亚洲 日韩 国产第一| 懂色aⅴ精品一区二区三区蜜月| 国产精品嫩草视频| 日本一区二区在线播放| 91久久精品美女| 最近2019好看的中文字幕免费| 亚洲第一区中文99精品| 中文字幕亚洲情99在线| 成人www视频在线观看| 亚洲老头老太hd| 亚洲国产精彩中文乱码av| 亚洲日本欧美中文幕| 国产精品青草久久久久福利99| 亚洲成人国产精品| 午夜欧美大片免费观看| 国产精品福利在线观看| 一级做a爰片久久毛片美女图片| 国产日韩中文字幕在线| 久久精品2019中文字幕| 亚洲国产精品悠悠久久琪琪| 在线电影欧美日韩一区二区私密| 国产成人久久久精品一区| 韩国v欧美v日本v亚洲| 亚洲伊人久久大香线蕉av| 久久青草精品视频免费观看| 欧美一级黑人aaaaaaa做受| 国产精品一区二区3区| 亚洲成人av片在线观看| 俺也去精品视频在线观看| 亚洲aⅴ男人的天堂在线观看| 国内外成人免费激情在线视频网站| 亚洲欧美另类自拍| 久久成人这里只有精品| 国产拍精品一二三| 亚洲天堂视频在线观看| 亚洲精品欧美日韩| www.精品av.com| 国产精品免费小视频| 色综合久久天天综线观看| 疯狂做受xxxx高潮欧美日本| 久久精品人人做人人爽| 九九热最新视频//这里只有精品| 成人午夜一级二级三级| 日韩av中文字幕在线免费观看| 亚洲欧美一区二区三区在线|