从xml生成动态where子句

时间:2012-01-03 14:14:54

标签: sql-server xml

我需要将xml发送到商店程序。 xml看起来像

<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>  
  1. SearchField标记包含将在其上进行搜索的搜索字段名称
  2. FilterCondition标记将保留我用来搜索和比较的运算符,例如'%''&lt; =''&lt; ='etc
  3. ConditionData标签将是我们将搜索的实际值
  4. MatchCase表示它区分大小写
  5. 我可以加载xml并迭代while循环游标并生成where子句。我的问题是如何根据字段数据类型生成where子句。

    如果字段是字符串,那么我们总是在''单引号中进行比较 如果字段是数字或位类型,那么我们可以比较没有单引号 如果该字段是日期时间,那么我想比较喜欢 convert(@dob,varchar,112) > '20100101'

    所以我的问题是如何基于数据类型生成where子句。 我需要动态获取字段数据类型,并相应地构建where子句。 请分享最好的主意。感谢


    我改变了我的xml位。我为每个字段都包含表名。所以请告诉我在这个sql中需要更改的内容,以显示每个字段的数据类型。显示我需要加入系统表的数据类型。所以请在你的sql中进行必要的更改,以便与sys table一起加入fetch&amp;显示每个字段的数据类型。

    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) 
    

    这是我的解决方案。

    /*
    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
    

1 个答案:

答案 0 :(得分:1)

2个小东西,1&lt; &安培; &GT;在XML标记中是非常非法的,如果你想要可靠地解析它,生成XML需要将这些编码到approriate实体引用:

&lt;    <    less than
&gt;    >    greater than
&amp;   &    ampersand
&apos;  '    apostrophe
&quot;  "    quotation mark

此外,您的结束节点和开放节点有一个拼写错误,例如&lt; param&gt;被&lt; params&gt;关闭(注意'S') - 假设这是一个错字。

尽管如此,最难的部分是将XML解析为可用于构建动态SQL的有用数据。您可以通过将XML解析为表来执行此操作:

DECLARE @tbl_WhereClause AS TABLE (
    SearchField VARCHAR(255),
    Operator VARCHAR(25),
    ConditionData VARCHAR(MAX),
    MatchCase BIT
)

DECLARE @WhereClause_XML XML 
SET @WhereClause_XML = '
<NewDataSet>   
<param>   
<SearchField>EmployeeID</SearchField>   
<FilterCondition> &gt;= </FilterCondition>   
<ConditionData>201</ConditionData>   
<MatchCase>0</MatchCase>   
</param> 
<param>     
<SearchField>DeptID</SearchField>   
<FilterCondition> = </FilterCondition>   
<ConditionData>AC01</ConditionData>   
<MatchCase>0</MatchCase>   
</param>   
<param>     
<SearchField>Dob</SearchField>   
<FilterCondition> &lt;= </FilterCondition>   
<ConditionData>23-MAR-2010</ConditionData>   
<MatchCase>0</MatchCase>   
</param>   
</NewDataSet>'

INSERT INTO @tbl_WhereClause (SearchField, Operator, ConditionData, MatchCase)
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' ) ConditionData
FROM    @WhereClause_XML.nodes('/NewDataSet/param') A(B) 

SELECT * FROM @tbl_WhereClause

一旦你获得了解析数据,它只是一个简单的案例(如果不是单调乏味)迭代表格并构建where clase字符串 - 如果你需要这方面的帮助,请告诉我。