以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化。

 本系列主要是针对T-SQL的总结。

【T-SQL基础】01.单表查询-几道sql查询题

【T-SQL基础】02.联接查询

【T-SQL基础】03.子查询

【T-SQL基础】04.表表达式-上篇

【T-SQL基础】04.表表达式-下篇

【T-SQL基础】05.集合运算

【T-SQL基础】06.透视、逆透视、分组集

【T-SQL基础】07.数据修改

【T-SQL基础】08.事务和并发

【T-SQL基础】09.可编程对象

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

【T-SQL进阶】01.好用的SQL TVP~~独家赠送[增-删-改-查]的例子

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

【T-SQL性能调优】01.TempDB的使用和性能问题

【T-SQL性能调优】02.Transaction Log的使用和性能问题

【T-SQL性能调优】03.执行计划

【T-SQL性能调优】04.死锁分析

持续更新......欢迎关注我!

一、什么是TVP?

表值参数Table-Value Parameter (TVP) 提供一种将客户端应用程序中的多行数据封送到 SQL Server 的简单方式,而不需要多次往返或特殊服务器端逻辑来处理数据。 您可以使用表值参数来包装客户端应用程序中的数据行,并使用单个参数化命令将数据发送到服务器。 传入的数据行存储在一个表变量中,然后您可以通过使用 Transact-SQL 对该表变量进行操作。

可以使用标准的 Transact-SQL SELECT 语句来访问表值参数中的列值。

简单点说就是当想传递aaaa,bbbb,cccc,dddd给存储过程时,可以先将aaa,bbb,ccc,dddd存到一张表中:

aaaa
bbbb
cccc
dddd

然后将这张表传递给存储过程。

如:当我们需要查询指定产品的信息时,通常可以传递一串产品ID到存储过程里面,如"1,2,3,4",然后查询出ID=1或ID=2或ID=3或ID=4的产品信息。

可以先将"1,2,3,4"存到一张表中,然后将这张表传给存储过程。

1
2
3
4

那么这种方法有什么优势呢?请接着往下看。

二、早期版本是怎么在 SQL Server 中传递多行的?

在 SQL Server 2008 中引入表值参数之前,用于将多行数据传递到存储过程或参数化 SQL 命令的选项受到限制。 开发人员可以选择使用以下选项,将多个行传递给服务器:

  • 使用一系列单个参数表示多个数据列和行中的值。 使用此方法传递的数据量受所允许的参数数量的限制。 SQL Server 过程最多可以有 2100 个参数。 必须使用服务器端逻辑才能将这些单个值组合到表变量或临时表中以进行处理。

  • 将多个数据值捆绑到分隔字符串或 XML 文档中,然后将这些文本值传递给过程或语句。 此过程要求相应的过程或语句包括验证数据结构和取消捆绑值所需的逻辑。

  • 针对影响多个行的数据修改创建一系列的单个 SQL 语句,例如通过调用 SqlDataAdapter 的 Update 方法创建的内容。 可将更改单独提交给服务器,也可以将其作为组进行批处理。 不过,即使是以包含多个语句的批处理形式提交的,每个语句在服务器上还是会单独执行。

  • 使用 bcp 实用工具程序或 SqlBulkCopy 对象将很多行数据加载到表中。 尽管这项技术非常有效,但不支持服务器端处理,除非将数据加载到临时表或表变量中。

三、例子

当我们需要查询指定产品的信息时,通常可以传递一串产品ID到存储过程里面,如"1,2,3,4",然后查询出ID=1或ID=2或ID=3或ID=4的产品信息。

我们可以先将“1,2,3,4”存到一张表中,然后作为参数传给存储过程。在存储过程里面操作这个参数。

1.使用TVP 查询产品

查询产品ID=1,2,3,4,5的产品

  1. public static void TestGetProductsByIDs()
  2. {
  3. Collection<int> productIDs = new Collection<int>();
  4. Console.WriteLine();
  5. Console.WriteLine("----- Get Product ------");
  6. Console.WriteLine("Product IDs: 1,2,3,4,5");
  7. productIDs.Add(1);
  8. productIDs.Add(2);
  9. productIDs.Add(3);
  10. productIDs.Add(4);
  11. productIDs.Add(5);
  12.  
  13. Collection<Product> dtProducts = GetProductsByIDs(productIDs);
  14. foreach (Product product in dtProducts)
  15. {
  16. Console.WriteLine("{0} {1}", product.ID, product.Name);
  17. }
  18. }

查询的方法:

  1. /// <summary>
  2. /// Data access layer. Gets products by the collection of the specific product' ID.
  3. /// </summary>
  4. /// <param name="conn"></param>
  5. /// <param name="productIDs"></param>
  6. /// <returns></returns>
  7. public static Collection<Product> GetProductsByIDs(SqlConnection conn, Collection<int> productIDs)
  8. {
  9. Collection<Product> products = new Collection<Product>();
  10. DataTable dtProductIDs = new DataTable("Product");
  11. dtProductIDs.Columns.Add("ID", typeof(int));
  12.  
  13. foreach (int id in productIDs)
  14. {
  15. dtProductIDs.Rows.Add(
  16. id
  17. );
  18. }
  19.  
  20. SqlParameter tvpProduct = new SqlParameter("@ProductIDsTVP", dtProductIDs);
  21. tvpProduct.SqlDbType = SqlDbType.Structured;
  22. //SqlHelper.ExecuteNonQuery(conn, CommandType.StoredProcedure, "procGetProducts", tvpProduct);
  23.  
  24. using (SqlDataReader dataReader = SqlHelper.ExecuteReader(conn, CommandType.StoredProcedure, "procGetProductsByProductIDsTVP", tvpProduct))
  25. {
  26. while (dataReader.Read())
  27. {
  28. Product product = new Product();
  29. product.ID = dataReader.IsDBNull(0) ? 0 : dataReader.GetInt32(0);
  30. product.Name = dataReader.IsDBNull(1) ? (string)null : dataReader.GetString(1).Trim();
  31.  
  32. products.Add(product);
  33. }
  34. }
  35. return products;
  36. } 

创建以产品ID作为列名的TVP:

  1. IF NOT EXISTS( SELECT * FROM sys.types WHERE name = 'ProductIDsTVP')
  2. CREATE TYPE [dbo].[ProductIDsTVP] AS TABLE
  3. (
  4. [ID] INT
  5. )
  6. GO

查询产品的存储过程:

  1. /****** Object: StoredProcedure [dbo].[procGetProductsByProductIDsTVP]******/
  2. SET ANSI_NULLS ON
  3. GO
  4. SET QUOTED_IDENTIFIER ON
  5. GO
  6.  
  7. IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[procGetProductsByProductIDsTVP]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
  8. DROP PROCEDURE [dbo].[procGetProductsByProductIDsTVP]
  9. GO
  10.  
  11. Create PROCEDURE [dbo].[procGetProductsByProductIDsTVP]
  12. (
  13. @ProductIDsTVP ProductIDsTVP READONLY
  14. )
  15. AS
  16.  
  17. SELECT p.ID, p.Name
  18.  
  19. FROM Product as p
  20. INNER JOIN @ProductIDsTVP as t on p.ID = t.ID

2.使用TVP 删除产品

删除产品ID=1,5,6的产品

  1. public static void TestDeleteProductsByIDs()
  2. {
  3. Collection<int> productIDs = new Collection<int>();
  4. Console.WriteLine();
  5. Console.WriteLine("----- Delete Products ------");
  6. Console.WriteLine("Product IDs: 1,5,6");
  7. productIDs.Add(1);
  8. productIDs.Add(5);
  9. productIDs.Add(6);
  10. DeleteProductsByIDs(productIDs);
  11. }

删除的方法:

  1. /// <summary>
  2. /// Deletes products by the collection of the specific product' ID
  3. /// </summary>
  4. /// <param name="conn"></param>
  5. /// <param name="productIDs"></param>
  6. public static void DeleteProductsByIDs(SqlConnection conn, Collection<int> productIDs)
  7. {
  8. Collection<Product> products = new Collection<Product>();
  9. DataTable dtProductIDs = new DataTable("Product");
  10. dtProductIDs.Columns.Add("ID", typeof(int));
  11.  
  12. foreach (int id in productIDs)
  13. {
  14. dtProductIDs.Rows.Add(
  15. id
  16. );
  17. }
  18.  
  19. SqlParameter tvpProduct = new SqlParameter("@ProductIDsTVP", dtProductIDs);
  20. tvpProduct.SqlDbType = SqlDbType.Structured;
  21. SqlHelper.ExecuteNonQuery(conn, CommandType.StoredProcedure, "procDeleteProductsByProductIDsTVP", tvpProduct);
  22. }

删除产品的存储过程:

  1. /****** Object: StoredProcedure [dbo].[procDeleteProductsByIDsByProductIDsTVP]******/
  2. SET ANSI_NULLS ON
  3. GO
  4. SET QUOTED_IDENTIFIER ON
  5. GO
  6.  
  7. IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[procDeleteProductsByProductIDsTVP]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
  8. DROP PROCEDURE [dbo].[procDeleteProductsByProductIDsTVP]
  9. GO
  10.  
  11. Create PROCEDURE [dbo].[procDeleteProductsByProductIDsTVP]
  12. (
  13. @ProductIDsTVP ProductIDsTVP READONLY
  14. )
  15. AS
  16.  
  17. DELETE p FROM Product AS p
  18. INNER JOIN @ProductIDsTVP AS t on p.ID = t.ID

3.使用TVP 增加产品

增加产品

ID=5,Name=bbb

ID=6,Name=abc

  1. public static void TestInsertProducts()
  2. {
  3. Collection<Product> products = new Collection<Product>();
  4. Console.WriteLine();
  5. Console.WriteLine("----- Insert Products ------");
  6. Console.WriteLine("Product IDs: 5-bbb,6-abc");
  7. products.Add(
  8. new Product()
  9. {
  10. ID = 5,
  11. Name = "qwe"
  12. });
  13.  
  14. products.Add(
  15. new Product()
  16. {
  17. ID = 6,
  18. Name = "xyz"
  19. });
  20.  
  21. InsertProducts(products);
  22. }

增加的方法:

  1. /// <summary>
  2. /// Inserts products by the collection of the specific products.
  3. /// </summary>
  4. /// <param name="conn"></param>
  5. /// <param name="products"></param>
  6. public static void InsertProducts(SqlConnection conn, Collection<Product> products)
  7. {
  8. DataTable dtProducts = new DataTable("Product");
  9. dtProducts.Columns.Add("ID", typeof(int));
  10. dtProducts.Columns.Add("Name", typeof(string));
  11.  
  12. foreach (Product product in products)
  13. {
  14. dtProducts.Rows.Add(
  15. product.ID,
  16. product.Name
  17. );
  18. }
  19.  
  20. SqlParameter tvpProduct = new SqlParameter("@ProductTVP", dtProducts);
  21. tvpProduct.SqlDbType = SqlDbType.Structured;
  22. SqlHelper.ExecuteNonQuery(conn, CommandType.StoredProcedure, "procInsertProductsByProductTVP", tvpProduct);
  23. }

增加产品的存储过程:

  1. /****** Object: StoredProcedure [dbo].[procInsertProductsByProductTVP]******/
  2. SET ANSI_NULLS ON
  3. GO
  4. SET QUOTED_IDENTIFIER ON
  5. GO
  6.  
  7. IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[procInsertProductsByProductTVP]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
  8. DROP PROCEDURE [dbo].[procInsertProductsByProductTVP]
  9. GO
  10.  
  11. Create PROCEDURE [dbo].[procInsertProductsByProductTVP]
  12. (
  13. @ProductTVP ProductTVP READONLY
  14. )
  15. AS
  16.  
  17. INSERT INTO Product (ID, Name)
  18. SELECT
  19. t.ID,
  20. t.Name
  21. FROM @ProductTVP AS t
  22.  
  23. GO

4.使用TVP 更新产品

 将ID=2的产品的Name更新为bbb

将ID=6的产品的Name更新为abc

  1. public static void TestUpdateProducts()
  2. {
  3. Collection<Product> products = new Collection<Product>();
  4. Console.WriteLine();
  5. Console.WriteLine("----- Update Products ------");
  6. Console.WriteLine("Product IDs: 2-bbb,6-abc");
  7. products.Add(
  8. new Product()
  9. {
  10. ID = 2,
  11. Name = "bbb"
  12. });
  13.  
  14. products.Add(
  15. new Product()
  16. {
  17. ID = 6,
  18. Name = "aaa"
  19. });
  20.  
  21. UpdateProducts(products);
  22. }

 更新的方法:

  1. /// <summary>
  2. /// Updates products by the collection of the specific products
  3. /// </summary>
  4. /// <param name="conn"></param>
  5. /// <param name="products"></param>
  6. public static void UpdateProducts(SqlConnection conn, Collection<Product> products)
  7. {
  8. DataTable dtProducts = new DataTable("Product");
  9. dtProducts.Columns.Add("ID", typeof(int));
  10. dtProducts.Columns.Add("Name", typeof(string));
  11.  
  12. foreach (Product product in products)
  13. {
  14. dtProducts.Rows.Add(
  15. product.ID,
  16. product.Name
  17. );
  18. }
  19.  
  20. SqlParameter tvpProduct = new SqlParameter("@ProductTVP", dtProducts);
  21. tvpProduct.SqlDbType = SqlDbType.Structured;
  22. SqlHelper.ExecuteNonQuery(conn, CommandType.StoredProcedure, "procUpdateProductsByProductTVP", tvpProduct);
  23. }

创建以产品ID和产品Name作为列名的TVP:

  1. IF NOT EXISTS( SELECT * FROM sys.types WHERE name = 'ProductTVP')
  2.  
  3. CREATE TYPE [dbo].[ProductTVP] AS TABLE(
  4. [ID] [int] NULL,
  5. [Name] NVARCHAR(100)
  6. )
  7.  
  8. GO

增加产品的存储过程:

  1. /****** Object: StoredProcedure [dbo].[procUpdateProductsByIDs]******/
  2. SET ANSI_NULLS ON
  3. GO
  4. SET QUOTED_IDENTIFIER ON
  5. GO
  6.  
  7. IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[procUpdateProductsByProductTVP]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
  8. DROP PROCEDURE [dbo].[procUpdateProductsByProductTVP]
  9. GO
  10.  
  11. Create PROCEDURE [dbo].[procUpdateProductsByProductTVP]
  12. (
  13. @ProductTVP ProductTVP READONLY
  14. )
  15. AS
  16.  
  17. Update p
  18. SET
  19. p.ID = t.ID,
  20. p.Name = t.Name
  21. FROM product AS p
  22. INNER JOIN @ProductTVP AS t on p.ID = t.ID
  23.  
  24. GO

结果:

注意:

(1)无法在表值参数中返回数据。 表值参数是只可输入的参数;不支持 OUTPUT 关键字。

(2)表值参数为强类型,其结构会自动进行验证。

(3)表值参数的大小仅受服务器内存的限制。

(4)删除表值参数时,需要先删除引用表值参数的存储过程。

四、写在最后

后期会将TVP的性能问题和SQL Bulk Copy的用法补上。

五、参考资料

表值参数 https://msdn.microsoft.com/zh-cn/library/bb675163.aspx

表值参数(数据库引擎)https://msdn.microsoft.com/zh-CN/Library/bb510489(SQL.100).aspx

推荐阅读:30分钟全面解析-SQL事务+隔离级别+阻塞+死锁

推荐阅读:T-SQL基础博客目录

作  者:
Jackson0714

出  处:http://www.cnblogs.com/jackson0714/

关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教!

版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信

声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力!

好用的SQL TVP~~独家赠送[增-删-改-查]的例子的更多相关文章

  1. iOS sqlite3 的基本使用(增 删 改 查)

    iOS sqlite3 的基本使用(增 删 改 查) 这篇博客不会讲述太多sql语言,目的重在实现sqlite3的一些基本操作. 例:增 删 改 查 如果想了解更多的sql语言可以利用强大的互联网. ...

  2. iOS FMDB的使用(增,删,改,查,sqlite存取图片)

    iOS FMDB的使用(增,删,改,查,sqlite存取图片) 在上一篇博客我对sqlite的基本使用进行了详细介绍... 但是在实际开发中原生使用的频率是很少的... 这篇博客我将会较全面的介绍FM ...

  3. django ajax增 删 改 查

    具于django ajax实现增 删 改 查功能 代码示例: 代码: urls.py from django.conf.urls import url from django.contrib impo ...

  4. C# ADO.NET (sql语句连接方式)(增,删,改)

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  5. ADO.NET 增 删 改 查

    ADO.NET:(数据访问技术)就是将C#和MSSQL连接起来的一个纽带 可以通过ADO.NET将内存中的临时数据写入到数据库中 也可以将数据库中的数据提取到内存中供程序调用 ADO.NET所有数据访 ...

  6. MVC EF 增 删 改 查

    using System;using System.Collections.Generic;using System.Linq;using System.Web;//using System.Data ...

  7. python基础中的四大天王-增-删-改-查

    列表-list-[] 输入内存储存容器 发生改变通常直接变化,让我们看看下面列子 增---默认在最后添加 #append()--括号中可以是数字,可以是字符串,可以是元祖,可以是集合,可以是字典 #l ...

  8. SQL Server T—SQL 语句【建 增 删 改】(建外键)

    一 创建数据库         如果多条语句要一起执行,那么在每条语句之后需要加 go 关键字 建库  :  create  database  数据库名  create  database  Dat ...

  9. 网站的增 / 删 / 改 / 查 时常用的 sql 语句

    最近在学习数据库 php + mysql 的基本的 crud 的操作,记录碰到的坑供自己参考.crud中需要用到的sql语句还是比较多的,共包括以下几个内容: 查询所有数据 查询表中某个字段 查询并根 ...

随机推荐

  1. rdlc报表大小设置

    参考:http://stackoverflow.com/questions/427730/how-to-limit-rdlc-report-for-one-page-in-a-pdf 主要设置为:报表 ...

  2. 基于AWS的云服务架构最佳实践

    ZZ from: http://blog.csdn.net/wireless_com/article/details/43305701 近年来,对于打造高度可扩展的应用程序,软件架构师们挖掘了若干相关 ...

  3. tkinter 在 x window 下的字体设置格式

    X Font Descriptors # X Font Descriptors are strings having the following format (the asterisks repre ...

  4. Docker-2:network containers

    docker run -d -P --name web training/webapp python app.py # -name means give the to-be-run container ...

  5. 解决MVC中JSON字符长度超出限制的异常

    解决MVC中JSON字符长度超出限制的异常 解决方法如下: <configuration> <system.web.extensions> <scripting> ...

  6. 页面轮换,ViewFlipper 和 ViewPager 的区别

    ViewFlipper继承ViewAnimator,切换view的时候是有动画效果的,适合做ppt,多界面的程序欢迎引导界面,算是个轻量级的组件,适合展示静态数据,少量数据. ViewPager继承V ...

  7. vs2013卸载后重新安装不能用了,如何解决

    vs2013卸载后重新安装不能用了 据说VS卸载后有残留文件,估计是注册文件没删除,弄了很多方法,最后只有重装.你可以下载一个cclearn清理注册表,再装试试 我卸载完用360清理了一下 之后再安装 ...

  8. 使用Safari远程调试iOS设备网页

    最近在做HTML 5游戏时,发布到手机上访问网页总是莫名其妙出现问题,苦于没有remote debug功能一直没有查找到问题. 这边博客详细介绍了iOS, Android, Windows Phone ...

  9. STM32之独立看门狗与窗口看门狗总结

    一.独立看门狗 STM32 的独立看门狗由内部专门的 40Khz 低速时钟驱动,即使主时钟发生故障,它也仍然有效. 看门狗的原理:单片机系统在外界的干扰下会出现程序跑飞的现象导致出现死循环,看门狗电路 ...

  10. C# Exception 写入文件

    /// <summary> /// 将异常打印到LOG文件 /// </summary> /// <param name="ex">异常< ...