【问题标题】:Recursive query with CTE - Concatenate all fields of child elements and add to parent使用 CTE 进行递归查询 - 连接子元素的所有字段并添加到父元素
【发布时间】:2016-10-03 04:55:23
【问题描述】:

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

我已将 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

样本数据

期望的输出

(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'))

【问题讨论】:

标签: sql-server


【解决方案1】:

不确定这个任务是否可以通过单个递归查询来完成:

1

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

从下到上移动更容易 - 当处理特定级别的条件树时,您肯定可以用括号括起来。然后将其用作完整的代码块。

2

第二件事是您不需要连接级别或构建树 - 您需要折叠所有嵌套元素以获得上层元素的组合代码。因此,正如您在示例递归代码中所述,您必须同时向下(或从底部移动到顶部时向上)到下一个级别 - 聚合当前级别的所有嵌套级别。

有点像

;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 中使用 recursive 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'))

它们在逻辑上是相同的。

【讨论】:

  • 感谢伊万·斯塔罗斯汀。您的解决方案效果很好。您的解决方案中是否需要处理最低级别的第一次更新?即使没有它,我也会得到正确的结果。
  • 也只是为了知识,这个解决方案是否可以使用递归查询(使用单个或多个 CTE)
  • “是否需要第一次更新” - 不确定,可能某些代码太复杂了。如果你能简化它就好了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-19
  • 2014-06-03
  • 1970-01-01
  • 2013-10-31
  • 1970-01-01
  • 2015-12-18
  • 1970-01-01
相关资源
最近更新 更多