【问题标题】:SQL Server CTE use IDs from single column with EXCEPT?SQL Server CTE 使用来自单列的 ID,除了?
【发布时间】:2014-07-09 16:20:19
【问题描述】:

前几天我从一个眼睛没有我那么昏暗的人那里得到了善意,我想我会再试一次。提前感谢您的帮助。

我有一个名为 Contacts 的 SQL Server (2012) 表。该表有四列我目前关心。该表共有 71,454 行。表中有两种类型的记录;公司和员工。两者都使用相同的列,命名为 (Client ID),作为它们的主键。公司名称的存在是公司和员工数据之间的区别。员工没有关联的公司名称。有 29,021 家公司离职 42,433 名员工。

可能有 0-n 名员工与任何一家公司相关联。我正在尝试创建能够反映公司和客户之间关系的输出(如果有的话)。我想使用公司 ID(客户 ID 列)作为我的锚数据集。

不确定我的定义是否正确,但我的想法是根据给定的公司名称创建已知公司的 CTE。然后,使用剩余的客户端 ID,但使用 EXCEPT 子句将已经检索到的客户端 ID 从结果集中过滤掉。

这里是我目前拥有的代码;

;
WITH    cte ( BaseID, Client_id, Company_name, 
        First_name, Last_name, [level] )
AS ( SELECT   Client_id AS BaseID ,
            Client_id ,
            Company_name ,
            First_name ,
            Last_name ,
            1
   FROM     dbo.Conv_client_clean
   WHERE    ( COMPANY_NAME IS NOT NULL
              OR COMPANY_NAME != ''
            )
   UNION ALL
   SELECT   c.BaseID ,
            children.Client_id ,
            children.Company_name ,
            children.First_name ,
            children.Last_name ,
            cte.[level] + 1
   FROM     dbo.Conv_client_clean children
            INNER JOIN cte c ON c.Client_id = children.CLIENT_ID
   EXCEPT
   SELECT   children.Client_id
   FROM     cte
 )
SELECT  BaseID ,
Client_id ,
Company_name ,
first_name ,
Last_name ,
[Level]
FROM    cte
OPTION  ( MAXRECURSION 0 );

在这种情况下,我收到以下错误;

消息 252,第 16 级,状态 1,第 3 行 递归公用表表达式“cte”不包含顶级 UNION ALL 运算符。

有什么建议吗?

谢谢!

【问题讨论】:

  • 您引用了cte.[level] 但没有创建[level] 别名,不确定EXCEPT 在递归cte 的上下文中如何工作,这可能有问题。
  • 山羊公司 - 感谢您的回复。我曾想过,也许是错误的,在'[level]'的顶级声明中添加[level](对不起,如果有错误的短语-我是新来的:))我实际上是在创建名称(或如您所说,别名)。在第一个 SELECT 中添加“AS [level]”会产生相同的结果。
  • 您的 where 子句中有一个 nonSARGable 谓词。 WHERE( COMPANY_NAME IS NOT NULL OR COMPANY_NAME != '' ) 您可以通过将其更改为 WHERE COMPANY_NAME > '' 来简化(和 SARGable)
  • 肖恩,谢谢你的建议。在这种情况下,我对优化并不感兴趣,但我会实施您的建议。
  • 当你删除你的EXCEPT子句时它会执行吗?

标签: sql recursion common-table-expression


【解决方案1】:

在递归 cte 查询中,在引用 cte 本身的一个 Union ALL 之后,您不能有更多的集合操作(​​联合、除外、联合全部、相交)。我认为您可以尝试如下更改查询并检查

...
UNION ALL
   SELECT   c.BaseID ,
            children.Client_id ,
            children.Company_name ,
            children.First_name ,
            children.Last_name ,
            cte.[level] + 1
   FROM     dbo.Conv_client_clean children
            WHERE children.Client_id NOT IN (SELECT Client_id FROM cte)

【讨论】:

  • Kiran,一个有价值和崇高的努力。不幸的是,它产生了 Msg 465, Level 16, State 1, Line 22 子查询中不允许递归引用。在等待解脱时:) 我决定采用老式的方法并将其他几张桌子固定在一起。我能够生产出现在已经足够好的东西了,但考虑到这不是生产。我决定将原始问题标记为已回答。当然,如果有人有想法,我会很高兴,但现在我不想浪费任何人的宝贵时间。再次感谢大家。
【解决方案2】:

正如 Kiran 所说,我能够编造出一种“老式”的方法,现在已经足够好了

感谢大家的关注。

【讨论】:

    【解决方案3】:

    我不确定你想用level 做什么。似乎公司是1,员工是2。如果是这样的话,你甚至不需要递归。您的 cte 的第一部分创建公司列表。没关系。现在使用它连接回原始表以显示所有员工。

    WITH
    cte( BaseID, ClientID, Company_name, First_name, Last_name )AS(
      SELECT    Base_ID,
                Base_ID AS Client_id ,
                Company_name,
                First_name,
                Last_name
       FROM     dbo.Conv_client_clean
       WHERE    COMPANY_NAME IS NOT NULL
                  OR COMPANY_NAME <> ''
    )
    select c2.Base_id, c2.Client_id,
           c1.Company_Name, c2.First_Name, c2.Last_Name,
           case when c2.client_id is null then 1 else 2 end Level
    from   cte c1
    join   Conv_client_clean c2
      on   c1.BaseID = isnull( c2.Client_ID, c2.Base_id )
    order by c1.BaseID, c2.Base_id;
    

    这就是我摆弄的地方。

    【讨论】:

      【解决方案4】:

      不幸的是,在您进行递归引用之后,除了 UNION ALL 之外的任何东西都将不起作用。如果你仔细想想,它是有道理的。

      递归在概念上与以下内容相同,其中递归一直持续到达到最大深度或查询不返回可供其他执行操作的结果。

      WITH Anchor AS (select...)
      ,recurse1 as (<Some body referring to Anchor>)
      ,recurse2 as (<Identical body except referring to recurse1>)
      ,recurse3 as (<Identical body except referring to recurse2>)
      ...
      select * from Anchor
      union all
      select * from recurse1
      union all
      select * from recurse2
      ...
      

      问题在于合取运算符适用于它之前的所有内容。在您的情况下, EXCEPT 对其左侧的所有内容都进行操作,其中包括 Anchor 查询。之后,在查找必须应用递归部分的锚点时,查询编译器不再找到“顶级联合所有运算符”,因为它已作为递归查询左侧的一部分使用。

      设计一些类似于括号的语法来限制表连词左侧的范围是没有帮助的,因为你会构建一个“多重递归引用”的情况,这也是非法的。

      底线是:在查询的递归部分中起作用的唯一连词是 UNION ALL,因为它只是连接右侧。它不需要左侧的知识来确定要包含的行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多