Table-values parameter(TVP)系列之一:在T-SQL中创建和使用TVP

一.摘要
  表值参数(Table-valued parameters)简称TVP,是SQL Server 2008中引入的一种新特性,它提供了一种内置的方式,让客户端应用可以只通过单独的一条参化数SQL语句,就可以向SQL Server发送多行数据。

二.简介
  在表值参数出现以前,当需要发送多行数据到SQL Server,我们只能使用一些替代方案来实现:
  (1) 使用一连串的独立参数来表示多列和多行数据的值。
      使用这一方法,可以被传递的数据总量受限于可用参数的个数。SQL Server的存储过程最多可以使用2100个参数。
      在这种方法中,服务端逻辑必须将这些独立的值组合到表变量中,或是临时表中进行处理。
  (2) 将多个数据值捆绑到带限定符的字符串或是XML文档中,然后再将文本值传递到一个存储过程或语句中。
      这种方式要求存储过程或语句中要有必要的数据结构验证和数据松绑的逻辑。
  (3) 为多行数据的修改创建一系列独立的SQL语句。
      就像在一个SqlDataAdapter中调用Update方法时产生的那些一样,这些更新可以被独立地或是分组成批地提交到服务器。
      不过,尽管成批提交中含有多重语句,但这些语句在服务端都是被分开独立执行的。
  (4) 使用bcp实用程序或是使用SqlBulkCopy对象将多行数据载入一个表中。
        尽管这一技术效率很高,但它并不支持在服务端执行(注:多行数据仍然无法一次性传给存储过程),除非数据是被载入到临时表或是表变量中。
  SQL Server 2008中的T-SQL功能新增了表值参数。利用这个新增特性,我们可以很方便地通过T-SQL语句,或者通过一个应用程序,将一个表作为参数传给函数或存储过程。
  (1) 表值参数表示你可以把一个表类型作为参数传递到函数或存储过程里。
  (2) 表值参数的功能可以允许你向被声明为T-SQL变量的表中导入数据,然后把该表作为一个参数传递到存储过程或函数中去。
  (3) 表值参数的优点在于你可以向存储过程或函数发送多行数据,而无需向以前那样必须声明多个参数或者使用XML参数类型来处理多行数据。

三.描述
  计划分三部分描述表值参数的应用。
  (1) 在T-SQL中创建和使用TVP
  (2) 在ADO.NET中利用DataTable对象,将其作为参数传给存贮过程
  (3) 在ADO.NET中利用Collection对象,将其作为参数传给存贮过程

四.第一部分:在T-SQL中创建和使用TVP
  参看URL: ms-help://MS.SQLCC.v10/MS.SQLSVR.v10.en/s10de_1devconc/html/5e95a382-1e01-4c74-81f5-055612c2ad99.htm
  1. 表值参数具有两个主要部分:SQL Server 类型以及引用该类型的参数,若要创建和使用表值参数,请执行以下步骤:
    (1) 创建表类型并定义表结构。
          TVP功能的基础是SQL2008中最新的用户自定义表类型(User-Defined Table Types),简称UDTT,它允许用户将表的定义注册为全局周知类型。
          注册之后,这些表类型可以像本地变量一样用于批处理中、以及存储过程的函数体中,也就是UDTT的变量可以作为参数在存储过程和参数化TSQL中使用。
                                  用户自定义表类型的使用有许多限制:
                                  (1) 一个用户自定义表类型不允许用来定义表的列类型,也不能用来定义一个用户自定义结构类型的字段。
                                  (2) 不允许在一个用户自定义表类型上创建一个非聚合索引,除非这个索引是基于此用户自定义表类型创建的主键或唯一约束。
                                  (3) 在用户自定义表类型的定义中,不能指定缺省值。
                                  (4) 一旦创建后,就不允许再对用户自定义表类型的定义进行修改。
                                  (5) 用户自定义函数不能以用户定义表类型中的计算列定义为参数来调用。
                                  (6) 一个用户自定义表类型不允许作为表值型参数来调用用户自定义函数。
                                        例如:

/* Create a user-defined table type */
CREATE TYPE OrderItem$Udt AS TABLE(
                                                  OrderId int primary key,
                                                  CustomerId int,
                                                  OrderedAt datetime
                                        )
                                        GO   

                (2) 声明具有表类型参数的例程。

                        --------------------------

CREATE PROCEDURE OrderItem$Insert(
                                @OrderHeaders AS OrderItem$Udt READONLY,
                                @OrderDetails AS OrderDetail$Udt READONLY)
                        AS
                        BEGIN
                                -- Bulk insert order header rows from TVP
                                INSERT INTO [OrderItem]
                            SELECT *, SYSDATETIME() FROM @OrderHeaders
                            -- Bulk insert order detail rows from TVP
                            INSERT INTO [OrderDetail]
                            SELECT *, SYSDATETIME() FROM @OrderDetails
                        END
                        GO

                (3) 声明表类型变量,并引用该表类型。

                        ---------------------------

                        IF OBJECT_ID (N'OrderItem', N'U') IS NOT NULL
                                DROP TABLE [OrderItem]
                        GO
                        CREATE TABLE [OrderItem](
                                OrderId int NOT NULL primary key,
                                CustomerId int NOT NULL,
                                OrderedAt datetime NOT NULL,
                                CreatedAt datetime2(0) NOT NULL DEFAULT sysdatetime()
                        )
                        GO

                (4) 使用 INSERT 语句填充表变量。

                        ------------------------

                       

            DECLARE @OrderItemUdt        dbo.OrderItem$Udt
            INSERT INTO @OrderItemUdt
            VALUES (1,20,GETDATE()),(2,31,GETDATE()),(100,4,GETDATE()),(201,51,GETDATE())
            SELECT * FROM @OrderItemUdt

                (5) 创建并填充表变量后,可以将该变量传递给例程。

                        ------------------------

                       

            EXEC dbo.OrderItem$Insert @OrderItemUdt,@OrderDetailUdt
            SELECT * FROM dbo.OrderItem

        2. 优点

                表值参数具有更高的灵活性,在某些情况下,可比临时表或其他传递参数列表的方法提供更好的性能。表值参数具有以下优势:

                (1)首次从客户端填充数据时,不获取锁。

                (2)提供简单的编程模型。

                (3)允许在单个例程中包括复杂的业务逻辑。

                (4)减少到服务器的往返。

                (5)可以具有不同基数的表结构。

                (6)是强类型。

                (7)使客户端可以指定排序顺序和唯一键。

        3. 限制

                表值参数有下面的限制:

                (1) SQL Server 不维护表值参数列的统计信息。

                (2) 表值参数必须作为输入 READONLY 参数传递到 Transact-SQL 例程。

                      不能在例程体中对表值参数执行诸如 UPDATE、DELETE 或 INSERT 这样的 DML 操作。

                      ***如果想要修改那些已经传入到存储过程或参数化语句中的表值型参数中的数据,只能通过向临时表或表变量中插入数据来实现。

                (3) 不能将表值参数用作 SELECT INTO 或 INSERT EXEC 语句的目标。

                      表值参数可以在 SELECT INTO 的 FROM 子句中,也可以在 INSERT EXEC 字符串或存储过程中。

        4. 作用域

                (1) 就像其他参数一样,表值参数的作用域也是存储过程、函数或动态 Transact-SQL 文本。

                (2) 表类型变量也与使用 DECLARE 语句创建的其他任何局部变量一样具有作用域。

                    可以在动态 Transact-SQL 语句内声明表值变量,并且可以将这些变量作为表值参数传递到存储过程和函数。

                (3) 一般多用于行数小于1000行的数据。

                    应用比较广泛的是在Browse Master的多行数据作为过滤条件时使用。

                    利用TVP使得一次插入多项或Select多行变得大为简单。过去,我们使用笨拙的逗号分隔列表或XML,虽其能够胜任,但不是以习惯的对象方式存在,而且存取速度也很慢。

          例如:会计系统的选择的多个部门多个科目或多个部所时,利用TVP的方式可以大大提高存取的速度也可提高编程的可读性。

五. 例子

USE AdventureWorks
GO
------------------------
IF  EXISTS (SELECT * FROM sys.types st JOIN sys.schemas ss ON st.schema_id = ss.schema_id
  WHERE st.name = N'OrderItem$Udt' AND ss.name = N'dbo')
DROP TYPE [dbo].[OrderItem$Udt]
GO
CREATE TYPE OrderItem$Udt AS TABLE(
  OrderId int primary key,
  CustomerId int,
  OrderedAt datetime)
GO
------------------------
IF  EXISTS (SELECT * FROM sys.types st JOIN sys.schemas ss ON st.schema_id = ss.schema_id
  WHERE st.name = N'OrderDetail$Udt' AND ss.name = N'dbo')
DROP TYPE [dbo].[OrderDetail$Udt]
GO
CREATE TYPE OrderDetail$Udt AS TABLE(
OrderId int,
    LineNumber int primary key(OrderId,LineNumber),
ProductId int,
Quantity int,
Price money)
GO
---------------------------
IF OBJECT_ID (N'OrderItem', N'U') IS NOT NULL
DROP TABLE [OrderItem]
GO
CREATE TABLE [OrderItem](
OrderId int NOT NULL primary key,
CustomerId int NOT NULL,
OrderedAt datetime NOT NULL,
CreatedAt datetime2(0) NOT NULL DEFAULT sysdatetime()
)
GO
--------------------------
IF OBJECT_ID (N'OrderDetail', N'U') IS NOT NULL
DROP TABLE [OrderDetail]
GO
CREATE TABLE [OrderDetail](
OrderId int NOT NULL,
LineNumber int NOT NULL primary key(OrderId,LineNumber),
ProductId int NOT NULL,
Quantity int NOT NULL,
Price money NOT NULL,
CreatedAt datetime2(0) NOT NULL DEFAULT sysdatetime())
GO
------------------------
IF OBJECT_ID(N'OrderItem$Insert',N'P') IS NOT NULL
DROP PROC OrderItem$Insert
GO
CREATE PROCEDURE OrderItem$Insert(
@OrderHeaders AS OrderItem$Udt READONLY,
@OrderDetails AS OrderDetail$Udt READONLY)
AS
BEGIN
-- Bulk insert order header rows from TVP
INSERT INTO [OrderItem]
    SELECT *, SYSDATETIME() FROM @OrderHeaders
    -- Bulk insert order detail rows from TVP
    INSERT INTO [OrderDetail]
    SELECT *, SYSDATETIME() FROM @OrderDetails
END
GO
------------------------
DECLARE @OrderItemUdt dbo.OrderItem$Udt
INSERT INTO @OrderItemUdt
VALUES (1,20,GETDATE()),(2,31,GETDATE()),(100,4,GETDATE()),(201,51,GETDATE())
SELECT * FROM @OrderItemUdt
------------------------
DECLARE @OrderDetailUdt dbo.OrderDetail$Udt
INSERT INTO @OrderDetailUdt
VALUES (1,1,11,111,1111),(1,2,12,121,1212.12),(1,3,13,131,1313.13),
  (2,1,21,211,2121),(2,2,22,222,2222.22),(2,3,23,231,2323.23),
  (100,1,101,1001,1001.1001),(100,2,102,1002,1002.1002),(100,3,103,1003,1003.1003),
  (201,1,2011,2011,201.201),(201,2,2012,2012,2012.2012)
SELECT * FROM  @OrderDetailUdt
------------------------
EXEC dbo.OrderItem$Insert @OrderItemUdt,@OrderDetailUdt
SELECT * FROM dbo.OrderItem
SELECT * FROM dbo.OrderDetail
GO

六.其他

          下一部分的内容为:Table-values parameter(TVP)系列之二: 在ADO.NET中利用DataTable对象,将其作为参数传给存贮过程

时间: 2024-10-01 03:40:52

Table-values parameter(TVP)系列之一:在T-SQL中创建和使用TVP的相关文章

ASP.NET 5系列教程 (六): 在 MVC6 中创建 Web API

ASP.NET 5.0 的主要目标之一是统一MVC 和 Web API 框架应用. 接下来几篇文章中您会了解以下内容: ASP.NET MVC 6 中创建简单的web API. 如何从空的项目模板中启动,及添加控件到应用中. 如何配置 ASP.NET 5.0 管道. 在 IIS 外对立部署应用. 本文的目的是从空的项目开始,逐步讲解如何创建应用.当然,您也可以从"Starter Web" 模板开始,它默认包含了MVC 6.权限.记录等其他模块,同时也内置了有效的控制器和视图在其中. 创

Table-values parameter(TVP)系列之二: 利用DataTable将其作为参数传给SP

一,回顾         上一部分讲述了"在T-SQL中创建和使用TVP",通过T-SQL建立如下的对象:         1)Tables           dbo.OrderItem           dbo.OrderDetail         2) User-Defined Table Types             dbo.OrderDetail$Udt             dbo.OrderItem$Udt         3) Stored Procedur

CLR探索系列-GC中的Card table和Brick Table(垃圾回收系列)

在CLR的垃圾回收子系统中,Card Table和Brick Table是两个比较有意思的表. 在GC的过程中,一个Heap在运行了一段时间以后,已经分配的空间就会越来越大.在进行了一次局部代或者是完全的垃圾回收以后,就会涉及到一个GC堆的类似碎片整理的概念.整理优化一次GC Heap.同时,这种机制保证了譬如一个IIS Server在长时间的运行过程中的稳定性并且优化了其内存管理. 这样的好处是显而易见的,但是采用这种解决方案带来的问题也很容易想到:譬如一个存在于GC Heap里面的"Smal

SQL Server 2008空间数据应用系列五:数据表中使用空间数据类型

原文:SQL Server 2008空间数据应用系列五:数据表中使用空间数据类型 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server 2008 R2调测. 2.具备 Transact-SQL 编程经验和使用 SQL Server Management Studio 的经验. 3.熟悉或了解Microsoft SQL Server 2008中的空间数据类型. 4.具备相应(比如OGC)的GIS专业理论知识. 5.其他相关知识.   通过前面几篇文章介

ASP.NET 5系列教程 (四):向视图中添加服务和发布应用到公有云

向视图中添加服务 现在,ASP.NET MVC 6 支持注入类到视图中,和VC类不同的是,对类是公开的.非嵌套或非抽象并没有限制.在这个例子中,我们创建了一个简单的类,用于统计代办事件.已完成事件和平均优先级的服务. 1. 添加命名为Services 的文件夹,在该文件夹下添加名称为 StatisticsService.cs 的类: StatisticsService 类代码设计如下: using System.Linq; using System.Threading.Tasks; using

火狐下table中创建form导致两个table之间出现空白的问题

在<FORM>中加CSS <table> ....... </table> <form style="padding:0; margin:0;"> <input name=""> </form> <table> ....... </table> 注意:在火狐中 <table> <form>.......</form> </tabl

grep 命令系列:如何在 UNIX 中根据文件内容查找文件

grep 命令系列:如何在 UNIX 中根据文件内容查找文件 为了完成课程作业,我写了很多 C 语言代码并把它们保存为 /home/user/c/*.c 和 *.h.那么在 UNIX shell 窗口中我如何能通过字符串或者单词(例如函数名 main())文件内容来查找文件呢? 你需要用到以下工具: [a] grep 命令 : 输出匹配模式的行. [b] find 命令: 在目录层次中查找文件. 使用 grep 命令根据内容查找文件 输入以下命令: grep 'string' *.txt gre

“Win10 UAP 开发系列”之 在MVVM模式中控制ListView滚动位置

原文:"Win10 UAP 开发系列"之 在MVVM模式中控制ListView滚动位置 这个扩展属性从WP8.1就开始用了,主要是为了解决MVVM模式中无法直接控制ListView滚动位置的问题.比如在VM中刷新了数据,需要将View中的ListView滚动到顶部,ListView只有一个ScrollIntoView()方法可以控制滚动的位置,但最好在VM中不要出现直接控制View的代码,需要通过其他的方式. 使用一个扩展属性即可实现: /// <summary> ///

火狐下table中创建form导致两个table之间出现空白_javascript技巧

在<FORM>中加CSS <table> ....... </table> <form style="padding:0; margin:0;"> <input name=""> </form> <table> ....... </table> 注意:在火狐中 <table> <form>.......</form> </tabl