以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化。
本系列主要是针对T-SQL的总结。
【T-SQL基础】06.透视、逆透视、分组集
【T-SQL基础】07.数据修改
【T-SQL基础】09.可编程对象
----------------------------------------------------------
【T-SQL进阶】01.好用的SQL TVP~~独家赠送[增-删-改-查]的例子
----------------------------------------------------------
【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的产品
public static void TestGetProductsByIDs()
{
Collection<int> productIDs = new Collection<int>();
Console.WriteLine();
Console.WriteLine("----- Get Product ------");
Console.WriteLine("Product IDs: 1,2,3,4,5");
productIDs.Add(1);
productIDs.Add(2);
productIDs.Add(3);
productIDs.Add(4);
productIDs.Add(5); Collection<Product> dtProducts = GetProductsByIDs(productIDs);
foreach (Product product in dtProducts)
{
Console.WriteLine("{0} {1}", product.ID, product.Name);
}
}
查询的方法:
/// <summary>
/// Data access layer. Gets products by the collection of the specific product' ID.
/// </summary>
/// <param name="conn"></param>
/// <param name="productIDs"></param>
/// <returns></returns>
public static Collection<Product> GetProductsByIDs(SqlConnection conn, Collection<int> productIDs)
{
Collection<Product> products = new Collection<Product>();
DataTable dtProductIDs = new DataTable("Product");
dtProductIDs.Columns.Add("ID", typeof(int)); foreach (int id in productIDs)
{
dtProductIDs.Rows.Add(
id
);
} SqlParameter tvpProduct = new SqlParameter("@ProductIDsTVP", dtProductIDs);
tvpProduct.SqlDbType = SqlDbType.Structured;
//SqlHelper.ExecuteNonQuery(conn, CommandType.StoredProcedure, "procGetProducts", tvpProduct); using (SqlDataReader dataReader = SqlHelper.ExecuteReader(conn, CommandType.StoredProcedure, "procGetProductsByProductIDsTVP", tvpProduct))
{
while (dataReader.Read())
{
Product product = new Product();
product.ID = dataReader.IsDBNull(0) ? 0 : dataReader.GetInt32(0);
product.Name = dataReader.IsDBNull(1) ? (string)null : dataReader.GetString(1).Trim(); products.Add(product);
}
}
return products;
}
创建以产品ID作为列名的TVP:
IF NOT EXISTS( SELECT * FROM sys.types WHERE name = 'ProductIDsTVP')
CREATE TYPE [dbo].[ProductIDsTVP] AS TABLE
(
[ID] INT
)
GO
查询产品的存储过程:
/****** Object: StoredProcedure [dbo].[procGetProductsByProductIDsTVP]******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[procGetProductsByProductIDsTVP]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[procGetProductsByProductIDsTVP]
GO Create PROCEDURE [dbo].[procGetProductsByProductIDsTVP]
(
@ProductIDsTVP ProductIDsTVP READONLY
)
AS SELECT p.ID, p.Name FROM Product as p
INNER JOIN @ProductIDsTVP as t on p.ID = t.ID
2.使用TVP 删除产品
删除产品ID=1,5,6的产品
public static void TestDeleteProductsByIDs()
{
Collection<int> productIDs = new Collection<int>();
Console.WriteLine();
Console.WriteLine("----- Delete Products ------");
Console.WriteLine("Product IDs: 1,5,6");
productIDs.Add(1);
productIDs.Add(5);
productIDs.Add(6);
DeleteProductsByIDs(productIDs);
}
删除的方法:
/// <summary>
/// Deletes products by the collection of the specific product' ID
/// </summary>
/// <param name="conn"></param>
/// <param name="productIDs"></param>
public static void DeleteProductsByIDs(SqlConnection conn, Collection<int> productIDs)
{
Collection<Product> products = new Collection<Product>();
DataTable dtProductIDs = new DataTable("Product");
dtProductIDs.Columns.Add("ID", typeof(int)); foreach (int id in productIDs)
{
dtProductIDs.Rows.Add(
id
);
} SqlParameter tvpProduct = new SqlParameter("@ProductIDsTVP", dtProductIDs);
tvpProduct.SqlDbType = SqlDbType.Structured;
SqlHelper.ExecuteNonQuery(conn, CommandType.StoredProcedure, "procDeleteProductsByProductIDsTVP", tvpProduct);
}
删除产品的存储过程:
/****** Object: StoredProcedure [dbo].[procDeleteProductsByIDsByProductIDsTVP]******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[procDeleteProductsByProductIDsTVP]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[procDeleteProductsByProductIDsTVP]
GO Create PROCEDURE [dbo].[procDeleteProductsByProductIDsTVP]
(
@ProductIDsTVP ProductIDsTVP READONLY
)
AS DELETE p FROM Product AS p
INNER JOIN @ProductIDsTVP AS t on p.ID = t.ID
3.使用TVP 增加产品
增加产品
ID=5,Name=bbb
ID=6,Name=abc
public static void TestInsertProducts()
{
Collection<Product> products = new Collection<Product>();
Console.WriteLine();
Console.WriteLine("----- Insert Products ------");
Console.WriteLine("Product IDs: 5-bbb,6-abc");
products.Add(
new Product()
{
ID = 5,
Name = "qwe"
}); products.Add(
new Product()
{
ID = 6,
Name = "xyz"
}); InsertProducts(products);
}
增加的方法:
/// <summary>
/// Inserts products by the collection of the specific products.
/// </summary>
/// <param name="conn"></param>
/// <param name="products"></param>
public static void InsertProducts(SqlConnection conn, Collection<Product> products)
{
DataTable dtProducts = new DataTable("Product");
dtProducts.Columns.Add("ID", typeof(int));
dtProducts.Columns.Add("Name", typeof(string)); foreach (Product product in products)
{
dtProducts.Rows.Add(
product.ID,
product.Name
);
} SqlParameter tvpProduct = new SqlParameter("@ProductTVP", dtProducts);
tvpProduct.SqlDbType = SqlDbType.Structured;
SqlHelper.ExecuteNonQuery(conn, CommandType.StoredProcedure, "procInsertProductsByProductTVP", tvpProduct);
}
增加产品的存储过程:
/****** Object: StoredProcedure [dbo].[procInsertProductsByProductTVP]******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[procInsertProductsByProductTVP]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[procInsertProductsByProductTVP]
GO Create PROCEDURE [dbo].[procInsertProductsByProductTVP]
(
@ProductTVP ProductTVP READONLY
)
AS INSERT INTO Product (ID, Name)
SELECT
t.ID,
t.Name
FROM @ProductTVP AS t GO
4.使用TVP 更新产品
将ID=2的产品的Name更新为bbb
将ID=6的产品的Name更新为abc
public static void TestUpdateProducts()
{
Collection<Product> products = new Collection<Product>();
Console.WriteLine();
Console.WriteLine("----- Update Products ------");
Console.WriteLine("Product IDs: 2-bbb,6-abc");
products.Add(
new Product()
{
ID = 2,
Name = "bbb"
}); products.Add(
new Product()
{
ID = 6,
Name = "aaa"
}); UpdateProducts(products);
}
更新的方法:
/// <summary>
/// Updates products by the collection of the specific products
/// </summary>
/// <param name="conn"></param>
/// <param name="products"></param>
public static void UpdateProducts(SqlConnection conn, Collection<Product> products)
{
DataTable dtProducts = new DataTable("Product");
dtProducts.Columns.Add("ID", typeof(int));
dtProducts.Columns.Add("Name", typeof(string)); foreach (Product product in products)
{
dtProducts.Rows.Add(
product.ID,
product.Name
);
} SqlParameter tvpProduct = new SqlParameter("@ProductTVP", dtProducts);
tvpProduct.SqlDbType = SqlDbType.Structured;
SqlHelper.ExecuteNonQuery(conn, CommandType.StoredProcedure, "procUpdateProductsByProductTVP", tvpProduct);
}
创建以产品ID和产品Name作为列名的TVP:
IF NOT EXISTS( SELECT * FROM sys.types WHERE name = 'ProductTVP') CREATE TYPE [dbo].[ProductTVP] AS TABLE(
[ID] [int] NULL,
[Name] NVARCHAR(100)
) GO
增加产品的存储过程:
/****** Object: StoredProcedure [dbo].[procUpdateProductsByIDs]******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[procUpdateProductsByProductTVP]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[procUpdateProductsByProductTVP]
GO Create PROCEDURE [dbo].[procUpdateProductsByProductTVP]
(
@ProductTVP ProductTVP READONLY
)
AS Update p
SET
p.ID = t.ID,
p.Name = t.Name
FROM product AS p
INNER JOIN @ProductTVP AS t on p.ID = t.ID 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~~独家赠送[增-删-改-查]的例子的更多相关文章
-
iOS sqlite3 的基本使用(增 删 改 查)
iOS sqlite3 的基本使用(增 删 改 查) 这篇博客不会讲述太多sql语言,目的重在实现sqlite3的一些基本操作. 例:增 删 改 查 如果想了解更多的sql语言可以利用强大的互联网. ...
-
iOS FMDB的使用(增,删,改,查,sqlite存取图片)
iOS FMDB的使用(增,删,改,查,sqlite存取图片) 在上一篇博客我对sqlite的基本使用进行了详细介绍... 但是在实际开发中原生使用的频率是很少的... 这篇博客我将会较全面的介绍FM ...
-
django ajax增 删 改 查
具于django ajax实现增 删 改 查功能 代码示例: 代码: urls.py from django.conf.urls import url from django.contrib impo ...
-
C# ADO.NET (sql语句连接方式)(增,删,改)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...
-
ADO.NET 增 删 改 查
ADO.NET:(数据访问技术)就是将C#和MSSQL连接起来的一个纽带 可以通过ADO.NET将内存中的临时数据写入到数据库中 也可以将数据库中的数据提取到内存*程序调用 ADO.NET所有数据访 ...
-
MVC EF 增 删 改 查
using System;using System.Collections.Generic;using System.Linq;using System.Web;//using System.Data ...
-
python基础中的四大天王-增-删-改-查
列表-list-[] 输入内存储存容器 发生改变通常直接变化,让我们看看下面列子 增---默认在最后添加 #append()--括号中可以是数字,可以是字符串,可以是元祖,可以是集合,可以是字典 #l ...
-
SQL Server T—SQL 语句【建 增 删 改】(建外键)
一 创建数据库 如果多条语句要一起执行,那么在每条语句之后需要加 go 关键字 建库 : create database 数据库名 create database Dat ...
-
网站的增 / 删 / 改 / 查 时常用的 sql 语句
最近在学习数据库 php + mysql 的基本的 crud 的操作,记录碰到的坑供自己参考.crud中需要用到的sql语句还是比较多的,共包括以下几个内容: 查询所有数据 查询表中某个字段 查询并根 ...
随机推荐
-
C/C++ Lua Parsing Engine
catalog . Lua语言简介 . 使用 Lua 编写可嵌入式脚本 . VS2010编译Lua . 嵌入和扩展: C/C++中执行Lua脚本 . 将C++函数导出到Lua引擎中: 在Lua脚本中执 ...
-
HDU 4406 最大费用最大流
题意:现有m门课程需要复习,已知每门课程的基础分和学分,共有n天可以复习,每天分为k个时间段,每个时间段可以复习一门课程,并使这门课程的分数加一,问在不挂科的情况下最高的绩点. 思路:(没做过费用流的 ...
-
try-catch-finally中return的执行情况分析
try-catch-finally中return的执行情况分析: 1.在try中没有异常的情况下try.catch.finally的执行顺序 try --- finally 2.如果try中有异常,执 ...
-
NMON监控工具
工具可将服务器的系统资源耗用情况收集起来并输出一个特定的文件,并可利用 excel 分析工具nmonanalyser进行数据的统计分析.并且,nmon运行不会占用过多的系统资源,通常情况下CPU利用率 ...
-
微信小程序 canvas导出图片模糊
//保存到手机相册save:function () { wx.canvasToTempFilePath({ x: , y: , width: , //导出图片的宽 height: , //导出图片的高 ...
-
PHP7 学习笔记(十七)变量函数 - unset
https://secure.php.net/manual/zh/function.unset.php unset()函数用来清除.销毁变量,不用的变量,可以用unset()将它销毁. 1.unset ...
-
java web中验证码生成的demo
首先创建一个CaptailCode类 package com.xiaoqiang.code; import java.awt.*; import java.awt.font.FontRenderCon ...
-
Day5--Python--字典
字典1.什么是字典 dict. 以{}表示,每一项用逗号隔开,内部元素用key:value的形式来保存数据 {'jj':'林俊杰','jay':'周杰伦'} 查询效率非常高,通过key来查找元素 内部 ...
-
11、可扩展MySQL+12、高可用
11.1.扩展MySQL 静态分片:根据key取hash,然后取模: 动态分片:用一个表来维护key与分片id的关系: 11.2.负载均衡 12. 12.2导致宕机得原因: 35%环境+35%性能+2 ...
-
uva-301-枚举-组合
题意:从A市到B市有n个站点,限制火车上搭乘的乘客数目,每个站点都从有一些乘车的订单,订单信息 从x到y,乘客m人,求解最大的收入是多少 最多7个站,22个订单 选取订单的时候没有顺序问题,所以不是全 ...