c# - Parsing T-SQL To Extract Part of WHERE Clause -


i have large sql database containing 'curves'. each curve has id (curveid). i'm trying determine main users of each curve , if used @ all. enable this, dbas providing logs of statements executed against database.

these statements can in complexity. want extract curveids being queried for.

example statement follows:

with g ( select [timevalue] [mc].[granularitylookup]  [timevalue] between '19-jul-2017 00:00' , '30-sep-2017 00:00'  , [1 hr] = 1),  d ( select [curveid], [deliverydate], [publishdate], avg([value]) value, max([periodnumber]) periodnumber  mc.curveid_6657_1_latest data  join  (select curveid id, deliverydate ddate, max(publishdate) pdate  mc.curveid_6657_1_latest curveid = 90564     , deliverydate >= '19-jul-2017 00:00' , deliverydate <= '30-sep-2017 00:00' group deliverydate,  curveid ) dates  on data.deliverydate = dates.ddate , data.publishdate = dates.pdate  data.curveid = 90564 , data.deliverydate >= '19-jul-2017 00:00' , data.deliverydate <= '30-sep-2017 00:00' group [curveid], [publishdate], [deliverydate] ) select  g.[timevalue] [deliver ydate] , d.[publishdate], d.[value], d.[periodnumber]  g left join  d on  g.[timevalue] = d.[deliverydate] order deliverydate asc, periodnumber asc, publishdate desc 

from statement, i'm interested in extracting user queried curveid 90564.

the statement may resemble either of following:

select * anytable curveid = 123 , deliverydate between '2017-01-01' , 2017-02-01' 

or

select * mc.anytable curveid in (1,2,3,4,5,6,7) 

again, want know curve ids. don't care of other clauses.

i'm using microsoft.sqlserver.transactsql.scriptdom namespace parse sql , have got point can identify statements using code similar below (pieced other samples):

string sql = @"with              g ( select [timevalue] [mc].[granularitylookup]              [timevalue] between '19-jul-2017 00:00' , '30-sep-2017 00:00'              , [1 hr] = 1),              d ( select [curveid], [deliverydate], [publishdate], avg([value]) value, max([periodnumber]) periodnumber              mc.curveid_6657_1_latest data              join              (select curveid id, deliverydate ddate, max(publishdate) pdate              mc.curveid_6657_1_latest             curveid = 90564                 , deliverydate >= '19-jul-2017 00:00' , deliverydate <= '30-sep-2017 00:00'             group deliverydate,  curveid ) dates              on data.deliverydate = dates.ddate , data.publishdate = dates.pdate              data.curveid = 90564             , data.deliverydate >= '19-jul-2017 00:00' , data.deliverydate <= '30-sep-2017 00:00'             group [curveid], [publishdate], [deliverydate] )             select              g.[timevalue] [deliver             ydate] , d.[publishdate], d.[value], d.[periodnumber]                          g             left join              d             on              g.[timevalue] = d.[deliverydate]             order deliverydate asc, periodnumber asc, publishdate desc";             var parser = new tsql120parser(false);              ilist<parseerror> errors;             var fragment = parser.parse(new stringreader(sql), out errors);              var wherevisitor = new wherevisitor();             fragment.accept(wherevisitor);              //  have clauses in wherevisitor.wherestatements  class wherevisitor : tsqlconcretefragmentvisitor {     public readonly list<whereclause> wherestatements = new list<whereclause>();      public override void visit(whereclause node)     {         wherestatements.add(node);     }  } 

each of clauses in wherevisitor.wherestatements (3 in example) expose property called searchcondition. unfortunately, run out of ideas. want achieve logic per below:

foreach (var clause in wherevisitor.wherestatements) {     //  if part of clause filters based on curveid      //        capture curveids      //  end if } 

other details:

  • using c# (.net 4.0)
  • sql server 2008
  • dll microsoft.sqlserver.transactsql.scriptdom (located in case @ 'c:\program files (x86)\microsoft sql server\130\tools\powershell\modules\sqlps\microsoft.sqlserver.transactsql.scriptdom.dll')

edit 1

some additional info:

  • curveid key table. not make sense in case operate on (e.g. curveid+1 or curveid <= 10).

edit 2 (partial solution)

having following visitor helps case clause resembles curveid = 123:

class curveidvisitor : tsqlconcretefragmentvisitor {     public readonly list<int> curveids = new list<int>();      public override void visit(booleancomparisonexpression exp)     {         if (exp.firstexpression columnreferenceexpression && exp.secondexpression integerliteral )         {             //  there possibility of ilk 'curveid = 123'             //  'identifier'             //  take last if there multiple.  example:             //      alias.curveid             //  goives 2 identifiers: alias , curveid             if (                 ((columnreferenceexpression) exp.firstexpression).multipartidentifier.identifiers.last().value.tolower() ==                 "curveid")             {                 //  curveid filter                 //  find curve id                 int curveid = int.parse(((integerliteral) exp.secondexpression).value);                 curveids.add(curveid);             }         } 

finally solved , hope benefits else in future. perhaps else may read ni time , provide better solution.

public class sqlparser {     public list<int> getqueriedcurveids(string sql)     {         var parser = new tsql120parser(false);          ilist<parseerror> errors;         var fragment = parser.parse(new stringreader(sql), out errors);          list<int> curveids = new list<int>();         curveidvisitor cidv = new curveidvisitor();         inpredicatevisitor inpv = new inpredicatevisitor();         fragment.acceptchildren(cidv);         fragment.acceptchildren(inpv);          curveids.addrange(cidv.curveids);         curveids.addrange(inpv.curveids);         return curveids.distinct().tolist();     } }    class curveidvisitor : tsqlconcretefragmentvisitor {     public readonly list<int> curveids = new list<int>();      public override void visit(booleancomparisonexpression exp)     {         if (exp.firstexpression columnreferenceexpression && exp.secondexpression integerliteral )         {             //  there possibility of ilk 'curveid = 123'             //  'identifier'             //  take last if there multiple.  example:             //      alias.curveid             //  goives 2 identifiers: alias , curveid             if (                 ((columnreferenceexpression) exp.firstexpression).multipartidentifier.identifiers.last().value.tolower() ==                 "curveid")             {                 //  curveid filter                 //  find curve id                 int curveid = int.parse(((integerliteral) exp.secondexpression).value);                 curveids.add(curveid);             }         }     } }  class inpredicatevisitor : tsqlconcretefragmentvisitor {     public readonly list<int> curveids = new list<int>();      public override void visit(inpredicate exp)     {         if (exp.expression columnreferenceexpression)         {             if (                 ((columnreferenceexpression) exp.expression).multipartidentifier.identifiers.last().value.tolower() ==                 "curveid")             {                 foreach (var value in exp.values)                 {                     if (value integerliteral)                     {                         curveids.add(int.parse(((integerliteral)value).value));                     }                 }             }         }     } } 

this cut down code demonstrate answer. in real life, want check parseerror collection , add error handling!


Comments

Popular posts from this blog

networking - Vagrant-provisioned VirtualBox VM is not reachable from Ubuntu host -

c# - ASP.NET Core - There is already an object named 'AspNetRoles' in the database -

ruby on rails - ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true -