从数据的组合拆分入手,逐渐引申到事务:
首先列表页选中一系列字符串,准备批量操作:
$('#btnCheck').click(function () {
var i = 0;
var tmpSelAsset = '';
//获取所有被选中的标签元素
var checked = $("input[name='defaultAssetInfo']:checked");
//将所有被选中的标签的值保存成一个字符串,以“$”隔开
for (i = 0; i < checked.length; i++) {
if (i < checked.length - 1)
tmpSelAsset += checked[i].value + '$';
else
tmpSelAsset += checked[i].value;
}
var revalue = "";
var ajaxUrl = "";
var mContractCode = document.getElementById("ContractCode").value;
ajaxUrl = "<%=RequestHelper.GetParentUrlOfCurrentPage() %>SaveShopInfo.aspx?Action=add&ContractCode=" + mContractCode + "&ShopCodes=" + tmpSelAsset;
revalue = doSubmitForm("curForm", ajaxUrl);
if (retValue.length > 0) {
var arrMsg = retValue.split('|');
var status = arrMsg[0];
var strmsg = arrMsg[1];
if (status == '100' || status == '103') {
location.href = "<%=Request.Url.ToString()%>";
}
}
parent.closeDialog();
});
//保存相对应字符串,并将合同编号和选中的商铺编号传值传过去:
if(mAction=="add")
{
mDicInputParam.Add(new DbParam("Action", "add"));
mDicInputParam.Add(new DbParam("ShopCodes", mShopCodes));
mDicInputParam.Add(new DbParam("ContractCode", mContractCode));
int rValue = AgileCorpDataService.ExecuteDataFunction("contract100111", mDicInputParam, out mDicSysParam);
mStatus = DbParamHelper.DbParamValue<int>(mDicSysParam, "Status", -1);
mRetValue = DbParamHelper.DbParamValue<String>(mDicSysParam, "Status","");
}
else
{
mDicInputParam.Add(new DbParam("Action", "delete"));
mDicInputParam.Add(new DbParam("ShopCodes", mShopCodes));
mDicInputParam.Add(new DbParam("ContractCode", mContractCode));
int rValue = AgileCorpDataService.ExecuteDataFunction("contract100111", mDicInputParam, out mDicSysParam);
mStatus = DbParamHelper.DbParamValue<int>(mDicSysParam, "Status", -1);
mRetValue = DbParamHelper.DbParamValue<String>(mDicSysParam, "Status", "");
}
重点还是在存储过程当中:
注:1.在理解的过程中,便于讲解,我将类似于“@pi_arrCount的值等于N”简写成“arrCount的值是N”
2.解释一般在执行语句之前,所以,执行语句执行的结果我在解释当中就已经阐述了
Contract100111:
单看Add的这个操作
CREATE PROCEDURE Asset_spExecuteContractShopInfo
(
@pi_Action VARCHAR(100),
@pi_ShopCodes varchar(8000),
@pi_ContractCode varchar(100),
@po_Status int output,
@po_RetValue varchar(200) output
)
as
--声明三个变量
DECLARE @pt_arrCount INT
DECLARE @pt_Index INT
DECLARE @pt_ShopCode VARCHAR(100)
--最后得到的@pt_arrCount肯定是个值,这个值的由来通过标量值函数得到的,详解请看
--操作1,通过操作1之后,这里的arrCount的值是3
SELECT @pt_arrCount = dbo.App_S_funcGetArrayLength(@pi_ShopCodes, '$')
if @pi_Action='add'
begin
--开始执行事务,事务执行两种情况:要么里面的语句全部执行,要么全部不执行!个人理解这样是为了防止数据关联,删除部分数据之后造成系统局部功能瘫痪
BEGIN TRANSACTION
SET @pt_Index = 1
--第一次:Index的值小于当前arrCount的值3
--第二次:index的值为2,小于当前arrCount,继续循环
--第三次:Index的值为3,等于当前arrCount,继续循环
WHILE @pt_Index <= @pt_arrCount
BEGIN
--开始执行,得到商铺编号,怎么得到,这里又涉及到另外一个标量值函数,详解请看
--操作2,在执行第一次的操作之后,shopCode的值是‘a’,再执行以下语句,先添加,后更新
--第二次操作,返回ShopCode的值是‘b’
--第三次操作,返回ShopCode的值是‘c’
SELECT @pt_ShopCode = dbo.App_S_funcGetValueOfArrayIndex(@pi_ShopCodes,'$', @pt_Index)
Insert into Asset_ContractShopInfo(ContractCode,ShopCode) values(@pi_ContractCode,@pt_ShopCode)
update Asset_ShopInfo set RentStatus=2 where ShopCode=@pt_ShopCode
--执行一次之后,Index的值变为2
--再次执行之后,Index的值变为3
SET @pt_Index = @pt_Index + 1
END
if @@error <> 0
--如果事务执行失败,回滚事务,操作失败
begin
rollback transaction
set @po_status = 900
set @po_RetValue = '添加失败'
end
else
--事务执行成功,操作成功
begin
commit transaction
set @po_status = 100
set @po_RetValue = '添加成功'
end
end
--结果 a b c
操作1:【拿‘a$b$c’这样一个字符串来举例】
dbo.App_S_funcGetArrayLength
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER FUNCTION [dbo].[App_S_funcGetArrayLength]
(
@pi_Str VARCHAR(1000), --字符【这里就是‘a$b$c’】
@pi_spliter VARCHAR(10) --分割符【‘$’】
)
RETURNS INT
AS
--这里开始
BEGIN
--首先声明三个需要的变量
DECLARE @pt_Location INT
DECLARE @pt_Start INT
DECLARE @pt_Length INT
--将获取的字符串两端空格去除,因为在执行的过程中会涉及到位置,空格亦算字符位置
SET @pi_Str = LTRIM(RTRIM(@pi_Str))
--接下来的就比较重要了,关于‘CHARINDEX’的相关用法,这里穿插一下其用法:
【1.SQL CHARINDEX函数主要是返回字符或者字符串在另一个字符串中的起始位置
2.函数方法:CHARINDEX(expression1,expression2[,start_location])
解释一下:expression1是要到expression2中找的字符,strat_location是CHARINDEX函数开始在expression2中找expression1的位置
3. SQL CHARINDEX函数返回一个整数,返回的整数是要找的字符串在被找的字符串中的位置。假如SQL CHARINDEX函数没有找到要找的字符串,那么函数整数“0”
4.先来简单理解一下在我们这个存储过程中紧接着的这句话:
CHARINDEX(@pi_spliter,@pi_str)就是要在传过来的’a$b$c’这个字符串当中找出‘$’的位置,即2,也就是说这里的@pi_Location的值就是2.(注意一下,这里的字符不是像数组当中的一样从0开始,而是从1开始!)】
SET @pt_Location = CHARINDEX(@pi_spliter,@pi_str)
SET @pt_Length = 1
--第一次:location的值是2,<>0条件成立,开始执行
--第二次:location的值是4,<>0仍然成立,开始执行
--第三次:location的值是0,<>0不成立,结束执行
WHILE @pt_Location <> 0
BEGIN
--第一次:strat值为3
--第二次;start值为5
SET @pt_Start = @pt_Location + 1
--第一次:好,接下来看,这里的CHARINDEX的参数变成3个了,再返回看下,最后的Start是啥意思,通过例子说一下:找出‘$’在‘a$b$c’中从第3个开始往后的第1个‘$’的位置!也就是第二个‘$’的位置,就是4,(注意一下:此时的location已经变成4了)
--第二次:找出‘$’在‘a$b$c’中从第5个开始往后的第1个‘$’的位置!即字符串当中‘c’的位置往后的‘$’显然,不存在了,这时候再看一下上面CHARINDEX函数当中的第3点:没有之后,返回给这里location的值就是0!
SET @pt_Location = CHARINDEX(@pi_spliter, @pi_str, @pt_Start)
--第一次:执行结束,length变为2
--第二次:执行结束,length变为3
SET @pt_Length = @pt_Length + 1
END
--第三次:返回length的长度:3
RETURN @pt_length
操作2:【这里一样用的是‘a$b$c’这样一个字符串来举例】
dbo.App_S_funcGetValueOfArrayIndex
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER FUNCTION [dbo].[App_S_funcGetValueOfArrayIndex]
(
--同样,先定义需要的变量,含义和之前的一样
@pi_Str VARCHAR(1000), --字符
@pi_spliter VARCHAR(10), --分割符
@pi_index INT
)
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE @pt_start INT
DECLARE @pt_next INT
DECLARE @pt_Seed INT
DECLARE @pt_location INT
--去除字符串空格
SET @pi_Str = LTRIM(RTRIM(@pi_Str));
SET @pt_start = 1
SET @pt_next = 1
--获得spliter的长度值,即‘$’的长度,类似于有规律的一串数字,就说123$23$aa$89,两个字符中间共有的是$,所以想办法拆分的时候需要将其长度知晓,再截去这部分长度
这里的Seed的值就是1
SET @pt_Seed = LEN(@pi_spliter)
--这里就不用多解释了,location的值就是2
SET @pt_location = CHARINDEX(@pi_spliter, @pi_str)
--第一次:location的值是2<>0成立,但是Index等于1不大于next,所以不执行
--第二次:index的值为2,大于1,开始执行
--第三次:Index的值为3,继续执行
WHILE @pt_location <> 0 AND @pi_Index > @pt_next
BEGIN
--第一次:start的值变为3
--第二次:start的值仍然为3
--第三次:start的值变为5
SET @pt_start = @pt_location + @pt_seed
--第二次:获取从第三个位置往后第一个$的位置:4,location为4
--第三次:获取从第五个位置往后第一个$的位置:不存在,即location为0
SET @pt_location = CHARINDEX(@pi_spliter, @pi_str, @pt_start)
--第二次:next的值变为2
--第三次:next的值变为3
SET @pt_next = @pt_next + 1
END
--第一次:location值为2,不执行
--第二次:location值为4,不执行
--第三次:location值为0,执行语句
IF @pt_location = 0
BEGIN
--第三次:location的值为6
SELECT @pt_location = LEN(@pi_str) + 1
END
--第一次:返回SUBSTRING,好,这里我们再看SUBSTRING的相关用法,第一次的结果是‘a’
【1.功能:返回字符、二进制、文本或图像表达式的一部分
2.语法:SUBSTRING ( expression, start, length ),怎么理解?
3.含义:expression 字符串、二进制字符串、文本、图像、列或包含列的表达式。请勿使用包含聚合函数的表达式。
start 整数或可以隐式转换为 int 的表达式,指定子字符串的开始位置。
length 整数或可以隐式转换为 int 的表达式,指定子字符串的长度。
4.再看我们这句话:返回的结果就是在‘a$b$c’(str)中第一个位置(strat),截取长度为1的字符串(location-strat),即结果就是‘a’】
--第二次:返回的结果就是在‘a$b$c’(str)中第3个位置(strat),截取长度为1的字符串(location-strat),即结果就是‘b’
--第三次:返回的结果就是在‘a$b$c’(str)中第5个位置(strat),截取长度为1的字符串(location-strat),即结果就是‘c’
RETURN SUBSTRING(@pi_str, @pt_start, @pt_location - @pt_start)
END
两个标量值函数的使用固然方便,在dbo.App_S_funcGetArrayLength中,最终得到的只是一个length的长度值,其他没有什么参数了,这是我开始时不解的地方,给定一个很大的length值就可以直接执行dbo.App_S_funcGetValueOfArrayIndex函数,但是,也是我思维局限的地方:假如字符串只是a$b,而我给定length的长度为100000000…,程序执行的时间可想而知,而且是浪费时间,通过dbo.App_S_funcGetArrayLength执行之后,length的值是动态的,就直接减少了时间的消耗。裨益一目了然!