Този пример не е толкова свързан с SQL-InterActive, а по-скоро с начина на програмиране на T-SQL. Тъй като знаем колко ценни са програмните примери, ние все пак го включихме в инструкциите.
Примерът добре илюстрира рекурсии, съхранени процедури. Главната процедура е предназначена за изчисляване на математически изрази, които въвеждаме под формата на низ от алфанумерични знаци.
Процедурите са направени така, че ако премахнем --!! пред print командите, виждаме хода на изпълнението и рекурсиите.
Помощна процедура, която връща аргументите
създайте процедура [dbo].[dl_PA_ParserGetArg]
@sExpression varchar(8000),
@lLeft bit,
@iOperand int,
@iLimit integer output,
@mResult float output,
@sEr varchar(8000) output
-- тестови параметри
-- декларирайте
-- @sExpression varchar(8000),
-- @lLeft bit,
-- @iOperand int,
-- @iLimit integer,
-- @mResult money,
-- @sEr varchar(8000)
--
-- задайте @sExpression='2*3'
-- задайте @lLeft=1
-- задайте @iOperand=2
-- exec dl_PA_GetArg @sExpression, @lLeft, @iOperand, @iLimit output,@mResult output, @sEr output
-- --!! print '-----'
-- --!! print @iLimit
-- --!! print cast(@mResult as char)
-- --!! print @sEr
както
декларирайте
@sTempArg varchar(8000)
set nocount on
set @sTempArg = ' '+@sExpression+' ' -- това е само за да се погрижи за лимитите в цикъла (да не получим j[0])
set @iOperand = @iOperand + 1
--!! print ' @sTempArg='+@sTempArg+'#'
--!! print ' @iOperand=' + Str(@iOperand) + '#' + SubString(@sTempArg,@iOperand,1)
if @lLeft=1
begin -- ляв аргумент
set @iLimit = @iOperand - 1
--!! print ' ляв аргумент'
--!! print ' @iLimit=' + Str(@iLimit) + '#' + SubString(@sTempArg,@iLimit,1)
while (@iLimit > 0) and not (SubString(@sTempArg,@iLimit,1) in ('+','-','/','*','\','|'))
set @iLimit = @iLimit-1
-- нека проверим за водещ "-" за левия аргумент.
if @iLimit > 1
if SubString(@sTempArg,@iLimit,1) = '-'
set @iLimit = @iLimit-1
--!! print ' @iLimit=' + Str(@iLimit) + '#' + SubString(@sTempArg,@iLimit,1)
--!! print ' @iLimitg='+cast(@iLimit as varchar)
set @sTempArg = SubString(@sTempArg,@iLimit+1,@iOperand-@iLimit-1)
--!! print ' изход ляв @sTempArg='+@sTempArg
end
else
begin -- десен аргумент
set @iLimit = @iOperand + 1
--!! print ' десен аргумент'
--!! print ' @iLimit=' + Str(@iLimit) + '#' + SubString(@sTempArg,@iLimit,1)
set @iLimit = @iLimit+1
--!! print ' Изчисляване...'
while (@iLimit <= Len(@sTempArg)) and not (SubString(@sTempArg,@iLimit,1) in ('+','-','/','*','\','|'))
set @iLimit = @iLimit+1
--!! print ' @iLimit=' + Str(@iLimit) + '#' + SubString(@sTempArg,@iLimit,1)
set @sTempArg = SubString(@sTempArg,@iOperand+1,@iLimit-@iOperand-1)
--!! print ' изход десен @sTempArg='+@sTempArg
end
if @sTempArg = ''
begin
if SubString(@sExpression,@iOperand,1) in ('+','-') -- това е за обработка на -x / +x
set @mResult = 0
else
set @sEr = 'Невалидна употреба на оператори в '+@sExpression
end
else
begin
--!! print ' изход @sTempArg='+@sTempArg
set @mResult = Convert(float,@sTempArg,2)
--!! print ' изход @mResult='+Str(@mResult,35,15)
-- !! проверка за грешка тук !! Обработка на изключения !!
-- if code <> 0 then
-- er = @sExpression+'не е число!'
-- else
-- begin
set @iLimit = @iLimit - 1
-- end
end
Главна процедура, която изчислява израза
създайте процедура [dbo].[dl_PA_ParseAritNew]
@sExpression varchar(8000),
@rResult float output,
@sEr varchar(8000) output -- @sEr = ''<- без грешка, @sEr <> '' <- грешка в израза
-- Внимание ! ---
-- Винаги извиквайте функцията като ParseArit(0,израз,'+',er) !!!!!
-- В израза не трябва да има интервали "2+3" = ОК / "2 + 3" <> ОК !!!!
-- В израза не трябва да има небалансирани скоби (виж тест 2)!
-- (c) А. Мертел, 1997, 2002
както
декларирайте
@iBrackets int, -- брояч на скобите ()
@i int, -- брояч 1
@j int, -- брояч 2
@iLen int, -- дължина на @sExpression
@iLeftLimit int, -- лимит на левия аргумент
@iRightLimit int, -- лимит на десния аргумент
@rLeft float, -- стойност на левия аргумент
@rRight float, -- стойност на десния аргумент
@lOperatorsExist bit, -- имаше ли оператор в израза
@sOrigExpression varchar(8000),
@sTemp varchar(8000) -- временно променлива
set nocount on
set @iLen = Len(@sExpression)
set @sOrigExpression = @sExpression
-- коригирайте "+x" в "x"
if SubString(@sExpression,1,1) = '+'
set @sExpression = Right(@sExpression,@iLen-1)
-- коригирайте "--x" в "x"
set @sExpression = Replace(@sExpression,'--','')
--!! print 'в функция exp = ' +@sOrigExpression
if @sEr = ''
begin -- без грешка при вход
set @lOperatorsExist = 0
-- обработка на скоби ()
set @i = Charindex('(',@sExpression)
if @i > 0 -- @i > 0?
begin -- има (,)
--!! print ' -- има скоби'
set @lOperatorsExist = 1
set @iBrackets = 1
set @j = @i + 1
while (@j <= @iLen) and (@iBrackets > 0) -- намерете първото съвпадение
begin
if SubString(@sExpression,@j,1) = ')'
set @iBrackets = @iBrackets-1
if SubString(@sExpression,@j,1) = '('
set @iBrackets = @iBrackets+1
set @j = @j + 1
end
set @sTemp = SubString(@sExpression,@i+1,@j-@i-2)
-- изчислете външната двойка
exec dl_PA_ParseAritNew @sTemp,@rResult output,@sEr output
--!! print ' ()@rResult='+Str(@rResult,25,10)
set @sTemp = LTrim(Str(@rResult,35,15))
--!! print ' ()@sTemp='+@sTemp
set @sTemp = Replace(@sTemp,',','.') -- сменете възможно ',' с '.'
--!! print ' ()@sTemp, след замяна ='+@sTemp
-- заменете външната двойка с резултата
set @sExpression = SubString(@sExpression,1,@i-1)+@sTemp+SubString(@sExpression,@j,@iLen-@j+1)
--!! print ' ()@sExpression=' + @sExpression
end -- има (,)
else -- @i > 0? не, няма (,)
begin -- проверка за *,/
set @i = 1
while not (SubString(@sExpression,@i,1) in ('*','/')) and (@i < @iLen)
set @i = @i + 1
if SubString(@sExpression,@i,1) in ('*','/')
begin -- *,/ намерени
--!! print ' -- намерени *,/ на позиция ' + Str(@i)
set @lOperatorsExist = 1
exec dl_PA_ParserGetArg @sExpression,1,@i,@iLeftLimit output,@rLeft output,@sEr output
exec dl_PA_ParserGetArg @sExpression,0,@i,@iRightLimit output,@rRight output,@sEr output
--!! print ' */@rLeft='+Str(@rLeft,25,15)
--!! print ' */@rRight='+Str(@rRight,25,15)
--!! print ' */@sEr='+@sEr
if @sEr = ''
begin
if SubString(@sExpression,@i,1) = '*'
set @rResult = @rLeft*@rRight
else
if SubString(@sExpression,@i,1) = '/'
exec dl_DivT @rLeft,@rRight,@rResult output
set @sTemp = LTrim(Str(@rResult,35,15))
--!! print ' */@rResult='+Str(@rResult,25,10)
--!! print ' */@sTemp='+@sTemp
set @sTemp = Replace(@sTemp,',','.') -- сменете възможно ',' с '.'
--!! print ' */@sTemp, след замяна ='+@sTemp
--!! print ' */Десен дел=#'+ SubString(@sExpression,@iRightLimit,Len(@sExpression)-@iRightLimit+1) + '#'
-- заменете *,/ с резултата
if @iLeftLimit > 0
set @sExpression = SubString(@sExpression,1,@iLeftLimit) + @sTemp + SubString(@sExpression,@iRightLimit,Len(@sExpression)-@iRightLimit+1)
else
set @sExpression = @sTemp + SubString(@sExpression,@iRightLimit,Len(@sExpression)-@iRightLimit+1)
--!! print ' */@sExpression(Финал)=' + @sExpression
end
end -- *,/ намерени
else
begin -- проверка за div, mod
set @i = 1
while not (SubString(@sExpression,@i,1) in ('\','|')) and (@i < @iLen)
set @i = @i + 1
if SubString(@sExpression,@i,1) in ('\','|')
begin -- div, mod намерени
--!! print ' -- намерени \,| (div,mod) на позиция ' + Str(@i)
set @lOperatorsExist = 1
exec dl_PA_ParserGetArg @sExpression,1,@i,@iLeftLimit output,@rLeft output,@sEr output
exec dl_PA_ParserGetArg @sExpression,0,@i,@iRightLimit output,@rRight output,@sEr output
--!! print ' \,|@rLeft='+Str(@rLeft,25,15)
--!! print ' \,|@rRight='+Str(@rRight,25,15)
--!! print ' \,|@sEr='+@sEr
if @sEr = ''
begin
if SubString(@sExpression,@i,1) = '\'
begin
if @rRight <> 0
set @rResult = cast((@rLeft / @rRight + 0.0) as int)
else
set @rResult = 0
end
else
if SubString(@sExpression,@i,1) = '|'
begin
if @rRight <> 0
set @rResult = cast(@rLeft as int) % cast(@rRight as int)
else
set @rResult = 0
end
set @sTemp = LTrim(Str(@rResult,35,15))
set @sTemp = Replace(@sTemp,',','.') -- сменете възможно ',' с '.'
-- заменете div, mod с резултата
if @iLeftLimit > 0
set @sExpression = SubString(@sExpression,1,@iLeftLimit) + @sTemp + SubString(@sExpression,@iRightLimit,Len(@sExpression)-@iRightLimit+1)
else
set @sExpression = @sTemp + SubString(@sExpression,@iRightLimit,Len(@sExpression)-@iRightLimit+1)
end
end -- div, mod намерени
else
begin -- проверка за събирания и изваждания
set @i = 1
if SubString(@sExpression,@i,1) = '-'
set @i = @i + 1
while not (SubString(@sExpression,@i,1) in ('+','-')) and (@i < @iLen)
set @i = @i + 1
--!! print ' +,- @i='+cast(@i as varchar)
if SubString(@sExpression,@i,1) in ('+','-')
begin -- +,- намерени
--!! print ' -- намерени +,- на позиция ' + Str(@i)
set @lOperatorsExist = 1
exec dl_PA_ParserGetArg @sExpression,1,@i,@iLeftLimit output,@rLeft output,@sEr output
exec dl_PA_ParserGetArg @sExpression,0,@i,@iRightLimit output,@rRight output,@sEr output
--!! print ' +,- @rLeft='+Str(@rLeft,25,15)
--!! print ' +,- @rRight='+Str(@rRight,25,15)
--!! print ' +,- @sEr='+@sEr
if SubString(@sExpression,@i,1) = '+'
set @rResult = @rLeft + @rRight
else
if SubString(@sExpression,@i,1) = '-'
set @rResult = @rLeft - @rRight
set @sTemp = LTrim(Str(@rResult,35,15))
--!! print ' +-@rResult='+Str(@rResult,25,10)
--!! print ' +-@sTemp='+@sTemp
set @sTemp = Replace(@sTemp,',','.') -- сменете възможно ',' с '.'
--!! print ' +-@sTemp, след замяна ='+@sTemp
-- заменете +,- с резултата
if @iLeftLimit > 0
set @sExpression = SubString(@sExpression,1,@iLeftLimit) + @sTemp + SubString(@sExpression,@iRightLimit,Len(@sExpression)-@iRightLimit+1)
else
set @sExpression = @sTemp + SubString(@sExpression,@iRightLimit,Len(@sExpression)-@iRightLimit+1)
--!! print ' +-@sExpression=' + @sExpression
end -- +,- намерени
end -- проверка за събирания и изваждания
end -- проверка за div, mod
end -- проверка за умножения, деления
if @sEr = ''
begin -- без грешка досега
if @lOperatorsExist = 1
begin
--!! print ' изход за рекурсия оригинал. @sExpression='+@sExpression+' @sExpression=' + @sExpression
exec dl_PA_ParseAritNew @sExpression,@rResult output,@sEr output -- парсинг с стойност
end
else
begin
--!! print ' изход за оригинал. @sExpression='+@sExpression+' @sExpression=' + @sExpression
set @rResult = Convert(float,@sExpression,2)
end
end -- без грешка досега
end -- без грешка при вход
Тестови примери
-- тестов случай 1
декларирайте
@sExpression varchar(8000),
@mResult money,
@sEr varchar(8000)
set @sExpression='2*3'
set @sEr = ''
--!! print @sExpression
exec dl_PA_ParseAritNew @sExpression, @mResult output,@sEr output
--!! print '-----'
--!! print Str(@mResult,35,15)
--!! print @sEr
--тестов случай 2
създайте таблица _TEST_PARSER
(
ФОРМУЛА varchar(300),
ТЕСТ money,
ГРЕШКА varchar(500),
РЕЗУЛТАТ money
)
декларирайте
@sExpression varchar(8000),
@mTest float,
@mResult float,
@i int,
@iBrackets int,
@sEr varchar(8000)
set nocount on
декларирайте crKurzor курсор local fast_forward за
select ФОРМУЛА,ТЕСТ
от _TEST_PARSER
отворете crKurzor
fetch next от crKurzor в @sExpression,@mTest
while @@fetch_status = 0
while @@fetch_status = 0
begin
-- проверка за баланс на скобите
print '--------------------------------------------------------'
print ' Тест: ' + @sExpression
print ' Очакван резултат = ' + Str(@mTest)
set @i = 1
set @iBrackets = 0
while (@i <= Len(@sExpression)) -- проверка на целия израз
begin
if SubString(@sExpression,@i,1) = ')'
set @iBrackets = @iBrackets-1
if SubString(@sExpression,@i,1) = '('
set @iBrackets = @iBrackets+1
set @i = @i + 1
end
if @iBrackets <> 0
set @sEr = 'Небалансирани скоби в ' + @sExpression
else
begin
set @sEr = ''
exec dl_PA_ParseAritNew @sExpression, @mResult output,@sEr output
end
print ' Изчислен резултат = ' + Str(@mResult)
if (@mResult = @mTest)
print ' ОК'
else
print ' Грешка (@mResult - @mTest) = ' + Str(@mResult - @mTest)
print '--------------------------------------------------------'
update _TEST_PARSER
set РЕЗУЛТАТ = @mResult,
ГРЕШКА = @sEr
where LTrim(RTrim(ФОРМУЛА)) = LTrim(RTrim(@sExpression))
fetch next от crKurzor в @sExpression,@mTest
end
close crKurzor
deallocate crKurzor
select LTrim(RTrim(ФОРМУЛА)) as 'Формула',ТЕСТ,РЕЗУЛТАТ,LTrim(RTrim(ГРЕШКА)) as 'ErrorMsg'
от _TEST_PARSER
where (ТЕСТ <> РЕЗУЛТАТ) or (ТЕСТ is null)