tsql - Parameterized SQL with dynamic WHERE column -


i trying write simple filtering, user can input column filter , value. tricky part dynamically choosing column filter.

i've found couple solutions on web , not sure implement. preference lean toward performance instead of maintainability. opinions appreciated.

let's assume have table "t", has 5 varchar columns: "c1", "c2", "c3", "c4", , "c5".

solution 1 - easy way

i use dynamic sql. on lines of:

declare @sql varchar(max) = 'select * t ' + @columnname + ' = ''' + @columnvalue + ''';' exec (@sql); 

which come out like:

select * t c1 = 'asdf' ; 

i don't want use solution following 2 reasons. i'm including simple point of reference before going down rabbit hole.

  1. it not guard against sql injection.
  2. even if parameterize columnvalue, have 5 different execution plans cached each of 5 columns since cannot parameterize @columnname.

solution 2 - or's

could use series of or's 2 parameters. let's say:

@columnname = 'c1' @columnvalue = 'asdf' 

then sql become:

select * t (@columnname = 'c1' , c1 = @columnvalue)   or (@columnname = 'c2' , c2 = @columnvalue)   or (@columnname = 'c3' , c3 = @columnvalue)   or (@columnname = 'c4' , c4 = @columnvalue)   or (@columnname = 'c5' , c5 = @columnvalue)   or (@columnname null , 0 = 0) ; 

i try stay away using or when possible. remember reading somewhere suffers performance issues, i'm no dba , can't up. thoughts?

solution 3 - coalesce

this solution relies on having parameter each column. parameters on lines of:

@c1 = 'asdf'; @c2 = null; @c3 = null; @c4 = null; @c5 = null; 

sql comes out to:

select * t c1 = coalesce(@c1, c1)   , c2 = coalesce(@c2, c2)   , c3 = coalesce(@c3, c3)   , c4 = coalesce(@c4, c4)   , c5 = coalesce(@c5, c5) ; 

does have opinion on method implement? i'm leaning towards coalesce, have no hard numbers or experience on matter. maybe there better way of doing things?

the safest way:

declare @sql nvarchar(max) = n'select * dbo.t '   + quotename(@columnname) + ' = @columnvalue;';  exec sp_executesql @sql, n'@columnvalue varchar(255)', @columnvalue; 

to guard further sql injection, first check:

if @columnname not in (n'c1',n'c2',n'c3',n'c4',n'c5') begin   raiserror('nice try! %s not valid.', 11, 1, @columnname);   return; end 

or @habo suggested, against sys.columns catalog view:

if not exists  (    select 1 sys.columns name = @columnname      , [object_id] = object_id('dbo.t') ) begin   raiserror('nice try! %s not valid.', 11, 1, @columnname);   return; end 

especially when combined optimize ad hoc workloads, ok have 5 different execution plans - since are, after all, 5 different queries optimize differently based on indexes on different columns, distribution of data within columns, etc.

your or , coalesce versions - unless pay compilation hit every single time - stuck using same plan no matter column provided, therefore may work situations, not others. , plan gets not based on what's best parameter sent first.

also, if concerned performance, maybe don't use select * - if don't need of columns. if do, never know when adds blob or geometry or xml or other expensive column table, , code retrieves though doesn't care it.


Comments

Popular posts from this blog

java - Run a .jar on Heroku -

java - Jtable duplicate Rows -

validation - How to pass paramaters like unix into windows batch file -