使用CTE进行递归查询 - 连接子元素的所有字段并添加到父元素

时间:2016-10-03 04:55:23

标签: sql-server

我必须通过格式化包含员工详细信息的xml来构建一个WHERE子句来搜索员工。

我已将xml数据移动到临时表,并使用CTE查询来获取每个节点描述。我面临的问题是将子元素分组为父级,必须为多个级别完成。

最终条件为Null的父元素必须填充子项的最终条件,直到顶层,如下面的屏幕截图所示。

请在下面找到sql查询和所需的输出:

DECLARE @SearchCriteriaXML XML

DECLARE @TempTable TABLE
(
       ParentName   NVARCHAR(250)
       ,LocalName NVARCHAR(250)
       ,FilterType NVARCHAR(MAX)
       ,Property NVARCHAR(250)
       ,Comparator NVARCHAR(250)
       ,ComparatorCondition NVARCHAR(250)
       ,ComparatorSign NVARCHAR(250)
       ,value NVARCHAR(MAX)
       ,InternalType NVARCHAR(MAX)
       ,FinalCondition NVARCHAR(MAX)
       ,Id  int 
       ,Parentid int      
);

SET @SearchCriteriaXML = 
N'<searchcriteria>
    <filter type=''and''>
         <filter type=''or''>
            <filter type=''and''>
                <condition property =''group'' comparator =''equals'' value=''Regional West Sales Team'' />
                <condition property =''group'' comparator =''equals'' value=''Everyone Christmas 2016'' not=''true'' />
                <filter type=''or''>
                    <condition property =''lastname'' comparator =''equals'' value=''John'' />
                    <condition property =''lastname'' comparator =''equals'' value=''Miller'' />
                </filter>
                <condition property =''State'' comparator =''equals'' value=''California'' />
            </filter>
            <filter type=''and''>
                <condition property =''group'' comparator =''equals'' value=''Metro West Sales Team'' />
                <condition property =''group'' comparator =''equals'' value=''Everyone Christmas 2016 1'' not=''true'' />
                <filter type=''or''>
                    <condition property =''lastname'' comparator =''equals'' value=''John 1'' />
                    <condition property =''lastname'' comparator =''equals'' value=''Miller 1'' />
                </filter>
                <condition property =''State'' comparator =''equals'' value=''Virginia'' />
            </filter>
        </filter>
        <condition property =''company'' comparator =''equals'' value=''Test Company'' />
    </filter>
</searchcriteria>'

DECLARE @idoc int, @doc varchar(1000); 
EXEC sp_xml_preparedocument @idoc OUTPUT, @SearchCriteriaXML;   

--SELECT *  into #temp
--FROM OPENXML (@idoc, 'searchcriteria//*')  

 ;WITH CTE 
 AS( 
-- SELECT stmt using OPENXML rowset provider  
SELECT *  
FROM   OPENXML (@idoc, '/searchcriteria//*')  
WITH
(
       ParentName NVARCHAR(400) '@mp:parentlocalname'
       ,LocalName NVARCHAR(400) '@mp:localname'
       ,Prefix NVARCHAR(200) '@mp:prev'
       ,[type] NVARCHAR(100) '@type'
       ,[Internaltype] NVARCHAR(100) '../@type'
       ,[property] NVARCHAR(100) '@property'
       ,[comparator] NVARCHAR(250) '@comparator'
       ,[comparatorCondition] NVARCHAR(250) '@not'
       ,[value] NVARCHAR(250) '@value',
       [id] int '@mp:id',
       [parentid] int '@mp:parentid'

))
INSERT INTO @TempTable

SELECT ParentName,LocalName,[type] AS FilterType,property,comparator,[comparatorCondition],
CASE 
       WHEN comparator = 'equals' AND [comparatorCondition] = 'true'  THEN '<>' 
       WHEN comparator = 'equals' THEN '=' 
       WHEN comparator = 'greaterthan' THEN '>'
       WHEN comparator = 'lessthan' THEN '<'
       WHEN comparator = 'greaterthanorequal' THEN '>='
       WHEN comparator = 'lessthanorequal' THEN '<='
END AS ComparatorSign
,value
,[Internaltype]
, NULL AS FinalCondition,
[id],
[parentid]

FROM CTE ORDER BY [parentid] DESC

UPDATE @TempTable SET 
FinalCondition = 
       CASE WHEN LocalName = 'condition' THEN ISNULL(property,'') + ' ' + ISNULL(ComparatorSign,'') + ' '+ ''''+ISNULL(value,'')+'''' END

select * from @TempTable

;With EmployeeDetails (Id, ParentId, FilterType, FinalCondition, Level)
AS
(
    SELECT Id, Parentid, FilterType, FinalCondition, Plevel = 1 FROM @TempTable WHERE Parentid = 0
    UNION ALL
    SELECT t.Id, t.Parentid, t.FilterType, t.FinalCondition,
    Plevel = e.Level + 1 FROM @TempTable t INNER JOIN EmployeeDetails e on e.Id = t.ParentId 
)
select * from EmployeeDetails

样本数据

Data Screenshot

所需的输出

(company = 'Test Company') and 
((group = 'Metro West Sales Team' and group <> 'Everyone Christmas 2016 1' and (lastname = 'John 1' or lastname = 'Miller 1') and State = 'Virginia') or 
(group = 'Regional West Sales Team' and group <> 'Everyone Christmas 2016' and (lastname = 'John1' or lastname = 'Miller1') and State = 'California'))

1 个答案:

答案 0 :(得分:0)

不确定是否可以使用单个递归查询完成此任务:

1

首先 - 你必须从下到上移动,而不是试图从上到下处理这个树(当你从where parentid = 0的代码开始时。那是因为你不知道有多少个括号你必须在访问下一个(更低)级别之前打开。

从底部移动到顶部使得它变得更加容易 - 当处理特定级别的条件树时,您肯定可以使用括号来包含它。然后将其用作完整的代码块。

2

第二件事是你需要不加入关卡或构建树 - 你需要折叠所有嵌套元素以获得上层元素的组合代码。因此,正如您在示例递归代码中所述,您必须下降(或者从下往上移动时上升)到下一级并同时 - 聚合当前的所有嵌套级别。 / p>

这样的东西
;with my_cte as
(
  select a, b, c
  from MyTable
  where parentid is null

  union all

  select a, b, group_concat(t.c)
  from MyTable t
  inner join my_cte tt
  on t.parent_id = tt.id
  group by t.parent_id
)

FOR XML子查询,这些都不可能在SQL SERVER中使用递归CTE

3

现在,我尝试完成这项任务:

DECLARE @SearchCriteriaXML XML

DECLARE @TempTable TABLE
(
       ParentName   NVARCHAR(250)
       ,LocalName NVARCHAR(250)
       ,FilterType NVARCHAR(MAX)
       ,Property NVARCHAR(250)
       ,Comparator NVARCHAR(250)
       ,ComparatorCondition NVARCHAR(250)
       ,ComparatorSign NVARCHAR(250)
       ,value NVARCHAR(MAX)
       ,InternalType NVARCHAR(MAX)
       ,FinalCondition NVARCHAR(MAX)
       ,Id  int 
       ,Parentid INT
       ,processed BIT
);

SET @SearchCriteriaXML = 
N'<searchcriteria>
    <filter type=''and''>
         <filter type=''or''>
            <filter type=''and''>
                <condition property =''group'' comparator =''equals'' value=''Regional West Sales Team'' />
                <condition property =''group'' comparator =''equals'' value=''Everyone Christmas 2016'' not=''true'' />
                <filter type=''or''>
                    <condition property =''lastname'' comparator =''equals'' value=''John'' />
                    <condition property =''lastname'' comparator =''equals'' value=''Miller'' />
                </filter>
                <condition property =''State'' comparator =''equals'' value=''California'' />
            </filter>
            <filter type=''and''>
                <condition property =''group'' comparator =''equals'' value=''Metro West Sales Team'' />
                <condition property =''group'' comparator =''equals'' value=''Everyone Christmas 2016 1'' not=''true'' />
                <filter type=''or''>
                    <condition property =''lastname'' comparator =''equals'' value=''John 1'' />
                    <condition property =''lastname'' comparator =''equals'' value=''Miller 1'' />
                </filter>
                <condition property =''State'' comparator =''equals'' value=''Virginia'' />
            </filter>
        </filter>
        <condition property =''company'' comparator =''equals'' value=''Test Company'' />
    </filter>
</searchcriteria>'

DECLARE @idoc int, @doc varchar(1000); 
EXEC sp_xml_preparedocument @idoc OUTPUT, @SearchCriteriaXML;   

;WITH CTE AS( 
    SELECT *
    FROM   OPENXML (@idoc, '/searchcriteria//*')  
    WITH
    (
           ParentName NVARCHAR(400) '@mp:parentlocalname'
           ,LocalName NVARCHAR(400) '@mp:localname'
           ,Prefix NVARCHAR(200) '@mp:prev'
           ,[type] NVARCHAR(100) '@type'
           ,[Internaltype] NVARCHAR(100) '../@type'
           ,[property] NVARCHAR(100) '@property'
           ,[comparator] NVARCHAR(250) '@comparator'
           ,[comparatorCondition] NVARCHAR(250) '@not'
           ,[value] NVARCHAR(250) '@value',
           [id] int '@mp:id',
           [parentid] int '@mp:parentid'

    )
)
INSERT INTO @TempTable(ParentName,
    LocalName,
    FilterType,
    PROPERTY,
    Comparator,
    ComparatorCondition,
    ComparatorSign,
    VALUE,
    InternalType,
    FinalCondition,
    Id,
    Parentid)
SELECT
    ParentName,
    LocalName,[type] AS FilterType,property,comparator,[comparatorCondition],
    CASE 
           WHEN comparator = 'equals' AND [comparatorCondition] = 'true'  THEN '<>' 
           WHEN comparator = 'equals' THEN '=' 
           WHEN comparator = 'greaterthan' THEN '>'
           WHEN comparator = 'lessthan' THEN '<'
           WHEN comparator = 'greaterthanorequal' THEN '>='
           WHEN comparator = 'lessthanorequal' THEN '<='
    END AS ComparatorSign
    ,value
    ,[Internaltype]
    , NULL AS FinalCondition,
    [id],
    [parentid]
FROM CTE ORDER BY [parentid] DESC

UPDATE t SET 
    FinalCondition = 
       CASE WHEN LocalName = 'condition' THEN ISNULL(property,'') + ' ' + ISNULL(ComparatorSign,'') + ' '+ ''''+ISNULL(value,'')+'''' END
FROM @TempTable t

-- processing the lowest level
UPDATE  t
SET     FinalCondition = CAST( '(' AS VARCHAR(MAX)) + STUFF(
            (
                SELECT  ' ' + REPLICATE(' ', 3 -LEN(tf.InternalType)) + tf.InternalType + ' ' + tf.FinalCondition
                FROM    @TempTable tf
                WHERE   tf.parentid = t.id
                FOR XML PATH(''), TYPE
            ).value('.', 'VARCHAR(MAX)'), 1, 4, '') + ')',
        Processed = 1
FROM    @TempTable t
WHERE   NOT EXISTS(
            SELECT  1
            FROM    @TempTable t3   
                    INNER JOIN  @TempTable t2
                        ON  t2.Parentid = t3.id
            WHERE   t3.Parentid = t.Id
        )
        AND t.FinalCondition IS NULL

-- moving from bottom to top of the condition tree
WHILE @@ROWCOUNT > 0
BEGIN
    UPDATE  t
    SET     FinalCondition = CAST('(' AS VARCHAR(MAX)) + STUFF(
                (
                    SELECT  ' ' + REPLICATE(' ', 3 -LEN(tf.InternalType)) + tf.InternalType + ' ' + tf.FinalCondition
                    FROM    @TempTable tf
                    WHERE   tf.parentid = t.id
                            AND tf.FinalCondition IS NOT NULL
                    FOR XML PATH(''), TYPE
                ).value('.', 'VARCHAR(MAX)'), 1, 4, '') + ')',
            Processed = 1
    FROM    @TempTable t
    WHERE   t.FinalCondition IS NULL
        AND NOT EXISTS(SELECT 1 FROM @TempTable tt WHERE tt.Parentid = t.Id AND tt.FinalCondition IS NULL)
END

SELECT  FinalCondition
FROM    @TempTable
WHERE Parentid = 0

我没有仔细审查所有可能和最有效的解决方案,但无论如何,主要的想法是:   - 处理最低级别的条件树   - 转到上一级并组合/汇总较低级别的处理条件

所以最后的select给出了:

(
  ( 
    ( group = 'Regional West Sales Team' and group <> 'Everyone Christmas 2016' and ( lastname = 'John'  or lastname = 'Miller') and State = 'California')  
    or ( group = 'Metro West Sales Team' and group <> 'Everyone Christmas 2016 1' and ( lastname = 'John 1'  or lastname = 'Miller 1') and State = 'Virginia')
  ) 
  and company = 'Test Company'
)
当然,

没有线刹和格式化。 请求的结果是:

(company = 'Test Company') and
((group = 'Metro West Sales Team' and group <> 'Everyone Christmas 2016 1' and (lastname = 'John 1' or lastname = 'Miller 1') and State = 'Virginia') or
(group = 'Regional West Sales Team' and group <> 'Everyone Christmas 2016' and (lastname = 'John1' or lastname = 'Miller1') and State = 'California'))

在逻辑上是相同的。