Der erste Teil ist nicht so sehr an SQL-InterActive gebunden, sondern mehr an die Art der Programmierung von T-SQL. Da wir wissen, wie wertvoll Programmbeispiele sind, haben wir es dennoch in die Anleitung aufgenommen.
Das Beispiel veranschaulicht gut Rekursionen, gespeicherte Prozeduren. Die Hauptprozedur dient der Berechnung mathematischer Ausdrücke, die wir in Form eines Strings alphanumerischer Zeichen eingeben.
Die Prozeduren sind so gestaltet, dass wir, wenn wir --!! vor den Print-Befehlen entfernen, den Ablauf der Ausführung und der Rekursionen sehen.
Hilfsprozedur, die Argumente zurückgibt
create procedure [dbo].[dl_PA_ParserGetArg]
@sExpression varchar(8000),
@lLeft bit,
@iOperand int,
@iLimit integer output,
@mResult float output,
@sEr varchar(8000) output
-- Testparameter
-- Deklaration
-- @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+' ' -- dies dient nur dazu, die Grenzen in der Schleife zu berücksichtigen (um nicht j[0] zu erhalten)
set @iOperand = @iOperand + 1
--!! print ' @sTempArg='+@sTempArg+'#'
--!! print ' @iOperand=' + Str(@iOperand) + '#' + SubString(@sTempArg,@iOperand,1)
if @lLeft=1
begin -- linkes Argument
set @iLimit = @iOperand - 1
--!! print ' linkes Argument'
--!! print ' @iLimit=' + Str(@iLimit) + '#' + SubString(@sTempArg,@iLimit,1)
while (@iLimit > 0) and not (SubString(@sTempArg,@iLimit,1) in ('+','-','/','*','\','|'))
set @iLimit = @iLimit-1
-- Überprüfen auf führendes "-" für das linke Argument.
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 -- rechtes Argument
set @iLimit = @iOperand + 1
--!! print ' rechtes Argument'
--!! print ' @iLimit=' + Str(@iLimit) + '#' + SubString(@sTempArg,@iLimit,1)
set @iLimit = @iLimit+1
--!! print ' Berechnung...'
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 ('+','-') -- dies ist für -x / +x Behandlung
set @mResult = 0
else
set @sEr = 'Ungültige Verwendung von Operatoren in '+@sExpression
end
else
begin
--!! print ' out @sTempArg='+@sTempArg
set @mResult = Convert(float,@sTempArg,2)
--!! print ' out @mResult='+Str(@mResult,35,15)
-- !! Überprüfen auf Fehler hier !! Ausnahmebehandlung !!
-- if code <> 0 then
-- er = @sExpression+'ist keine Zahl!'
-- else
-- begin
set @iLimit = @iLimit - 1
-- end
end
Die Hauptprozedur, die den Ausdruck berechnet
create procedure [dbo].[dl_PA_ParseAritNew]
@sExpression varchar(8000),
@rResult float output,
@sEr varchar(8000) output -- @sEr = ''<- ohne Fehler, @sEr <> '' <- Fehler im Ausdruck
-- Warnung ! ---
-- Rufe die Funktion immer als ParseArit(0, Ausdruck, '+', er) !!!!!
-- Im Ausdruck dürfen keine Leerzeichen sein "2+3" = OK / "2 + 3" <> OK !!!!
-- Im Ausdruck dürfen keine unausgeglichenen Klammern sein (siehe Test 2)!
-- (c) A. Mertelj, 1997, 2002
as
declare
@iBrackets int, -- Klammern () Zähler
@i int, -- Zähler 1
@j int, -- Zähler 2
@iLen int, -- Länge von @sExpression
@iLeftLimit int, -- linke Argumentgrenze
@iRightLimit int, -- rechte Argumentgrenze
@rLeft float, -- Wert des linken Arguments
@rRight float, -- Wert des rechten Arguments
@lOperatorsExist bit, -- gab es einen Operator im Ausdruck
@sOrigExpression varchar(8000),
@sTemp varchar(8000) -- temporäre Variable
set nocount on
set @iLen = Len(@sExpression)
set @sOrigExpression = @sExpression
-- korrigiere "+x" in "x"
if SubString(@sExpression,1,1) = '+'
set @sExpression = Right(@sExpression,@iLen-1)
-- korrigiere "--x" in "x"
set @sExpression = Replace(@sExpression,'--','')
--!! print 'in function exp = ' +@sOrigExpression
if @sEr = ''
begin -- kein Fehler beim Eintritt
set @lOperatorsExist = 0
-- bearbeite Klammern ()
set @i = Charindex('(',@sExpression)
if @i > 0 -- @i > 0?
begin -- es gibt (,)
--!! print ' -- es gibt Klammern'
set @lOperatorsExist = 1
set @iBrackets = 1
set @j = @i + 1
while (@j <= @iLen) and (@iBrackets > 0) -- finde das erste Paar
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)
-- berechne das äußere Paar
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,',','.') -- mögliche ',' in '.' ändern
--!! print ' ()@sTemp, nach Ersetzung ='+@sTemp
-- ersetze das äußere Paar durch das Ergebnis
set @sExpression = SubString(@sExpression,1,@i-1)+@sTemp+SubString(@sExpression,@j,@iLen-@j+1)
--!! print ' ()@sExpression=' + @sExpression
end -- es gibt (,)
else -- @i > 0? nein, keine (,)
begin -- überprüfe auf *,/
set @i = 1
while not (SubString(@sExpression,@i,1) in ('*','/')) and (@i < @iLen)
set @i = @i + 1
if SubString(@sExpression,@i,1) in ('*','/')
begin -- *,/ gefunden
--!! print ' -- es gibt *,/ gefunden an Position ' + 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,',','.') -- mögliche ',' in '.' ändern
--!! print ' */@sTemp, nach Ersetzung ='+@sTemp
--!! print ' */Rechter Teil=#'+ SubString(@sExpression,@iRightLimit,Len(@sExpression)-@iRightLimit+1) + '#'
-- ersetze *,/ durch Ergebnis
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 -- *,/ gefunden
else
begin -- überprüfe auf 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 gefunden
--!! print ' -- es gibt \,| (div,mod) gefunden an Position ' + 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,',','.') -- mögliche ',' in '.' ändern
-- ersetze div, mod durch Ergebnis
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 gefunden
else
begin -- überprüfe auf Additionen und Subtraktionen
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 -- +,- gefunden
--!! print ' -- es gibt +,- gefunden an Position ' + 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,',','.') -- mögliche ',' in '.' ändern
--!! print ' +-@sTemp, nach Ersetzung ='+@sTemp
-- ersetze +,- durch Ergebnis
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 -- +,- gefunden
end -- überprüfe auf Additionen und Subtraktionen
end -- überprüfe auf div, mod
end -- überprüfe auf Multiplikationen, Divisionen
if @sEr = ''
begin -- bisher kein Fehler
if @lOperatorsExist = 1
begin
--!! print ' out for recursion orig. @sExpression='+@sExpression+' @sExpression=' + @sExpression
exec dl_PA_ParseAritNew @sExpression,@rResult output,@sEr output -- parse mit Wert
end
else
begin
--!! print ' out for orig. @sExpression='+@sExpression+' @sExpression=' + @sExpression
set @rResult = Convert(float,@sExpression,2)
end
end -- bisher kein Fehler
end -- kein Fehler beim Eintritt
Testfälle
-- Testfall 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
-- Testfall 2
create table _TEST_PARSER
(
FORMEL varchar(300),
TEST money,
ER varchar(500),
ERGEBNIS 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 FORMEL,TEST
from _TEST_PARSER
open crKurzor
fetch next from crKurzor into @sExpression,@mTest
while @@fetch_status = 0
while @@fetch_status = 0
begin
-- Überprüfen auf Klammerausgleich
print '--------------------------------------------------------'
print ' Testen: ' + @sExpression
print ' Erwartetes Ergebnis = ' + Str(@mTest)
set @i = 1
set @iBrackets = 0
while (@i <= Len(@sExpression)) -- gesamten Ausdruck überprüfen
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 = 'Unausgeglichene Klammern in ' + @sExpression
else
begin
set @sEr = ''
exec dl_PA_ParseAritNew @sExpression, @mResult output,@sEr output
end
print ' Berechnetes Ergebnis = ' + Str(@mResult)
if (@mResult = @mTest)
print ' OK'
else
print ' Fehler (@mResult - @mTest) = ' + Str(@mResult - @mTest)
print '--------------------------------------------------------'
update _TEST_PARSER
set ERGEBNIS = @mResult,
ER = @sEr
where LTrim(RTrim(FORMEL)) = LTrim(RTrim(@sExpression))
fetch next from crKurzor into @sExpression,@mTest
end
close crKurzor
deallocate crKurzor
select LTrim(RTrim(FORMEL)) as 'Formel',TEST,ERGEBNIS,LTrim(RTrim(ER)) as 'Fehlermeldung'
from _TEST_PARSER
where (TEST <> ERGEBNIS) or (TEST is null)