sqlserverinsert操作效率(堆表vs聚集索引表)
sqlserverinsert操作效率(堆表vs聚集索引表)
“SQL Server的Insert操作在堆表或者聚集索引表的时候,哪个效率更高?为什么高?”
之前有同事问过我这个问题,为了确保日志库的记录效率,于是我做了简单测试了,首先要先强调几点概念:
堆表:没有聚集索引的表,记录通过IAM页以及PFS 页来确定哪页有空闲空间。
聚集索引表:有聚集索引的表,记录是根据聚集键值所在页的键值逻辑顺序维护的
Demo:如下
分别对堆表和聚集表进行5个并发线程,每个线程各10000次循环插入
1.堆表测试
--1. 创建一张堆表
create table Insert_Test (id int identity, name char(200))
go
堆表Insert用时:34.127秒
2.聚集索引表测试
create table Insert_Test2 (id int identity primary key clustered, name char(200))
go
聚集索引表Insert用时:22.885秒
结果:聚集索引的插入速度比堆表要快10秒以上(个人机器配置不同,时间差异也会高或低,我的本子性能较低)
分析
堆表插入:
每一次insert,总是被插入到表的任意可用空间上,通过IAM找到文件中的哪段区间属于目标表,通过PFS页找出这些区间内的哪些页面有可用空间,如果页面没有可用空间,需要通过GAM页和SGAM页查找将分配的某个表的可用区间。
聚集索引:
由于我的聚集键为自增id列,所以每次插入都将集中在最后一个数据页上。
总体来说:由于堆表插入的行的目标位置没有定义,因此确定在堆表中哪里放置行通常比在有聚集索引的表中放置行的效率低。
聚集索引表Insert的弊端
根据上面分析,聚集索引为自增列时,最后的数据页会成为集中insert的目标页,因此会成为热点,通时,SQL Server 使用闩锁,所以预测大并发insert操作会在最终页产生资源阻塞,实测确实如此:
(200个并发线程,每个线程执行100次insert操作)执行过程中,查看等待资源情况
select
wait_type,
count(*) as num_waiting_tasks,
sum(wait_duration_ms) as total_wait_time_ms
from sys.dm_os_waiting_tasks
where session_id>50
group by wait_type
order by wait_type
和预测情况一样,98个请求在等待闩锁资源。
那么,推断如果使用guid作为主键,插入时会分散各个数据页面,进而将热点页平铺开,这点确实有效果,但是拆分页的成本会相当的高,拆分页也是非常损伤性能的。
继续补充个情况,假如你需要长期大量insert操作,不如采用batch,效果会更快,将上面的脚本改为如下:
declare @i int
set @i = 1
while @i <=10000
begin
if @i %5000 = 0
begin
if (@@TRANCOUNT>0)
begin
COMMIT TRAN
BEGIN TRAN
end
end
insert into Insert_Test2 select 'aaa'
set @i =@i + 1
end
if (@@TRANCOUNT>0)
commit tran
单次执行从原先的8秒降为3秒,有兴趣的朋友可以自己测试
原因简单说下,Insert操作时需要进行预写日志的步骤,每个单独的insert操作都要写一遍ldf文件,这样的性能很低,如果每5000条insert包含在一个事务中后提交,它把很多小的transaction合并成一个大的合适的transaction来减少磁盘写操作,从而获得极大性能提升。
Batch size究竟多大才是最佳的呢?这个取决您的机器,需要你自己测试。
sqlserver中的大数据的批量操作(批量插入,批量删除)
sqlserver中的⼤数据的批量操作(批量插⼊,批量删除)⾸先我们建⽴⼀个测试⽤员⼯表---创建⼀个测试的员⼯表---create table Employee(EmployeeNo int primary key, --员⼯编号EmployeeName nvarchar(50) null, --员⼯名称CreateUser nvarchar(50) null, --创建⼈CreateDate datetime null, --创建时间)执⾏后结果:那么假如我们要批量插⼊10000条数据,应该怎么办?这⾥有四种⽅法(普通循环,事务循环、批量插⼊、cte插⼊)1、普通循环插⼊(while)/**********************************************普通循环(插⼊数据10000,执⾏时间:1283毫秒)********************************************/--开启开关(记录sql语句各个阶段所消耗的时间)---set statistics time on;--声明两个变量---declare@Index int;declare@Timer datetime;--对两个变量进⾏赋值----set@Index=1;set@Timer=GETDATE();--当循环⼩于1000次执⾏添加语句---while@Index<=10000begin--执⾏添加的语句--insert into Employee(EmployeeNo,EmployeeName,CreateUser,CreateDate)values(@Index,'员⼯'+cast(@Index as CHAR(5)),'system',GETDATE())--设置循环次数加1set@Index=@Index+1end--获取执⾏的毫秒数--select DATEDIFF(MS,@Timer,GETDATE()) as'执⾏时间(毫秒)'--关闭开关(记录SQL语句各阶段所消耗的时间)set statistics time off;执⾏普通循环插⼊10000条数据,⼤概需要1200多毫秒,结果如图所⽰2、事务循环插⼊/**********************************************事务循环(插⼊数据1000,执⾏时间:460毫秒)********************************************/--开启事务--begin tran;--开启开关(记录sql语句各个阶段所消耗的时间)---set statistics time on;--声明两个变量---declare@Index int;declare@Timer Datetime;--对两个变量进⾏赋值----set@Index=1;set@Timer=GETDATE();--当循环⼩于1000次执⾏添加语句---while@Index<=10000begin--执⾏添加的语句--insert into Employee(EmployeeNo,EmployeeName,CreateUser,CreateDate)values(@Index,'员⼯'+cast(@Index as CHAR(5)),'system',GETDATE())--设置循环次数加1set@Index=@Index+1end--获取执⾏的毫秒数--select DATEDIFF(MS,@Timer,GETDATE()) as'执⾏时间(毫秒)'set statistics time off;--提交事务--commit;执⾏事务循环插⼊10000条数据,⼤概需要400多毫秒,结果如下所⽰:3、批量插⼊/**********************************************批量插⼊(插⼊数据10000,执⾏时间:33毫秒)********************************************/--开启开关(记录sql语句各个阶段所消耗的时间)--set statistics time on;--声明⼀个时间变量---declare@Timer datetime;---对时间变量进⾏赋值---set@Timer=GETDATE();---执⾏批量操作的sql语句---insert Employee(EmployeeNo,EmployeeName,CreateUser,CreateDate)select top(10000) EmployeeNo=ROW_NUMBER() over( order by c1.[object_id]),'员⼯','system',GETDATE()from sys.columns as c1 cross join sys.columns as c2order by c1.object_id--获取执⾏的毫秒数--select DATEDIFF(MS, @Timer, GETDATE()) AS[执⾏时间(毫秒)];--关闭开关(记录SQL语句各阶段所消耗的时间)--SET STATISTICS TIME OFF;执⾏批量插⼊10000条数据,⼤概只要33毫秒,结果如图所⽰:4、CTE插⼊--/*******************************************--***CTE插⼊(插⼊数据10000,执⾏时间:40毫秒)--********************************************/--开启开关(记录sql语句各个阶段所消耗的时间)--set statistics time on;--声明⼀个时间变量并赋值--declare@Timer datetime=GETDATE();---将要添加10000条语句组合成CTE模块---;with CTE(EmployeeNo,EmployeeName,CreateUser,CreateDate) as (select top(10000) EmployeeNo = ROW_NUMBER() over (order by C1.[OBJECT_ID]), '员⼯', 'system', GETDATE()from SYS.COLUMNS as C1 cross join SYS.COLUMNS as C2order by C1.[OBJECT_ID])--执⾏CTE插⼊语句---insert Employee select EmployeeNo,EmployeeName,CreateUser,CreateDate from CTE;--获取执⾏的毫秒数--select DATEDIFF(MS, @Timer, GETDATE()) as[执⾏时间(毫秒)];---关闭开关(记录sql语句各个阶段所消耗的时间)---set statistics time off;执⾏插⼊10000条数据,⼤概需要40毫秒,结果如图所⽰:最后我们查看⼀下,批量插⼊10000条数据的员⼯表⼩结:1)按执⾏时间,效率依次为:CTE和批量插⼊效率相当,速度最快,事务插⼊次之,单循环插⼊速度最慢;2)单循环插⼊速度最慢是由于INSERT每次都有⽇志,事务插⼊⼤⼤减少了写⼊⽇志次数,批量插⼊只有⼀次⽇志,CTE的基础是CLR,善⽤速度是最快的那么,假如我们要批量删除我们插⼊的数据,怎么办呢?批量删除有3中⽅法(循环删除、批量删除、truncate 删除)1、循环删除--/*******************************************--***循环删除(删除数据10000,执⾏时间:20毫秒)--********************************************/set statistics time on;--声明⼀个时间变量---declare@Timer datetime=GETDATE();--删除语句--delete from Employee--获取执⾏的毫秒数--select DATEDIFF(MS, @Timer, GETDATE()) as[执⾏时间(毫秒)];set statistics time off;删除10000条数据,所需的时间⼤概为20毫秒,如下所⽰:2、批量删除/**********************************************批量删除(删除数据10000,执⾏时间:23毫秒)********************************************/set statistics time on;declare@Timer datetime=GETDATE();SET ROWCOUNT10000;while1=1begin--开启事务--begin tran--执⾏删除--delete from Employee;--提交事务--commit;IF@@ROWCOUNT=0break;endset ROWCOUNT0;--获取执⾏的毫秒数---select DATEDIFF(MS, @Timer, GETDATE()) as[执⾏时间(毫秒)];set statistics time off;删除10000条数据,所需的时间⼤概为23毫秒,如下所⽰:3、truncate删除--/*******************************************--***truncate删除(删除数据10000,执⾏时间:3毫秒)--********************************************/set statistics time on;--声明⼀个时间变量--declare@Timer datetime=getdate();--执⾏truncate语句--truncate table Employee---获取执⾏的毫秒数---select DATEDIFF(MS, @Timer, GETDATE()) as[执⾏时间(毫秒)]set statistics time off删除10000条数据,所需的时间⼤概为3毫秒,如下所⽰:⼩结:1)TRUNCATE太快了,清除10W数据⼀点没压⼒,批量删除次之,最后的DELTE太慢了2)TRUNCATE快是因为它属于DDL语句,只会产⽣极少的⽇志,普通的DELETE不仅会产⽣⽇志,⽽且会锁记录PS:。
SQLServer创建索引(index)
SQLServer创建索引(index)索引的简介:索引分为聚集索引和⾮聚集索引,数据库中的索引类似于⼀本书的⽬录,在⼀本书中通过⽬录可以快速找到你想要的信息,⽽不需要读完全书。
索引主要⽬的是提⾼了SQL Server系统的性能,加快数据的查询速度与减少系统的响应时间。
但是索引对于提⾼查询性能也不是万能的,也不是建⽴越多的索引就越好。
索引建少了,⽤ WHERE ⼦句找数据效率低,不利于查找数据。
索引建多了,不利于新增、修改和删除等操作,因为做这些操作时,SQL SERVER 除了要更新数据表本⾝,还要连带⽴即更新所有的相关索引,⽽且过多的索引也会浪费硬盘空间。
索引的分类:索引就类似于中⽂字典前⾯的⽬录,按照拼⾳或部⾸都可以很快的定位到所要查找的字。
唯⼀索引(UNIQUE):每⼀⾏的索引值都是唯⼀的(创建了唯⼀约束,系统将⾃动创建唯⼀索引)主键索引:当创建表时指定的主键列,会⾃动创建主键索引,并且拥有唯⼀的特性。
聚集索引(CLUSTERED):聚集索引就相当于使⽤字典的拼⾳查找,因为聚集索引存储记录是物理上连续存在的,即拼⾳ a 过了后⾯肯定是 b ⼀样。
⾮聚集索引(NONCLUSTERED):⾮聚集索引就相当于使⽤字典的部⾸查找,⾮聚集索引是逻辑上的连续,物理存储并不连续。
PS:聚集索引⼀个表只能有⼀个,⽽⾮聚集索引⼀个表可以存在多个。
什么情况下使⽤索引:语法:CREATE [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ] INDEX index_nameON <object> ( column_name [ ASC | DESC ] [ ,...n ] )[ WITH <backward_compatible_index_option> [ ,...n ] ][ ON { filegroup_name | "default" } ]<object> ::={[ database_name. [ owner_name ] . | owner_name. ]table_or_view_name}<backward_compatible_index_option> ::={PAD_INDEX| FILLFACTOR = fillfactor| SORT_IN_TEMPDB| IGNORE_DUP_KEY| STATISTICS_NORECOMPUTE| DROP_EXISTING}参数:UNIQUE:为表或视图创建唯⼀索引。
SQLServer-索引详细教程(聚集索引,非聚集索引)
SQLServer-索引详细教程(聚集索引,⾮聚集索引)作者:(⼀)必读:深⼊浅出理解索引结构实际上,您可以把索引理解为⼀种特殊的⽬录。
微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和⾮聚集索引(nonclustered index,也称⾮聚类索引、⾮簇集索引)。
下⾯,我们举例来说明⼀下聚集索引和⾮聚集索引的区别:其实,我们的汉语字典的正⽂本⾝就是⼀个聚集索引。
⽐如,我们要查“安”字,就会很⾃然地翻开字典的前⼏页,因为“安”的拼⾳是“an”,⽽按照拼⾳排序汉字的字典是以英⽂字母“a”开头并以“z”结尾的,那么“安”字就⾃然地排在字典的前部。
如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;同样的,如果查“张”字,那您也会将您的字典翻到最后部分,因为“张”的拼⾳是“zhang”。
也就是说,字典的正⽂部分本⾝就是⼀个⽬录,您不需要再去查其他⽬录来找到您需要找的内容。
我们把这种正⽂内容本⾝就是⼀种按照⼀定规则排列的⽬录称为“聚集索引”。
如果您认识某个字,您可以快速地从⾃动中查到这个字。
但您也可能会遇到您不认识的字,不知道它的发⾳,这时候,您就不能按照刚才的⽅法找到您要查的字,⽽需要去根据“偏旁部⾸”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。
但您结合“部⾸⽬录”和“检字表”⽽查到的字的排序并不是真正的正⽂的排序⽅法,⽐如您查“张”字,我们可以看到在查部⾸之后的检字表中“张”的页码是672页,检字表中“张”的上⾯是“驰”字,但页码却是63页,“张”的下⾯是“弩”字,页⾯是390页。
很显然,这些字并不是真正的分别位于“张”字的上下⽅,现在您看到的连续的“驰、张、弩”三字实际上就是他们在⾮聚集索引中的排序,是字典正⽂中的字在⾮聚集索引中的映射。
我们可以通过这种⽅式来找到您所需要的字,但它需要两个过程,先找到⽬录中的结果,然后再翻到您所需要的页码。
sqlserver中索引失效的集中情况
sqlserver中索引失效的集中情况原⽂出处改善SQL语句 很多⼈不知道SQL语句在sql server中是如何执⾏的,他们担⼼⾃⼰所写的SQL语句会被SQL SERVER误解。
⽐如:select * from table1 where name=''zhangsan'' and tID > 10000和执⾏:select * from table1 where tID > 10000 and name=''zhangsan'' ⼀些⼈不知道以上两条语句的执⾏效率是否⼀样,因为如果简单的从语句先后上看,这两个语句的确是不⼀样,如果tID是⼀个聚合索引,那么后⼀句仅仅从表的10000条以后的记录中查找就⾏了;⽽前⼀句则要先从全表中查找看有⼏个name=''zhangsan''的,⽽后再根据限制条件条件tID>10000来提出查询结果。
事实上,这样的担⼼是不必要的。
sql server中有⼀个“查询分析优化器”,它可以计算出where⼦句中的搜索条件并确定哪个索引能缩⼩表扫描的搜索空间,也就是说,它能实现⾃动优化。
虽然查询优化器可以根据where⼦句⾃动的进⾏查询优化,但⼤家仍然有必要了解⼀下“查询优化器”的⼯作原理,如⾮这样,有时查询优化器就会不按照您的本意进⾏快速查询。
在查询分析阶段,查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否有⽤。
如果⼀个阶段可以被⽤作⼀个扫描参数(SARG),那么就称之为可优化的,并且可以利⽤索引快速获得所需数据。
SARG的定义:⽤于限制搜索的⼀个操作,因为它通常是指⼀个特定的匹配,⼀个值得范围内的匹配或者两个以上条件的AND连接。
形式如下:列名操作符 <常数或变量>或<常数或变量> 操作符列名列名可以出现在操作符的⼀边,⽽常数或变量出现在操作符的另⼀边。
SQL Server 关键字大全概述
SQL Server 关键字ADD ALTER TABLE(修改表)语句的一个选项,为现有的表添加一个新列。
ALL SELECT(选择,查询(SQL))语句的一个选项,用于SELECT列表中,与UNION操作符和GROUP BY子句一起使用。
在所有这些子句中,ALL选项指定重复行可以出现在结果集中。
ALTER ALTER object(改变对象)语句是Transact-SQL数据定义语言的一部分,修改几个数据库对象的属性。
有5个ALTER对象语句:ALTER DATABASE,ALTER TABLE,ALTERVIEW,ALTER TRIGGER和ALTER PROCEDURE。
AND布尔操作符。
如果AND操作符连接两个条件,检索两个条件都为真的行。
ANY用于SELECT语句的比较操作符。
如果一个内查询的结果含有至少一行满足这个比较,ANY操作符计算的结果为真。
AS用于定义列表达式的相关名字,如SUM(budget) ASsum_of_budgets。
ASC ASCENDING的简写形式,用于SELECT语句的ORDER BY子句中定义升序排序。
AUTHORIZATION CREATE SCHEMA语句的一个子句,该子句定义模式对象所有者的ID。
这个标识符必须是数据库中合法的用户帐号。
AVG AVERAGE的简写形式。
聚集函数AVG计算列中值的平均值,该函数的参数必须是数字。
BACKUP备份数据库、事务日志或文件组中的一个或多个文件。
对应的Transact-SQL语句是BACKUP DATABASE和BACKUP LOG。
BEGIN如果在BEGIN匛ND形式中使用,开始一个Transact-SQL事务。
BEGIN TRANSACTION 语句开始一个事务。
BETWEEN与SELECT语句一起使用的一个操作符,这个操作符用于搜索指定范围的所有值。
BREAK BREAK语句停止块内的语句的执行,并开始这个块后的语句的执行。
针对Sqlserver大数据量插入速度慢或丢失数据的解决方法
针对Sqlserver⼤数据量插⼊速度慢或丢失数据的解决⽅法我的设备上每秒将2000条数据插⼊数据库,2个设备总共4000条,当在程序⾥⾯直接⽤insert语句插⼊时,两个设备同时插⼊⼤概总共能插⼊约2800条左右,数据丢失约1200条左右,测试了很多⽅法,整理出了两种效果⽐较明显的解决办法:⽅法⼀:使⽤Sql Server函数:1.将数据组合成字串,使⽤函数将数据插⼊内存表,后将内存表数据复制到要插⼊的表。
2.组合成的字符换格式:'111|222|333|456,7894,7458|0|1|2014-01-01 12:15:16;1111|2222|3333|456,7894,7458|0|1|2014-01-01 12:15:16',每⾏数据中间⽤“;”隔开,每个字段之间⽤“|”隔开。
3.编写函数:CREATE FUNCTION [dbo].[fun_funcname](@str VARCHAR(max),@splitchar CHAR(1),@splitchar2 CHAR(1))--定义返回表RETURNS @t TABLE(MaxValue float,Phase int,SlopeValue float,Data varchar(600),Alarm int,AlmLev int,GpsTime datetime,UpdateTime datetime) AS/*author:hejun licreate date:2014-06-09*/BEGINDECLARE @substr VARCHAR(max),@substr2 VARCHAR(max)--申明单个接收值declare @MaxValue float,@Phase int,@SlopeValue float,@Data varchar(8000),@Alarm int,@AlmLev int,@GpsTime datetimeSET @substr=@strDECLARE @i INT,@j INT,@ii INT,@jj INT,@ijj1 int,@ijj2 int,@m int,@mm intSET @j=LEN(REPLACE(@str,@splitchar,REPLICATE(@splitchar,2)))-LEN(@str)--获取分割符个数IF @j=0BEGIN--INSERT INTO @t VALUES (@substr,1) --没有分割符则插⼊整个字串set @substr2=@substr;set @ii=0SET @jj=LEN(REPLACE(@substr2,@splitchar2,REPLICATE(@splitchar2,2)))-LEN(@substr2)--获取分割符个数WHILE @ii<=@jjBEGINif(@ii<@jj)beginSET @mm=CHARINDEX(@splitchar2,@substr2)-1 --获取分割符的前⼀位置if(@ii=0)set @MaxValue=cast(LEFT(@substr2,@mm) as float)else if(@ii=1)set @Phase=cast(LEFT(@substr2,@mm) as int)else if(@ii=2)set @SlopeValue=cast(LEFT(@substr2,@mm) as float)else if(@ii=3)set @Data=cast(LEFT(@substr2,@mm) as varchar)else if(@ii=4)set @Alarm=cast(LEFT(@substr2,@mm) as int)else if(@ii=5)set @AlmLev=cast(LEFT(@substr2,@mm) as int)else if(@ii=6)INSERT INTO @t VALUES(@MaxValue,@Phase,@SlopeValue,''+@Data+'',@Alarm,@AlmLev,cast(@substr2 as datetime),GETDATE())SET @substr2=RIGHT(@substr2,LEN(@substr2)-(@mm+1)) --去除已获取的分割串,得到还需要继续分割的字符串endelseBEGIN--当循环到最后⼀个值时将数据插⼊表INSERT INTO @t VALUES(@MaxValue,@Phase,@SlopeValue,''+@Data+'',@Alarm,@AlmLev,cast(@substr2 as datetime),GETDATE())END--ENDSET @ii=@ii+1ENDENDELSEBEGINSET @i=0WHILE @i<=@jBEGINIF(@i<@j)BEGINSET @m=CHARINDEX(@splitchar,@substr)-1 --获取分割符的前⼀位置--INSERT INTO @t VALUES(LEFT(@substr,@m),@i+1)-----⼆次循环开始--1.线获取要⼆次截取的字串set @substr2=(LEFT(@substr,@m));--2.初始化⼆次截取的起始位置set @ii=0--3.获取分隔符个数SET @jj=LEN(REPLACE(@substr2,@splitchar2,REPLICATE(@splitchar2,2)))-LEN(@substr2)--获取分割符个数WHILE @ii<=@jjBEGINif(@ii<@jj)beginSET @mm=CHARINDEX(@splitchar2,@substr2)-1 --获取分割符的前⼀位置if(@ii=0)set @MaxValue=cast(LEFT(@substr2,@mm) as float)else if(@ii=1)set @Phase=cast(LEFT(@substr2,@mm) as int)else if(@ii=2)set @SlopeValue=cast(LEFT(@substr2,@mm) as float)else if(@ii=3)set @Data=cast(LEFT(@substr2,@mm) as varchar)else if(@ii=4)set @Alarm=cast(LEFT(@substr2,@mm) as int)else if(@ii=5)set @AlmLev=cast(LEFT(@substr2,@mm) as int)else if(@ii=6)INSERT INTO @t VALUES(@MaxValue,@Phase,@SlopeValue,''+@Data+'',@Alarm,@AlmLev,cast(@substr2 as datetime),GETDATE())SET @substr2=RIGHT(@substr2,LEN(@substr2)-(@mm+1)) --去除已获取的分割串,得到还需要继续分割的字符串endelseBEGIN--当循环到最后⼀个值时将数据插⼊表INSERT INTO @t VALUES(@MaxValue,@Phase,@SlopeValue,''+@Data+'',@Alarm,@AlmLev,cast(@substr2 as datetime),GETDATE())END--ENDSET @ii=@ii+1END-----⼆次循环结束SET @substr=RIGHT(@substr,LEN(@substr)-(@m+1)) --去除已获取的分割串,得到还需要继续分割的字符串ENDELSEBEGIN--INSERT INTO @t VALUES(@substr,@i+1)--对最后⼀个被分割的串进⾏单独处理-----⼆次循环开始--1.线获取要⼆次截取的字串set @substr2=@substr;--2.初始化⼆次截取的起始位置set @ii=0--3.获取分隔符个数SET @jj=LEN(REPLACE(@substr2,@splitchar2,REPLICATE(@splitchar2,2)))-LEN(@substr2)--获取分割符个数WHILE @ii<=@jjBEGINif(@ii<@jj)beginSET @mm=CHARINDEX(@splitchar2,@substr2)-1 --获取分割符的前⼀位置if(@ii=0)set @MaxValue=cast(LEFT(@substr2,@mm) as float)else if(@ii=1)set @Phase=cast(LEFT(@substr2,@mm) as int)else if(@ii=2)set @SlopeValue=cast(LEFT(@substr2,@mm) as float)else if(@ii=3)set @Data=cast(LEFT(@substr2,@mm) as varchar)else if(@ii=4)set @Alarm=cast(LEFT(@substr2,@mm) as int)else if(@ii=5)set @AlmLev=cast(LEFT(@substr2,@mm) as int)else if(@ii=6)INSERT INTO @t VALUES(@MaxValue,@Phase,@SlopeValue,''+@Data+'',@Alarm,@AlmLev,cast(@substr2 as datetime),GETDATE())SET @substr2=RIGHT(@substr2,LEN(@substr2)-(@mm+1)) --去除已获取的分割串,得到还需要继续分割的字符串endelseBEGIN--当循环到最后⼀个值时将数据插⼊表INSERT INTO @t VALUES(@MaxValue,@Phase,@SlopeValue,''+@Data+'',@Alarm,@AlmLev,cast(@substr2 as datetime),GETDATE())ENDSET @ii=@ii+1END-----⼆次循环结束ENDSET @i=@i+1ENDENDRETURNEND4.调⽤函数语句:insert into [mytable] select * from [dbo].[fun_funcname]('111|222|333|456,7894,7458|0|1|2014-01-01 12:15:16;1111|2222|3333|456,7894,7458|0|1|2014-01-01 12:15:16',';','|');5.结果展⽰:select * from [mytable] ;⽅法⼆:使⽤BULK INSERT⼤数据量插⼊第⼀种操作,使⽤Bulk将⽂件数据插⼊数据库Sql代码创建数据库CREATE DATABASE [db_mgr]GO创建测试表USE db_mgrCREATE TABLE dbo.T_Student(F_ID [int] IDENTITY(1,1) NOT NULL,F_Code varchar(10) ,F_Name varchar(100) ,F_Memo nvarchar(500) ,F_Memo2 ntext ,PRIMARY KEY (F_ID))GO填充测试数据Insert Into T_Student(F_Code, F_Name, F_Memo, F_Memo2) select'code001', 'name001', 'memo001', '备注' union all select'code002', 'name002', 'memo002', '备注' union all select'code003', 'name003', 'memo003', '备注' union all select'code004', 'name004', 'memo004', '备注' union all select'code005', 'name005', 'memo005', '备注' union all select'code006', 'name006', 'memo006', '备注'开启xp_cmdshell存储过程(开启后有安全隐患)EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;EXEC sp_configure 'show advanced options', 0;RECONFIGURE;使⽤bcp导出格式⽂件:EXEC master..xp_cmdshell 'BCP db_mgr.dbo.T_Student format nul -f C:/student_fmt.xml -x -c -T'使⽤bcp导出数据⽂件:EXEC master..xp_cmdshell 'BCP db_mgr.dbo.T_Student out C:/student.data -f C:/student_fmt.xml -T'将表中数据清空truncate table db_mgr.dbo.T_Student使⽤Bulk Insert语句批量导⼊数据⽂件:BULK INSERT db_mgr.dbo.T_StudentFROM 'C:/student.data'WITH(FORMATFILE = 'C:/student_fmt.xml')使⽤OPENROWSET(BULK)的例⼦:T_Student表必须已存在INSERT INTO db_mgr.dbo.T_Student(F_Code, F_Name) SELECT F_Code, F_NameFROM OPENROWSET(BULK N'C:/student.data', FORMATFILE=N'C:/student_fmt.xml') AS new_table_name 使⽤OPENROWSET(BULK)的例⼦:tt表可以不存在SELECT F_Code, F_Name INTO db_mgr.dbo.ttFROM OPENROWSET(BULK N'C:/student.data', FORMATFILE=N'C:/student_fmt.xml') AS new_table_name。
如何使用 SQL Server 执行计划,看懂 SQL Server 执行计划,SQL Server 执行计划动作含义,理解SQL执行计划
【如何使用SQL Server 执行计划】【看懂SQL Server 执行计划】【SQL Server 执行计划各个动作含义】【理解SQL Server 执行计划的意思】要理解执行计划,怎么也得先理解,那各种各样的名词吧。
鉴于自己还不是很了解。
本文打算作为只写懂的,不懂的懂了才写。
在开头要先说明,第一次看执行计划要注意,SQL Server的执行计划是从右向左看的。
名词解析:扫描:逐行遍历数据。
先建立一张表,并给大家看看大概是什么样子的。
CREATE TABLE Person(Id int IDENTITY(1,1) NOT NULL,Name nvarchar(50) NULL,Age int NULL,Height int NULL,Area nvarchar(50) NULL,MarryHistory nvarchar(10) NULL,EducationalBackground nvarchar(10) NULL,Address nvarchar(50) NULL,InSiteId int NULL) ON[PRIMARY]表中的数据14万左右,大概类似下面这样:此表,暂时没有任何索引。
一、数据访问操作1、表扫描表扫描:发生于堆表,并且没有可用的索引可用时,会发生表扫描,表示整个表扫描一次。
现在,我们来对此表执行一条简单的查询语句:SELECT*From Person WHERE Name ='公子'查看执行计划如下:表扫描,顾名思义就是整张表扫描,找到你所需要的数据了。
2、聚集索引扫描聚集索引扫描:发生于聚集表,也相当于全表扫描操作,但在针对聚集列的条件如(WHERE Id > 10)等操作时,效率会较好。
下面我们在Id列来对此表加上一个聚集索引CREATE CLUSTERED INDEX IX_Id ON Person(Id)再次执行同样的查询语句:SELECT*From Person WHERE Name ='公子'执行计划如下:为什么建的聚集索引在Id列,会对扫描有影响呢?更何况与Name条件也没关系啊?其实,你加了聚集索引之后,表就由堆表变成了聚集表。
sqlserver数据库 提高效率方法
SQL Server 数据库是一种常见的关系型数据库管理系统,它被广泛应用于企业级应用程序和数据管理系统中。
然而,随着数据库规模的增大和日常操作的复杂性增加,数据库的性能和效率往往成为关注的焦点。
提高SQL Server数据库的效率不仅可以显著改善系统的响应速度和稳定性,也可以节约资源和降低成本。
本文将介绍一些提高SQL Server 数据库效率的方法,帮助管理员和开发人员更好地管理和优化数据库系统。
1. 使用合适的索引索引是数据库中用来加快对表中数据的访问速度的结构,它可以通过创建索引来优化查询的性能。
在SQL Server中,通过对经常进行搜索,排序和过滤的数据列创建合适的索引,可以显著提高查询性能。
定期对索引进行维护和优化也是提高数据库效率的关键步骤。
2. 优化查询语句优化SQL查询语句对于提高数据库效率至关重要。
在编写查询语句时,应避免使用全表扫描,尽量减少数据量,避免使用不必要的连接和子查询,合理使用排序和分组等操作,以及避免使用模糊查询和通配符查询等低效操作。
3. 定期备份和恢复定期备份数据库是保障数据库安全的重要手段,同时备份还能够减少数据库维护的风险。
在备份时,管理员应该选择合适的备份策略,并对备份文件进行存储和管理,以确保数据库在出现故障或灾难时能够快速恢复。
4. 使用存储过程和触发器存储过程和触发器是SQL Server中重要的数据库对象,它们可以提高数据库的安全性和可维护性,同时还能减少网络流量和客户端执行开销,提高数据库的效率。
在编写存储过程和触发器时,应遵循一些最佳实践,如避免多次嵌套存储过程和触发器,减少对数据库的锁定和阻塞。
5. 使用物理分区技术SQL Server支持对数据表进行物理分区,这可以帮助管理员更好地管理数据,并根据需求对数据进行调优。
通过物理分区,可以提高查询和数据加载的性能,同时也方便了数据备份和恢复。
总结通过上述方法,可以显著提高SQL Server数据库的性能和效率,使其能够更好地满足企业应用程序和数据管理系统的需求。
当SqlServer数据量很大时,如何优化表格能加快处理速度
当SqlServer数据量很大时,如何优化表格能加快处理速度表设计和查询的一些参考1.合理使用索引索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。
现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。
索引的使用要恰到好处,其使用原则如下:●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。
比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。
如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。
如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。
在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。
另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序应当简化或避免对大型表进行重复的排序。
当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。
以下是一些影响因素:●索引中不包括一个或几个待排序的列;●group by或order by子句中列的次序与索引的次序不一样;●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。
如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。
比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。
sqlserver数据库导出表里所有数据成insert语句
sqlserver数据库导出表⾥所有数据成insert语句有时候,我们想把数据库的某张表⾥的所有数据导⼊到另外⼀个数据库或另外⼀台计算机上的数据库,对于sql server有这样的⼀种⽅法下⾯我以sql server 2008 R2,数据库是Northwind数据库为例,⽬标:把Northwind数据库的Orders表导出成insert语句。
第⼀步:选择Northwind数据库,右键-任务-⽣成脚本:第⼆步:在弹出的“⽣成和发布脚本”的简介窗⼝,按“下⼀步”按钮:第三步:在“选择对象”窗⼝,选中“选择特定数据库对象”,展开表,勾选要⽣成insert语句的表,我这⾥选的是order表,按“下⼀步”按钮:第四步:在弹出的“设置脚本编写选项”窗⼝,按“⾼级”按钮,在弹出的“⾼级脚本编写选项”中下拉下拉条⾄底部,设置“要编写脚本的数据类型”为“仅限数据”(“仅限数据”是只导出数据为insert语句,如果是导出表结构的话选择“仅限架构”,选择“架构和数据”则架构和insert语句都⽣成),按“确定”按钮:第五步:在“设置脚本编写选项”窗⼝,“指定如何保存或发布脚本”的“输出类型”选中“将脚本保存到特定位置”,勾选“保存到⽂件”,则可以指定⼀个保存的路径,保存为.sql⽂件,勾选“保存到新建查询窗⼝”,则会新打开⼀个查询窗⼝,把所有insert 语句放到新查询窗⼝:第六步:在“设置脚本编写选项”窗⼝,按“下⼀步”按钮,弹出:第七步:在“摘要”窗⼝,按“下⼀步”按钮:第⼋步:在“保存或发布脚本”窗⼝,按“完成”按钮:最终会⾃动新建⼀个查询窗⼝,order表⾥的所有数据都转换成insert语句。
=====================================================================================将表⾥的数据批量⽣成INSERT语句的存储过程增强版有时候,我们需要将某个表⾥的数据全部或者根据查询条件导出来,迁移到另⼀个相同结构的库中⽬前SQL Server⾥⾯是没有相关的⼯具根据查询条件来⽣成INSERT语句的,只有借助第三⽅⼯具(third party tools)这种脚本⽹上也有很多,但是⽹上的脚本还是⽋缺⼀些规范和功能,例如:我只想导出特定查询条件的数据,⽹上的脚本都是导出全表数据如果表很⼤,对性能会有很⼤影响这⾥有⼀个存储过程(适⽤于SQLServer2005 或以上版本)-- =============================================-- Author: <桦仔>-- Blog: </lyhabc/>-- Create date: <2014/10/18>-- Description: <根据查询条件导出表数据的insert脚本>-- =============================================CREATE PROCEDURE[InsertGenerator](@tableName NVARCHAR(100),-- the table name@whereClause NVARCHAR(MAX),--col1=1@includeIdentity INT-- include identity column(1:yes,0:no))AS--of an INSERT DML statement.DECLARE@string NVARCHAR(MAX) --for storing the first half of INSERT statementDECLARE@stringData NVARCHAR(MAX) --for storing the data (VALUES) related statementDECLARE@dataType NVARCHAR(20) --data types returned for respective columnsDECLARE@schemaName NVARCHAR(20) --schema name returned from sys.schemasDECLARE@schemaNameCount int--shema countDECLARE@QueryString NVARCHAR(MAX) -- provide for the whole query,DECLARE@identity INT--identity column(1:yes,0:no)set@QueryString=''--如果有多个schema,选择其中⼀个schemaSELECT@schemaNameCount=COUNT(*)FROM sys.tables tINNER JOIN sys.schemas s ON t.schema_id = s.schema_idWHERE =@tableNameWHILE(@schemaNameCount>0)BEGIN--如果有多个schema,依次指定select@schemaName= namefrom(SELECT ROW_NUMBER() over(order by s.schema_id) RowID,FROM sys.tables tINNER JOIN sys.schemas s ON t.schema_id = s.schema_idWHERE =@tableName) as vwhere RowID=@schemaNameCount--Declare a cursor to retrieve column specific information--for the specified tableDECLARE cursCol CURSOR FAST_FORWARDFORSELECTclmns.[name]AS[column_name],usrt.[name]AS[data_type],CAST(COLUMNPROPERTY(clmns.id, clmns.[name], N'IsIdentity') AS int) AS[Identity]FROM dbo.sysobjects AS tbl WITH (NOLOCK)INNER JOIN dbo.syscolumns AS clmns WITH (NOLOCK) ON clmns.id=tbl.idLEFT JOIN dbo.systypes AS usrt WITH (NOLOCK) ON usrt.xusertype = clmns.xusertypeLEFT JOIN dbo.sysusers AS sclmns WITH (NOLOCK) ON sclmns.uid = usrt.uidLEFT JOIN dbo.systypes AS baset WITH (NOLOCK) ON baset.xusertype = clmns.xtype and baset.xusertype = baset.xtype LEFT JOIN dbo.syscomments AS defaults WITH (NOLOCK) ON defaults.id = clmns.cdefaultLEFT JOIN dbo.syscomments AS cdef WITH (NOLOCK) ON cdef.id = clmns.id AND cdef.number= clmns.colidWHERE (tbl.[type]='U') AND (tbl.[name]=@tableName AND SCHEMA_NAME(tbl.uid)=@schemaName) AND CAST(COLUMNPROPERTY(clmns.id, clmns.[name], N'IsIdentity') AS int)=@includeIdentity ORDER BY tbl.[name], clmns.colorderOPEN cursColSET@string='INSERT INTO ['+@schemaName+'].['+@tableName+']('SET@stringData=''DECLARE@colName NVARCHAR(500)FETCH NEXT FROM cursCol INTO@colName, @dataType,@identityPRINT@schemaNamePRINT@colNameIF@@fetch_status<>0BEGINPRINT'Table '+@tableName+' not found, processing skipped.'CLOSE curscolDEALLOCATE curscolRETURNENDWHILE@@FETCH_STATUS=0BEGINIF@dataType IN ( 'varchar', 'char', 'nchar', 'nvarchar' )BEGINSET@stringData=@stringData+'''''''''+isnull('+@colName+','''')+'''''',''+'ENDELSEIF@dataType IN ( 'text', 'ntext' ) --if the datatype--is text or something elseBEGINSET@stringData=@stringData+'''''''''+isnull(cast('+@colName+' as nvarchar(max)),'''')+'''''',''+'END--from varchar implicitlyBEGINSET@stringData=@stringData+'''convert(money,''''''+isnull(cast('+@colName+' as nvarchar(max)),''0.0000'')+''''''),''+'ENDELSEIF@dataType='datetime'BEGIN-- SET @stringData = @stringData-- + '''convert(datetime,''''''+--isnull(cast(' + @colName + ' as nvarchar(max)),''null'')+''''''),''+'SET@stringData=@stringData+'COALESCE(''''''''+CONVERT(varchar(max),'+@colName+',120)+'''''''',''NULL'')+'',''+'ENDELSEIF@dataType='image'BEGINSET@stringData=@stringData+'''''''''+isnull(cast(convert(varbinary,'+@colName+')as varchar(6)),''0'')+'''''',''+'ENDELSE--presuming the data type is int,bit,numeric,decimalBEGINSET@stringData=@stringData+'''''''''+isnull(cast('+@colName+' as nvarchar(max)),''0'')+'''''',''+'ENDSET@string=@string+'['+@colName+']'+','FETCH NEXT FROM cursCol INTO@colName, @dataType,@identityEND--After both of the clauses are built, the VALUES clause contains a trailing comma which needs to be replaced with a single quote. The prefixed clause will only face removal of the trailing comma. DECLARE@Query NVARCHAR(MAX) -- provide for the whole query,-- you may increase the sizePRINT@whereClauseIF ( @whereClause IS NOT NULLAND@whereClause<>'')BEGINPRINT'stringData:'+@stringDataSET@query='SELECT '''+SUBSTRING(@string, 0, LEN(@string))+') VALUES(''+ '+SUBSTRING(@stringData, 0,LEN(@stringData) -2)+'''+'')''FROM '+@schemaName+'.'+@tableName+' WHERE '+@whereClausePRINT@query-- EXEC sp_executesql @query --load and run the built query--Eventually, close and de-allocate the cursor created for columns information.ENDELSEBEGINSET@query='SELECT '''+SUBSTRING(@string, 0, LEN(@string))+') VALUES(''+ '+SUBSTRING(@stringData, 0,LEN(@stringData) -2)+'''+'')''FROM '+@schemaName+'.'+@tableNameENDCLOSE cursColDEALLOCATE cursColSET@schemaNameCount=@schemaNameCount-1IF(@schemaNameCount=0)BEGINSET@QueryString=@QueryString+@queryENDELSEBEGINSET@QueryString=@QueryString+@query+' UNION ALL 'END--SET @QueryString=REPLACE(@QueryString,'convert(datetime,''NULL'')',NULL)PRINT CONVERT(VARCHAR(MAX),@schemaNameCount)+'---'+@QueryStringEND--PRINT @QueryStringEXEC sp_executesql @QueryString--load and run the built query--Eventually, close and de-allocate the cursor created for columns information.这⾥要声明⼀下,如果你有多个schema,并且每个schema下⾯都有同⼀张表,那么脚本只会⽣成其中⼀个schema下⾯的表insert脚本⽐如我现在有三个schema,下⾯都有customer这个表CREATE SCHEMA testCREATE TABLE test.[customer](city int,region int)CREATE SCHEMA test1CREATE TABLE test1.[customer](city int,region int)在执⾏脚本的时候他只会⽣成dbo这个schema下⾯的表insert脚本INSERT INTO[dbo].[customer]([city],[region]) VALUES('1','2')这个脚本有⼀个缺陷⽆论你的表的字段是什麽数据类型,导出来的时候只能是字符表结构CREATE TABLE[dbo].[customer](city int,region int)导出来的insert脚本INSERT INTO[dbo].[customer]([city],[region]) VALUES('1','2')我这⾥演⽰⼀下怎麽⽤有两种⽅式1、导全表数据InsertGenerator 'customer', null或InsertGenerator 'customer', ''2、根据查询条件导数据InsertGenerator 'customer', 'city=3'或者InsertGenerator 'customer', 'city=3 and region=8'点击⼀下,选择全部然后复制新建⼀个查询窗⼝,然后粘贴其实SQLServer的技巧有很多最后,⼤家可以看⼀下代码,⾮常简单,如果要⽀持SQLServer2000,只要改⼀下代码就可以了补充:创建⼀张测试表CREATE TABLE testinsert (id INT,name VARCHAR(100),cash MONEY,dtime DATETIME) INSERT INTO[dbo].[testinsert]( [id], [name], [cash], [dtime] )VALUES ( 1, -- id - int'nihao', -- name - varchar(100)8.8, -- cash - moneyGETDATE() -- dtime - datetime)SELECT*FROM[dbo].[testinsert]测试InsertGenerator 'testinsert' ,''InsertGenerator 'testinsert' ,'name=''nihao'''InsertGenerator 'testinsert' ,'name=''nihao'' and cash=8.8'datetime类型会有⼀些问题⽣成的结果会⾃动帮你转换INSERT INTO[dbo].[testinsert]([id],[name],[cash],[dtime]) VALUES('1','nihao',convert(money,'8.80'),convert(datetime,'02 8 2015 5:17PM'))群⾥的⼈共享的另⼀个脚本IF OBJECT_ID('spGenInsertSQL','P') IS NOT NULLDROP PROC spGenInsertSQLGOCREATE proc spGenInsertSQL (@tablename varchar(256),@number BIGINT,@whereClause NVARCHAR(MAX))asbegindeclare@sql varchar(8000)declare@sqlValues varchar(8000)set@sql=' ('set@sqlValues='values (''+'select@sqlValues=@sqlValues+ cols +' + '','' + ' ,@sql=@sql+'['+ name +'],'from(select casewhen xtype in (48,52,56,59,60,62,104,106,108,122,127)then'case when '+ name +' is null then ''NULL'' else '+'cast('+ name +' as varchar)'+' end'when xtype in (58,61,40,41,42)then'case when '+ name +' is null then ''NULL'' else '+''''''''' + '+'cast('+ name +' as varchar)'+'+'''''''''+' end'when xtype in (167)then'case when '+ name +' is null then ''NULL'' else '+''''''''' + '+'replace('+ name+','''''''','''''''''''')'+'+'''''''''+' end'when xtype in (231)then'case when '+ name +' is null then ''NULL'' else '+'''N'''''' + '+'replace('+ name+','''''''','''''''''''')'+'+'''''''''+' end'when xtype in (175)then'case when '+ name +' is null then ''NULL'' else '+''''''''' + '+'cast(replace('+ name+','''''''','''''''''''') as Char('+cast(length as varchar) +'))+'''''''''+' end' when xtype in (239)then'case when '+ name +' is null then ''NULL'' else '+'''N'''''' + '+'cast(replace('+ name+','''''''','''''''''''') as Char('+cast(length as varchar) +'))+'''''''''+' end' else'''NULL'''end as Cols,namefrom syscolumnswhere id =object_id(@tablename)) TIF (@number!=0AND@number IS NOT NULL)BEGINset@sql='select top '+CAST(@number AS VARCHAR(6000))+'''INSERT INTO ['+@tablename+']'+left(@sql,len(@sql)-1)+') '+left(@sqlValues,len(@sqlValues)-4) +')'' from '+@tablename print@sqlENDELSEBEGINset@sql='select ''INSERT INTO ['+@tablename+']'+left(@sql,len(@sql)-1)+') '+left(@sqlValues,len(@sqlValues)-4) +')'' from '+@tablenameprint@sqlENDPRINT@whereClauseIF ( @whereClause IS NOT NULL AND@whereClause<>'')BEGINset@sql=@sql+' where '+@whereClauseprint@sqlENDexec (@sql)endGOView Code调⽤⽰例--⾮dbo默认架构需注意--⽀持数据类型:bigint,int, bit,char,datetime,date,time,decimal,money, nvarchar(50),tinyint, nvarchar(max),varchar(max),datetime2--调⽤⽰例如果top⾏或者where条件为空,只需要把参数填上nullspGenInsertSQL 'customer'--表名, 2--top ⾏数, 'city=3 and didian=''⼤连'''--where 条件--导出全表 where条件为空spGenInsertSQL 'customer'--表名, null--top ⾏数,null--where 条件INSERT INTO[Department] ([DepartmentID],[Name],[GroupName],[Company],[ModifiedDate]) values (1,N'售后部',N'销售组',N'中国你好有限公司XX分公司','05 5 2015 5:58PM') INSERT INTO[Department] ([DepartmentID],[Name],[GroupName],[Company],[ModifiedDate]) values (2,N'售后部',N'销售组',N'中国你好有限公司XX分公司','05 5 2015 5:58PM')。
