Acest exemplu nu este atât de legat de SQL-InterActive, ci mai mult de modul de programare T-SQL. Deoarece știm cât de valoroase sunt exemplele de programare, l-am inclus totuși în instrucțiuni.
Exemplul ilustrează bine recursiile, procedurile stocate. Procedura principală este destinată calculării expresiilor matematice pe care le introducem sub formă de șir de caractere alfanumerice.
Procedurile sunt realizate astfel încât, dacă eliminăm --!! înainte de comenzile print, vedem fluxul de execuție și recursiile.
Procedura auxiliară, care returnează argumentele
create procedure [dbo].[dl_PA_ParserGetArg]
@sExpression varchar(8000),
@lLeft bit,
@iOperand int,
@iLimit integer output,
@mResult float output,
@sEr varchar(8000) output
-- parametrii de test
-- declara
-- @sExpression varchar(8000),
-- @lLeft bit,
-- @iOperand int,
-- @iLimit integer,
-- @mResult money,
-- @sEr varchar(8000)
--
-- set @sExpression='2*3'
-- set @lLeft=1
-- set @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
as
declare
@sTempArg varchar(8000)
set nocount on
set @sTempArg = ' '+@sExpression+' ' --acesta este doar pentru a avea grijă de limite în buclă (pentru a nu obține j[0])
set @iOperand = @iOperand + 1
--!! print ' @sTempArg='+@sTempArg+'#'
--!! print ' @iOperand=' + Str(@iOperand) + '#' + SubString(@sTempArg,@iOperand,1)
if @lLeft=1
begin -- argumentul din stânga
set @iLimit = @iOperand - 1
--!! print ' arg stâng'
--!! print ' @iLimit=' + Str(@iLimit) + '#' + SubString(@sTempArg,@iLimit,1)
while (@iLimit > 0) and not (SubString(@sTempArg,@iLimit,1) in ('+','-','/','*','\','|'))
set @iLimit = @iLimit-1
-- să verificăm pentru "-" de început pentru argumentul din stânga.
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 ' out left @sTempArg='+@sTempArg
end
else
begin -- argumentul din dreapta
set @iLimit = @iOperand + 1
--!! print ' arg drept'
--!! print ' @iLimit=' + Str(@iLimit) + '#' + SubString(@sTempArg,@iLimit,1)
set @iLimit = @iLimit+1
--!! print ' Calculând...'
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 ' out right @sTempArg='+@sTempArg
end
if @sTempArg = ''
begin
if SubString(@sExpression,@iOperand,1) in ('+','-') -- acesta este pentru gestionarea -x / +x
set @mResult = 0
else
set @sEr = 'Utilizare invalidă a operatorilor în '+@sExpression
end
else
begin
--!! print ' out @sTempArg='+@sTempArg
set @mResult = Convert(float,@sTempArg,2)
--!! print ' out @mResult='+Str(@mResult,35,15)
-- !! verifică pentru eroare aici !! Gestionarea excepțiilor !!
-- if code <> 0 then
-- er = @sExpression+'nu este un număr!'
-- else
-- begin
set @iLimit = @iLimit - 1
-- end
end
Procedura principală, care calculează expresia
create procedure [dbo].[dl_PA_ParseAritNew]
@sExpression varchar(8000),
@rResult float output,
@sEr varchar(8000) output -- @sEr = ''<- fără eroare, @sEr <> '' <- eroare în expresie
-- Atenție ! ---
-- Funcția trebuie apelată întotdeauna ca ParseArit(0,expresie,'+',er) !!!!!
-- Expresia nu trebuie să conțină spații "2+3" = OK / "2 + 3" <> OK !!!!
-- Expresia nu trebuie să conțină paranteze neechilibrate (vezi testul 2)!
-- (c) A. Mertelj, 1997, 2002
as
declare
@iBrackets int, -- contor paranteze ()
@i int, -- contor 1
@j int, -- contor 2
@iLen int, -- lungimea @sExpression
@iLeftLimit int, -- limita argumentului din stânga
@iRightLimit int, -- limita argumentului din dreapta
@rLeft float, -- valoarea argumentului din stânga
@rRight float, -- valoarea argumentului din dreapta
@lOperatorsExist bit, -- a existat un operator în expresie
@sOrigExpression varchar(8000),
@sTemp varchar(8000) -- variabilă temporară
set nocount on
set @iLen = Len(@sExpression)
set @sOrigExpression = @sExpression
-- corectează "+x" în "x"
if SubString(@sExpression,1,1) = '+'
set @sExpression = Right(@sExpression,@iLen-1)
-- corectează "--x" în "x"
set @sExpression = Replace(@sExpression,'--','')
--!! print 'în funcție exp = ' +@sOrigExpression
if @sEr = ''
begin -- fără eroare la intrare
set @lOperatorsExist = 0
-- gestionează parantezele ()
set @i = Charindex('(',@sExpression)
if @i > 0 -- @i > 0?
begin -- există (,)
--!! print ' -- există paranteze'
set @lOperatorsExist = 1
set @iBrackets = 1
set @j = @i + 1
while (@j <= @iLen) and (@iBrackets > 0) -- găsește prima potrivire
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)
-- calculează perechea exterioară
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,',','.') -- schimbă posibilele ',' în '.'
--!! print ' ()@sTemp, după înlocuire ='+@sTemp
-- înlocuiește perechea exterioară cu rezultatul
set @sExpression = SubString(@sExpression,1,@i-1)+@sTemp+SubString(@sExpression,@j,@iLen-@j+1)
--!! print ' ()@sExpression=' + @sExpression
end -- există (,)
else -- @i > 0? nu, nu există (,)
begin -- verifică pentru *,/
set @i = 1
while not (SubString(@sExpression,@i,1) in ('*','/')) and (@i < @iLen)
set @i = @i + 1
if SubString(@sExpression,@i,1) in ('*','/')
begin -- *,/ găsit
--!! print ' -- există *,/ găsit la poziția ' + 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,',','.') -- schimbă posibilele ',' în '.'
--!! print ' */@sTemp, după înlocuire ='+@sTemp
--!! print ' */Desni del=#'+ SubString(@sExpression,@iRightLimit,Len(@sExpression)-@iRightLimit+1) + '#'
-- înlocuiește *,/ cu rezultat
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(Final)=' + @sExpression
end
end -- *,/n găsit
else
begin -- verifică pentru 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 găsit
--!! print ' -- există \,| (div,mod) găsit la poziția ' + 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,',','.') -- schimbă posibilele ',' în '.'
-- înlocuiește div, mod cu rezultat
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 găsit
else
begin -- verifică pentru adunări și scăderi
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 -- +,- găsit
--!! print ' -- există +,- găsit la poziția ' + 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,',','.') -- schimbă posibilele ',' în '.'
--!! print ' +-@sTemp, după înlocuire ='+@sTemp
-- înlocuiește +,- cu rezultat
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 -- +,- găsit
end -- verifică pentru adunări și scăderi
end -- verifică pentru div, mod
end -- verifică pentru înmulțiri, împărțiri
if @sEr = ''
begin -- fără eroare până acum
if @lOperatorsExist = 1
begin
--!! print ' out for recursion orig. @sExpression='+@sExpression+' @sExpression=' + @sExpression
exec dl_PA_ParseAritNew @sExpression,@rResult output,@sEr output -- analizează cu valoare
end
else
begin
--!! print ' out for orig. @sExpression='+@sExpression+' @sExpression=' + @sExpression
set @rResult = Convert(float,@sExpression,2)
end
end -- fără eroare până acum
end -- fără eroare la intrare
Exemple de test
-- caz de test 1
declare
@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
--caz de test 2
create table _TEST_PARSER
(
FORMULA varchar(300),
TEST money,
ER varchar(500),
REZULTAT money
)
declare
@sExpression varchar(8000),
@mTest float,
@mResult float,
@i int,
@iBrackets int,
@sEr varchar(8000)
set nocount on
declare crKurzor cursor local fast_forward for
select FORMULA,TEST
from _TEST_PARSER
open crKurzor
fetch next from crKurzor into @sExpression,@mTest
while @@fetch_status = 0
while @@fetch_status = 0
begin
-- verifică echilibrul parantezelor
print '--------------------------------------------------------'
print ' Testare: ' + @sExpression
print ' Rezultatul așteptat = ' + Str(@mTest)
set @i = 1
set @iBrackets = 0
while (@i <= Len(@sExpression)) -- verifică întreaga expresie
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 = 'Paranteze dezechilibrate în ' + @sExpression
else
begin
set @sEr = ''
exec dl_PA_ParseAritNew @sExpression, @mResult output,@sEr output
end
print ' Rezultatul calculat = ' + Str(@mResult)
if (@mResult = @mTest)
print ' OK'
else
print ' Eroare (@mResult - @mTest) = ' + Str(@mResult - @mTest)
print '--------------------------------------------------------'
update _TEST_PARSER
set REZULTAT = @mResult,
ER = @sEr
where LTrim(RTrim(FORMULA)) = LTrim(RTrim(@sExpression))
fetch next from crKurzor into @sExpression,@mTest
end
close crKurzor
deallocate crKurzor
select LTrim(RTrim(FORMULA)) as 'Formula',TEST,REZULTAT,LTrim(RTrim(ER)) as 'ErrorMsg'
from _TEST_PARSER
where (TEST <> REZULTAT) or (TEST is null)