views:

148

answers:

6

Hi. This is a stored procedure (at least I have to be assuming so), that constructs an Sql query as a String and then calls it with exec. For some reason the dude that called himself a programmer, concatenates some values using "~" characters and them splits them back in the application layer. This SP breaks when, some checkbox is "selected" .. The question is how can I debug this nightmare. I have found that when the CheckBox is selected, it adds an unnecessary "~1" to the beginning of some TypeCode and it breaks. if not selected, it works as intended. (BTW I'd really like to know what kind of coding practice is this thing called).

  ALTER  PROC [dbo].[BUTCEREP] @SirketKod varchar(10), @IlkTarih varchar(10), @SonTarih varchar(10), @Tip int, @Tur int, @MasrafMrk varchar(100), @DovizKod varchar(5), @MaliTKod varchar(10)
    -- Tip 0-TarihAraligi; 1-2AylikButce; 2-YTDButce; 3-YTDCompPrev
    -- Tur 0-Muhasebe 1-Gider 2-Butce 3-ButceMuhasebe 4-ButceGider
    AS
      declare
        @SqlText nvarchar(4000),
        @SqlBud varchar(4000),
        @SqlMuh varchar(4000),
        @SqlGid varchar(4000),
        @xIlkT datetime,
        @xSonT datetime,
        @ThisMonth varchar(10),
        @NextMonth varchar(10),
        @ThisYear varchar(10),
        @PrevYear varchar(10)


      if (not @IlkTarih is null) set @xIlkT = CONVERT(datetime,@IlkTarih,103)
      if (not @SonTarih is null) set @xSonT = CONVERT(datetime,@SonTarih,103)
      set @DovizKod=IsNull(@DovizKod, '')
      if @Tip=1 and MONTH(@xIlkT)=12 set @xIlkT = dateadd(month,-1,@xIlkT)

      set @ThisMonth = CONVERT(varchar,MONTH(@xIlkT))
      set @NextMonth = CONVERT(varchar,MONTH(@xIlkT)+1)
      set @ThisYear  = CONVERT(varchar,YEAR(@xIlkT))
      set @PrevYear  = CONVERT(varchar,YEAR(@xIlkT)-1)

      set @SqlMuh='select X.MuhHesapKod,X.Tarih,SUM(CAST(X.Borc   AS FLOAT)) Borc,'+
                                               'SUM(CAST(X.Alacak AS FLOAT)) Alacak' +
                   ' from (select CASE S.N WHEN 1 THEN LEFT(D.MuhHesapKod,3)' +
                                         ' WHEN 2 THEN LEFT(D.MuhHesapKod,7)' +
                                         ' ELSE D.MuhHesapKod END MuhHesapKod,' +
                                 'D.Tarih,Borc,Alacak' +
                  ' from '+@DovizKod+'MUH_D D inner join '+@DovizKod+'MUH_H H on (H.SirketKod=D.SirketKod) and (H.KontrolNo=D.KontrolNo)'+
                 ' CROSS JOIN dbo.SeriTable(3) S' +
                 ' where D.SirketKod=' + QuoteName(@SirketKod,'''') +
                  case when IsNull(@MasrafMrk,'')<>'' then ' and D.MasrafMrkKod in (select Kod from GetArray('+QuoteName(@MasrafMrk,'''')+'))' else '' end +
                  case @Tip
                    when 0 then ' and D.Tarih BETWEEN ' + IsNull(QuoteName(CONVERT(varchar,@xIlkT,103),''''),'D.Tarih') + ' and ' + IsNull(QuoteName(CONVERT(varchar,@xSonT,103),''''),'D.Tarih')
                    when 1 then ' and YEAR(D.Tarih)=' +@ThisYear+' and MONTH(D.Tarih) in ('+@ThisMonth+','+@NextMonth+')'
                    when 2 then ' and YEAR(D.Tarih)=' +@ThisYear+' and MONTH(D.Tarih)<='+@ThisMonth
                    when 3 then ' and (YEAR(D.Tarih)='+@PrevYear+' or (YEAR(D.Tarih)='+@ThisYear+' and MONTH(D.Tarih)<='+@ThisMonth+'))'
                  end +
                  ' and IsNull(H.FisTip,'''')<>''KAPANIS'''+
                           ') X' +
                 ' where X.MuhHesapKod in (select Kod from MALIT_M where SirketKod=' + QuoteName(@SirketKod,'''') +
                                                                   ' and MaliTKod='  + QuoteName(@MaliTKod,'''') + ')' +
                 ' group by X.MuhHesapKod,X.Tarih'

      set @SqlMuh = ' select 1 Tip,CAST(4 AS char(1))+''~0~''+M.MuhHesapKod Kod,' +
                    case @Tip
                      when 0 then 'SUM((M.Borc-M.Alacak)) TopBak'
                      when 1 then 'SUM(CASE WHEN MONTH(M.Tarih)=' +@ThisMonth+' THEN 1 ELSE 0 END*(M.Borc-M.Alacak)) A1Bak,' +
                                  'SUM(CASE WHEN MONTH(M.Tarih)=' +@NextMonth+' THEN 1 ELSE 0 END*(M.Borc-M.Alacak)) A2Bak'
                      when 2 then 'SUM(CASE WHEN MONTH(M.Tarih)=' +@ThisMonth+' THEN 1 ELSE 0 END*(M.Borc-M.Alacak)) A1Bak,' +
                                  'SUM(CASE WHEN MONTH(M.Tarih)<='+@ThisMonth+' THEN 1 ELSE 0 END*(M.Borc-M.Alacak)) A2Bak'
                      when 3 then 'SUM(CASE WHEN YEAR(M.Tarih)=' +@ThisYear+' AND MONTH(M.Tarih)=' +@ThisMonth+' THEN 1 ELSE 0 END*(M.Borc-M.Alacak)) A1Bak,' +
                                  'SUM(CASE WHEN YEAR(M.Tarih)=' +@ThisYear+' AND MONTH(M.Tarih)<='+@ThisMonth+' THEN 1 ELSE 0 END*(M.Borc-M.Alacak)) A2Bak,' +
                                  'SUM(CASE WHEN YEAR(M.Tarih)=' +@PrevYear+' THEN 1 ELSE 0 END*(M.Borc-M.Alacak)) A3Bak'
                    end +
                    ' from (' + @SqlMuh + ') M' +
                    ' where M.MuhHesapKod in (select Kod from MALIT_M where SirketKod=' + QuoteName(@SirketKod,'''') +
                                                                       ' and MaliTKod=' + QuoteName(@MaliTKod,'''') + ')'+
                    ' group by M.MuhHesapKod'

      set @SqlGid = ' select 1 Tip,CAST(2 AS char(1))+''~0~''+C.GiderKod Kod,' +
                    case @Tip
                      when 0 then 'SUM((2*C.IslemTuru-1)*'+CASE WHEN @DovizKod='' THEN '(TutarTL+GVergiTutar)' ELSE 'GEURO' END+') TopBak'
                      when 1 then 'SUM(CASE WHEN MONTH(C.Tarih)='+@ThisMonth+' THEN 1 ELSE 0 END*(2*C.IslemTuru-1)*'+CASE WHEN @DovizKod='' THEN '(TutarTL+GVergiTutar)' ELSE 'GEURO' END+') A1Bak,' +
                                  'SUM(CASE WHEN MONTH(C.Tarih)='+@NextMonth+' THEN 1 ELSE 0 END*(2*C.IslemTuru-1)*'+CASE WHEN @DovizKod='' THEN '(TutarTL+GVergiTutar)' ELSE 'GEURO' END+') A2Bak'
                      when 2 then 'SUM(CAST(CASE WHEN MONTH(C.Tarih)=' +@ThisMonth+ ' THEN 1 ELSE 0 END*(2*C.IslemTuru-1)*'+CASE WHEN @DovizKod='' THEN '(TutarTL+GVergiTutar)' ELSE 'GEURO' END+' AS FLOAT)) A1Bak,' +
                                  'SUM(CAST(CASE WHEN MONTH(C.Tarih)<='+@ThisMonth+ ' THEN 1 ELSE 0 END*(2*C.IslemTuru-1)*'+CASE WHEN @DovizKod='' THEN '(TutarTL+GVergiTutar)' ELSE 'GEURO' END+' AS FLOAT)) A2Bak'
                      when 3 then 'SUM(CAST(CASE WHEN YEAR(C.Tarih)=' +@ThisYear+' AND MONTH(C.Tarih)=' +@ThisMonth+ ' THEN 1 ELSE 0 END*(2*C.IslemTuru-1)*'+CASE WHEN @DovizKod='' THEN '(TutarTL+GVergiTutar)' ELSE 'GEURO' END+' AS FLOAT)) A1Bak,' +
                                  'SUM(CAST(CASE WHEN YEAR(C.Tarih)=' +@ThisYear+' AND MONTH(C.Tarih)<='+@ThisMonth+ ' THEN 1 ELSE 0 END*(2*C.IslemTuru-1)*'+CASE WHEN @DovizKod='' THEN '(TutarTL+GVergiTutar)' ELSE 'GEURO' END+' AS FLOAT)) A2Bak,' +
                                  'SUM(CAST(CASE WHEN YEAR(C.Tarih)=' +@PrevYear+' THEN 1 ELSE 0 END*(2*C.IslemTuru-1)*'+CASE WHEN @DovizKod='' THEN '(TutarTL+GVergiTutar)' ELSE 'GEURO' END+' AS FLOAT)) A3Bak'
                    end +
                    ' from CHI_D C'+
                    ' where C.GiderKod in (select Kod from MALIT_M where SirketKod=' + QuoteName(@SirketKod,'''') +
                                                                   ' and MaliTKod='  + QuoteName(@MaliTKod ,'''') + ')'+
                    case when IsNull(@MasrafMrk,'')<>'' then ' and C.MasrafMrkKod in (select Kod from GetArray('+QuoteName(@MasrafMrk,'''')+'))' else '' end +

                    case @Tip
                      when 0 then ' and C.Tarih BETWEEN ' + IsNull(QuoteName(CONVERT(varchar,@xIlkT,103),''''),'C.Tarih') + ' and ' + IsNull(QuoteName(CONVERT(varchar,@xSonT,103),''''),'C.Tarih') + ')'
                      when 1 then ' and YEAR(C.Tarih)='+@ThisYear+' and MONTH(C.Tarih) in (' +@ThisMonth+','+@NextMonth+')'
                      when 2 then ' and YEAR(C.Tarih)='+@ThisYear+' and MONTH(C.Tarih)<='+@ThisMonth
                      when 3 then ' and (YEAR(C.Tarih)='+@PrevYear+' or (YEAR(C.Tarih)='+@ThisYear+' and MONTH(C.Tarih)<='+@ThisMonth+'))'
                    end +
                    ' group by C.GiderKod'

      set @SqlBud='select 0 Tip,CAST(D.IslemTipi AS char(1))+''~0~''+D.KalemKod Kod,' +
                  case @Tip
                    when 0 then 'sum((2*D.IslemTuru-1)*IsNull(D.Tutar,0)) TopBak'
                    when 1 then 'sum(CASE WHEN MONTH(D.BasTarih)='+@ThisMonth+' THEN 1 ELSE 0 END*(2*D.IslemTuru-1)*IsNull(D.Tutar,0)) A1Bak,' +
                                'sum(CASE WHEN MONTH(D.BasTarih)='+@NextMonth+' THEN 1 ELSE 0 END*(2*D.IslemTuru-1)*IsNull(D.Tutar,0)) A2Bak'
                    when 2 then 'sum(CASE WHEN MONTH(D.BasTarih)='+@ThisMonth+' THEN 1 ELSE 0 END*(2*D.IslemTuru-1)*IsNull(D.Tutar,0)) A1Bak,' +
                                'sum((2*D.IslemTuru-1)*IsNull(D.Tutar,0)) A2Bak'
                    when 3 then 'sum(CASE WHEN MONTH(D.BasTarih)=' +@ThisMonth+' THEN 1 ELSE 0 END*(2*D.IslemTuru-1)*IsNull(D.Tutar,0)) A1Bak,' +
                                'sum(CASE WHEN MONTH(D.BasTarih)<='+@ThisMonth+' THEN 1 ELSE 0 END*(2*D.IslemTuru-1)*IsNull(D.Tutar,0)) A2Bak,' +
                                'sum((2*D.IslemTuru-1)*IsNull(D.Tutar,0)) A3Bak'
                  end +
                  ' from dbo.GetButceList('+ QuoteName(@SirketKod,'''') +','+ IsNull(QuoteName(@MasrafMrk,''''),'null')+','+QuoteName(@DovizKod,'''')+',0) D' +
                  ' where D.KalemKod in (select Kod from MALIT_M where SirketKod=' + QuoteName(@SirketKod,'''') +
                                                                 ' and MaliTKod='  + QuoteName(@MaliTKod,'''') + ')'+
                  case @Tip
                    when 0 then ' and D.BasTarih>=' + IsNull(QuoteName(CONVERT(varchar,@xIlkT,103),''''),'D.BasTarih') + ' and D.BitTarih<=' + IsNull(QuoteName(CONVERT(varchar,@xSonT,103),''''),'D.BitTarih')
                    when 1 then ' and YEAR(D.BasTarih)='+@ThisYear+' and MONTH(D.BasTarih) in (' +@ThisMonth+','+@NextMonth+')'
                    when 2 then ' and YEAR(D.BasTarih)='+@ThisYear+' and MONTH(D.BasTarih)<='+@ThisMonth
                    when 3 then ' and YEAR(D.BasTarih)='+@ThisYear
                  end +
                  ' group by D.KalemKod,D.IslemTipi'

    -- Tur 0-Muhasebe 1-Gider 2-Butce 3-ButceMuhasebe 4-ButceGider
      set @SqlText = case @Tur
                       when 0 then @SqlMuh
                       when 1 then @SqlGid
                       when 2 then @SqlBud
                       when 3 then @SqlBud + ' union all ' + @SqlMuh
                       when 4 then @SqlBud + ' union all ' + @SqlGid
                     end

      if @Tip in (1,2)
        set @SqlText='select Kod,' +
                            'SUM(CASE Tip WHEN 0 THEN IsNull(A1Bak,0) ELSE 0 END) A10Bak,' +  --budget first
                            'SUM(CASE Tip WHEN 0 THEN IsNull(A2Bak,0) ELSE 0 END) A20Bak,' +  --budget second
                            'SUM(CASE Tip WHEN 1 THEN IsNull(A1Bak,0) ELSE 0 END) A11Bak,' +  --actual first
                            'SUM(CASE Tip WHEN 1 THEN IsNull(A2Bak,0) ELSE 0 END) A21Bak ' +  --actual second
                      'from (' + @SqlText + ') X' +
                      ' group by Kod'
      else if @Tip = 3
        set @SqlText='select Kod,' +
                            'SUM(CASE Tip WHEN 1 THEN IsNull(A3Bak,0) ELSE 0 END) A00Bak,' +  --actual prev
                            'SUM(CASE Tip WHEN 0 THEN IsNull(A1Bak,0) ELSE 0 END) A10Bak,' +  --budget first
                            'SUM(CASE Tip WHEN 0 THEN IsNull(A2Bak,0) ELSE 0 END) A20Bak,' +  --budget second
                            'SUM(CASE Tip WHEN 1 THEN IsNull(A1Bak,0) ELSE 0 END) A11Bak,' +  --actual first
                            'SUM(CASE Tip WHEN 1 THEN IsNull(A2Bak,0) ELSE 0 END) A21Bak,' +  --actual second
                            'SUM(CASE Tip WHEN 0 THEN IsNull(A3Bak,0) ELSE 0 END) A01Bak ' +  --budget yearly
                      'from (' + @SqlText + ') X' +
                      ' group by Kod'

      PRINT @SqlText
      exec sp_executesql @SqlText
A: 

All those CASE statements make it look like a logistical nightmare. He should really brake it down into manageable chunks of SQL Code, which could then be placed in separate stored procedures.

Hopefully this will also enable him into seeing how he can simplify the SQL better. Far too many CASE statements in there for my liking.

You could do with going over the requirements spec for it to see why it was written in that way. I kind of think some metadata table could be used to help with the query, to help store some of the values which get checked in the CASE statements.

EDIT:

As you're now supporting it I would suggest trying to rewrite it without the dynamic SQL. This may help when it comes to you understanding whats going on as well as breaking it down into more manageable chunks.

kevchadders
There is no more him. Some people coded this thig they call erp 10 years ago. And I am the new guy, which will be doing this kinda stuff everyday. I asked this question actually, to find out what to say to my manager if he tries to fire me :)
AIC
and there is no documentation or requirements spec of this thing..
AIC
I was about to ask about there being any specs for it... doh!
kevchadders
If you'll be in charge of this code, I suggest you learn to love it and internalize it. It's your code now.
Wadih M.
A: 

you should try rewriting this using CLR stored procedures

scatman
+1  A: 

Rewriting would be the best solution. But, to try and debug it, use SQL Profiler (one of the SQL Server performance tools that comes with SQL Server) to put a stored procedure trace on the database and capture the exact command that executes this stored procedure. This will allow you to capture the exact parameter values being passed in and allow you to run it repeatedly in a SQL Management Studio window.

Then, you can modify the stored procedure to print out various values you wish to inspect at various points throughout the stored procedure in order to determine what is happening at any given point.

Alternatively, you can use visual studio to enable SQL debugging and actually step through the stored procedure.

NYSystemsAnalyst
Visual Studio SQL debugging works on SQL Server 2008 and higher.
Wadih M.
I thought it was SQL 2005 and higher?
NYSystemsAnalyst
A: 

1) at the top of the procedure, add an INSERT statement to log the input parameters into a table, with something like:

INSERT INTO YourLog (TextValue) VALUES ('EXEC BUTCEREP @SirketKod='''+COALESCE(@SirketKod,'NULL')+''', @IlkTarih='''+COALESCE(@IlkTarih,'NULL')+''', ...)

2) run this procedure several times from the application to capture the input parameters, including several good runs as well as the know problem.

3) add many PRINT statements all over the code. Even break down the long assignments into smaller, yet multiple assignments (where different parts are concatenated, the "+") and put prints between them:

...
PRINT @SqlMuh
...
PRINT @SqlMuh
...
PRINT @SqlGid
...

4) take the 'EXEC ...' strings from the log, and run the commands from SSMS, look at the print output and the code and determine what is going on.

KM
Very interesting profiling technique. Thanks.
Wadih M.
A: 

Since you asked how you could potentially debug this:

Change the beginning from this:

  ALTER  PROC [dbo].[BUTCEREP] @SirketKod varchar(10), @IlkTarih varchar(10), @SonTarih varchar(10), @Tip int, @Tur int, @MasrafMrk varchar(100), @DovizKod varchar(5), @MaliTKod varchar(10)
    -- Tip 0-TarihAraligi; 1-2AylikButce; 2-YTDButce; 3-YTDCompPrev
    -- Tur 0-Muhasebe 1-Gider 2-Butce 3-ButceMuhasebe 4-ButceGider
    AS
      declare
        @SqlText nvarchar(4000),
        @SqlBud varchar(4000),
        @SqlMuh varchar(4000),
        @SqlGid varchar(4000),
        @xIlkT datetime,
        @xSonT datetime,
        @ThisMonth varchar(10),
        @NextMonth varchar(10),
        @ThisYear varchar(10),
        @PrevYear varchar(10)

Into this:

  declare @SqlText as nvarchar(4000)
  declare @SqlBud as varchar(4000)
  declare @SqlMuh as varchar(4000)
  declare @SqlGid as varchar(4000)
  declare @xIlkT as datetime
  declare @xSonT as datetime
  declare @ThisMonth as varchar(10)
  declare @NextMonth as varchar(10)
  declare @ThisYear as varchar(10)
  declare @PrevYear as varchar(10)

  set @ThisYear = 'your test value'
  set @NextMonth = 'your test value'
  set @ThisMonth = 'your test value'
  set @SqlText = 'your test value'
  set @SqlBud = 'your test value'
  set @SqlMuh = 'your test value'
  set @SqlGid = 'your test value'
  set @xIlkT = 'your test value'
  set @xSonT = 'your test value'
  set @PrevYear = 'your test value'

And debug at will. Append select statements in between sections like:

select @SqlMuh 

To know their values at specific places.

Once you're happy with the changes you've done, go back and ALTER the SP. This will cut down the repetitive task of ALTERING-EXECUTING everytime you want to check something.

Wadih M.
A: 

I had a project like this last year and I found that the best thing to do was to create a table with a identity field and a varchar field and write the @SqlText variable to it just before you EXEC it. That way as you test and see results, you can get the latest SQL that was executed.

Long term, I agree with the majority of the people here that you should re-write this (although that's probably not a good short term solution unless this is the only SP that is like this).

Jeff Hornby
everything is like this .. plus the deplhi code (which i am not a delphi programmer) ;)
AIC