【问题标题】:How to turn a multiple self Join query into a recursive CTE如何将多个自联接查询转换为递归 CTE
【发布时间】:2015-05-23 04:00:23
【问题描述】:

我希望有人能慢慢地向我解释这一点,因为从我读过的所有内容来看,我似乎不明白如何做我想做的事。

我有一个包含 IP 和电子邮件列的表。我收到一封电子邮件,我需要查找相应的 Ips,但它并没有到此结束,然后我需要查找那些新 Ips 的电子邮件,然后是这些电子邮件的 Ips,依此类推,直到没有更多新电子邮件和 Ips。

我可以这样做:

SELECT DISTINCT t.CUSTOMER_IP, t.CUSTOMER_EMAIL
   FROM [Main table] t
          INNER JOIN [Main table] t1 ON (t.CUSTOMER_IP = t1.CUSTOMER_IP)
          INNER JOIN [Main table] t2 ON (t1.CUSTOMER_EMAIL = t2.CUSTOMER_EMAIL)
          INNER JOIN [Main table] t3 ON (t2.CUSTOMER_IP = t3.CUSTOMER_IP)
   WHERE t3.CUSTOMER_EMAIL = 'ejskslsks@gmail.com'
           AND t1.CUSTOMER_IP IS NOT NULL
        AND t2.CUSTOMER_IP IS NOT NULL
        AND t3.CUSTOMER_IP IS NOT NULL
        and t.ISSUE_DATE BETWEEN '2015-02-23 00:00:00' AND '2015-02-23 23:59:59' 

到目前为止一切顺利,除了这限制了我的搜索,我需要创建某种递归查询,例如:

WITH iptable as
(
SELECT DISTINCT CUSTOMER_IP, customer_email, 1 as loopy
       FROM [Main table]
       WHERE CUSTOMER_EMAIL = 'ejskslsks@gmail.com'
       AND ISSUE_DATE BETWEEN '2015-02-23 00:00:00' AND '2015-02-23 23:59:59'
       AND CUSTOMER_IP IS NOT NULL
union all
SELECT t.CUSTOMER_IP, t.CUSTOMER_EMAIL, iptable.loopy +1 as loopy
    FROM [Main table] t
              INNER JOIN iptable ON (iptable.CUSTOMER_IP = t.CUSTOMER_IP)
              INNER JOIN [Main table] t1 ON (t.CUSTOMER_EMAIL = t1.CUSTOMER_EMAIL)
    where t.ISSUE_DATE BETWEEN '2015-02-23 00:00:00' AND '2015-02-23 23:59:59' 
    and iptable.loopy <2

)
Select DISTINCT CUSTOMER_IP, CUSTOMER_EMAIL from iptable

loopy列只是为了能够控制迭代次数,在这个例子中只有2个。这只会给我新的电子邮件,因为它不会从这些电子邮件中寻找新的ip。

我不知道如何解决这个问题,我是 SQL 初学者。我还需要提供其他信息吗?也许 CTE 不是最好的方法?我考虑过 WHILE 查询,但我必须使用 temptables,如果可能的话,我想避免使用它们。

提前谢谢你!

【问题讨论】:

  • 你用的是什么关系型数据库?
  • 只需将 2 增加到另一个值,看看会发生什么。
  • @GordonLinoff 是的,我当然试过了,它在第 2 次迭代后给了我相同的结果,只是它重复了 ips 和电子邮件,第 4 次迭代需要很长时间
  • 您可能还想考虑使用脚本或编程工具。 PowerShell 将是一个很好的例子(它是免费的,并且可以访问 .NET 框架),或者如果您在软件商店,您甚至可以编写一个简单的应用程序。您将可以访问一些更简单的逻辑控件,以使循环更易于理解和实现。因为在某些时候,您需要能够停止循环/递归,这样您就不会重新检查您已经检查过的电子邮件。
  • 这个问题听起来像是图遍历,这不是 SQL 的原生优势。在 SQL 中,即使是简单的树遍历也是一个令人头疼的问题。

标签: sql join recursion inner-join common-table-expression


【解决方案1】:

在选择联合下选择时,您需要一种不包含父记录的方法,否则您将拥有大量中间结果集(这会对性能产生负面影响)。这绝对不是 SQL 最好的东西。

另外 - 您可以加入多个条件(CUSTOMER_EMAIL 或 CUSTOMER_IP) - 从而避免多次加入同一个表。

我喜欢让 使用 cte 的 select 语句成为限制结果的语句 - 并且只在 cte 中包含特定于关系的内容。他们一开始就令人头晕目眩。可能是这样的:

with iptable (ROOT_EMAIL, CUSTOMER_IP, CUSTOMER_EMAIL, ISSUE_DATE, DEPTH )as
(
    select top (1)
        parent.CUSTOMER_EMAIL ROOT_EMAIL,
        parent.CUSTOMER_IP,
        parent.CUSTOMER_EMAIL,
        parent.ISSUE_DATE, 
        1 as DEPTH
    from
        [Main table] parent
    where
        parent.CUSTOMER_IP is not null
    order by 
        parent.ISSUE_DATE

    union all

    select
        parent.ROOT_EMAIL,
        child.CUSTOMER_IP,
        child.CUSTOMER_EMAIL,
        child.ISSUE_DATE,
        parent.DEPTH + 1
    from
        [Main table] child
        inner join
        iptable parent
        on
            (
                child.CUSTOMER_EMAIL= parent.CUSTOMER_EMAIL
                or
                child.CUSTOMER_IP = parent.CUSTOMER_IP
            )
            and
            child.ISSUE_DATE > parent.ISSUE_DATE
)
select distinct
    CUSTOMER_EMAIL,
    CUSTOMER_IP
from 
    iptable
where
    ROOT_EMAIL ='ejskslsks@gmail.com'
    and
    ISSUE_DATE between '2015-02-23 00:00:00' and '2015-02-23 23:59:59'

我假设您期望给定电子邮件可能有多个 IP - 并且给定 IP 可能有多个电子邮件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    • 2014-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-26
    • 1970-01-01
    相关资源
    最近更新 更多