i need to send a xml to a store procedure. the xml would look like
<NewDataSet>
<param>
<SearchField>EmployeeID</SearchField>
<FilterCondition> >= </FilterCondition>
<ConditionData>201</ConditionData>
<MatchCase>0</MatchCase>
</params>
<param>
<SearchField>DeptID</SearchField>
<FilterCondition> = </FilterCondition>
<ConditionData>AC01</ConditionData>
<MatchCase>0</MatchCase>
</params>
<param>
<SearchField>Dob</SearchField>
<FilterCondition> <= </FilterCondition>
<ConditionData>23-MAR-2010</ConditionData>
<MatchCase>0</MatchCase>
</params>
</NewDataSet>
- SearchField tag hold the search field name on which search will occur
- FilterCondition tag will hold the operator i use to search and compare like ‘%’ ‘<=’ ‘<=’ etc
- ConditionData tag will actual value which we will search
- MatchCase indicate it will case sensitive or not
i can load the xml and iterate in while loop of cursor and generate a where clause. my question is how can i generate where clause based on field data type.
if the field is string then we always compare like with in ” single quote
if the field is numeric or bit type then we can compare without single quote
if the field is date time then i like to compare like
convert(@dob,varchar,112) > '20100101'
so my question is how to generate where clause based on data type.
i need to fetch the field data type dynamically and accordingly build the where clause.
please share the best idea. thanks
i change my xml bit. i include table name for every field. so please tell me what i need to change in this sql to show the data type for every field. to show the data type i need to join with system table. so please do necessary change in ur sql for join with sys table to fetch & show the data type for each field.
INSERT INTO @tbl_WhereClause (SearchField, Operator, ConditionData, MatchCase,TableName)
SELECT A.B.value('(SearchField)[1]', 'VARCHAR(255)' ) SearchField,
A.B.value('(FilterCondition)[1]', 'VARCHAR(25)' ) Operator,
A.B.value('(ConditionData)[1]', 'VARCHAR(MAX)' ) ConditionData,
A.B.value('(MatchCase)[1]', 'BIT' ) MatchCase,
A.B.value('(Table)[1]', 'VARCHAR(MAX)' ) TableName
FROM @WhereClause_XML.nodes('/NewDataSet/param') A(B)
here is my solution.
/*
XML DATA SAMPLE
<NewDataSet>
<param>
<SearchField>EmpID</SearchField>
<FilterCondition> >= </FilterCondition>
<ConditionData>201</ConditionData>
<MatchCase>0</MatchCase>
<Table>Employee</Table>
</param>
<param>
<SearchField>DeptName</SearchField>
<FilterCondition> = </FilterCondition>
<ConditionData>AC01</ConditionData>
<MatchCase>1</MatchCase>
<Table>Department</Table>
</param>
<param>
<SearchField>Dob</SearchField>
<FilterCondition> >= </FilterCondition>
<ConditionData>20120104</ConditionData>
<MatchCase>0</MatchCase>
<Table>Employee</Table>
</param>
</NewDataSet>'
*/
CREATE PROCEDURE GenericSearch
(
@WhereClause_XML XML,
@LogicalOperator VARCHAR(3)
)
AS
BEGIN
DECLARE @SearchField VARCHAR(255),
@Operator VARCHAR(25),
@ConditionData VARCHAR(MAX),
@MatchCase BIT,
@TableName VARCHAR(MAX),
@Validity VARCHAR(100),
@ColumnType VARCHAR(128),
@ColumnPrecision INT,
@ColumnScale INT,
@ColumnNullable bit,
@WhereClause VARCHAR(MAX)
DECLARE @tbl_WhereClause AS TABLE
(
SearchField VARCHAR(255),
Operator VARCHAR(25),
ConditionData VARCHAR(MAX),
MatchCase BIT,
TableName VARCHAR(MAX),
Validity VARCHAR(100),
ColumnType VARCHAR(128),
ColumnPrecision INT,
ColumnScale INT,
ColumnNullable bit
)
INSERT INTO @tbl_WhereClause (SearchField, Operator, ConditionData, MatchCase,TableName,
Validity, ColumnType, ColumnPrecision, ColumnScale, ColumnNullable)
SELECT A.B.value('(SearchField)[1]', 'VARCHAR(255)' ) SearchField,
A.B.value('(FilterCondition)[1]', 'VARCHAR(25)' ) Operator,
A.B.value('(ConditionData)[1]', 'VARCHAR(MAX)' ) ConditionData,
A.B.value('(MatchCase)[1]', 'BIT' ) MatchCase,
A.B.value('(Table)[1]', 'VARCHAR(MAX)' ) TableName,
CASE WHEN t.NAME+c.NAME IS NULL THEN 'invalid' ELSE 'valid' END ,
ty.NAME,
c.PRECISION,
c.Scale,
c.Is_Nullable
FROM @WhereClause_XML.nodes('/NewDataSet/param') A(B)
LEFT JOIN sys.tables t ON t.name = A.B.value('(Table)[1]', 'VARCHAR(MAX)' )
LEFT JOIN sys.COLUMNS c ON T.OBJECT_ID = c.OBJECT_ID AND c.name = A.B.value('(SearchField)[1]', 'VARCHAR(255)' )
LEFT JOIN sys.types ty ON c.system_type_id = ty.system_type_id
--SELECT * FROM @tbl_WhereClause
SET @WhereClause= 'WHERE 1=1'
DECLARE SearchCursor CURSOR FOR
SELECT * FROM @tbl_WhereClause
OPEN SearchCursor
FETCH NEXT FROM SearchCursor
INTO @SearchField, @Operator,@ConditionData,@MatchCase,@TableName,@Validity,@ColumnType,@ColumnPrecision,@Colu mnScale,@ColumnNullable
WHILE @@FETCH_STATUS = 0
BEGIN
IF CHARINDEX('INT', UPPER(@ColumnType)) > 0
BEGIN
SET @WhereClause = @WhereClause + space(1)+ @LogicalOperator + space(1) + @SearchField + space(1)+@Operator+space(1)+@ConditionData
END
ELSE IF CHARINDEX('NUMERIC', UPPER(@ColumnType)) > 0
BEGIN
SET @WhereClause = @WhereClause + space(1)+ @LogicalOperator + space(1) + @SearchField + space(1)+@Operator+space(1)+@ConditionData
END
ELSE IF CHARINDEX('BIT', UPPER(@ColumnType)) > 0
BEGIN
SET @WhereClause = @WhereClause + space(1)+ @LogicalOperator + space(1) + @SearchField + space(1)+@Operator+space(1)+@ConditionData
END
ELSE IF CHARINDEX('DECIMAL', UPPER(@ColumnType)) > 0
BEGIN
SET @WhereClause = @WhereClause + space(1)+ @LogicalOperator + space(1) + @SearchField + space(1)+@Operator+space(1)+@ConditionData
END
ELSE IF CHARINDEX('FLOAT', UPPER(@ColumnType)) > 0
BEGIN
SET @WhereClause = @WhereClause + space(1)+ @LogicalOperator + space(1) + @SearchField + space(1)+@Operator+space(1)+@ConditionData
END
ELSE IF CHARINDEX('REAL', UPPER(@ColumnType)) > 0
BEGIN
SET @WhereClause = @WhereClause + space(1)+ @LogicalOperator + space(1) + @SearchField + space(1)+@Operator+space(1)+@ConditionData
END
ELSE IF CHARINDEX('MONEY', UPPER(@ColumnType)) > 0
BEGIN
SET @WhereClause = @WhereClause + space(1)+ @LogicalOperator + space(1) + @SearchField + space(1)+@Operator+space(1)+@ConditionData
END
ELSE IF CHARINDEX('DATE', UPPER(@ColumnType)) > 0
BEGIN
SET @WhereClause = @WhereClause + space(1)+ @LogicalOperator + space(1) + 'CONVERT(varchar,'+@SearchField+',112)' + space(1)+@Operator+'CONVERT(varchar,'''+@ConditionData+''',112)'
END
ELSE IF CHARINDEX('CHAR', UPPER(@ColumnType)) > 0
BEGIN
SET @WhereClause = @WhereClause + space(1)+ @LogicalOperator + space(1) + @SearchField + space(1)+@Operator+''''+@ConditionData+''''
END
FETCH NEXT FROM SearchCursor
INTO @SearchField, @Operator,@ConditionData,@MatchCase,@TableName,@Validity,@ColumnType,@ColumnPrecision,@ColumnScale,@ColumnNullable
END
CLOSE SearchCursor
DEALLOCATE SearchCursor
SELECT @WhereClause
END
2 small things, 1 < & > etc are very illegal in XML markup, if you want to reliably parse it, whatever is generating the XML will need to encode these to the approriate entity reference:
Also, your closing nodes and opening nodes have a typo, for instance <param> is closed by <params> (note the ‘S’) – assuming this is a typo.
That said, the hardest part is parsing the XML into usefull data that you can use to build your dynamic SQL. You could do this by parsing the XML into a table:
Once you have the parsed data, its just an easy case (if not tedious) of iterating through the table and building the where clase string – let me know if you need assistance with that part.