SQL 查询:查找连接的联系人,直到三度深度

SQL query: Finding connected contacts until third degree of depth

我正在使用 PostgreSQL 8.2.15 (Greenplum Database 4.3.3.1 build 1),这意味着不支持使用 WITH RECURSIVE。

有样本table:

select * from reports_Table

reporter spammer
 AAA    BBB
 AAA    CCC
 DDD    CCC
 DDD    BBB
 DDD    EEE
 DDD    FFF
 EEE    DDD
 CCC    AAA
 FFF    DDD
 BBB    AAA
 BBB    CCC
 BBB    DDD

通过sql,我试图获取所有记者以及连接到 AAA 的垃圾邮件发送者的列表,直到第三级深度。在上面的示例中,查询的结果将是:

AAA  
BBB  
CCC  
DDD  
FFF  
EEE  

BBB 和 CCC 直接与 AAA 相连,因此是 AAA 的一级连接,
DDD 是 AAA 的二级连接,因为它是通过 CCC 连接的,
FFF 和 EEE 是 AAA 的三度连接,因为它们都是通过 DDD 连接的。

我已经设法在查询中找到这一点,我认为它在逻辑上是可行的,但无法继续,因为似乎无法理解发生的语法错误:

ERROR: syntax error at or near "WHILE" 

很可能我使用的版本需要不同的 WHILE LOOP 语法,但我似乎无法修复它。

/*supported tables*/
CREATE TEMP TABLE variables as
select 1 as first_column, 'AAA'::text as specific_reporter, 3 as degreeNumber

CREATE TEMP TABLE CollectedReporters(
specific_reporter text
);
GO
INSERT INTO CollectedReporters 
select specific_reporter from variables;

/*main query*/
BEGIN
    WHILE (select degreeNumber from variables) >= 1 LOOP
        INSERT INTO CollectedReporters
            SELECT ct.spammer::text as specific_reporter 
                FROM reports_Table ct
                    INNER JOIN CollectedReporters cc ON ct.reporter = cc.specific_reporter::text
                    LEFT JOIN CollectedReporters cc2 ON ct.spammer = cc2.specific_reporter::text
                WHERE cc2.specific_reporter IS NULL;

        UPDATE variables
            SET degreeNumber = degreeNumber - 1;
    END WHILE;
END;

SELECT * FROM  CollectedReporters

非常感谢任何帮助!

这里的问题是你不了解数据库语法,我建议你覆盖the guide on PG,这样你会更清楚

  • 首先,您只能在 PL/pgSQL 函数内部使用 BEGIN-END 结构,因为它是 PL/pgSQL 语言语法的一部分。在函数外使用时,"BEGIN"表示事务开始,应适当使用
  • 当您通过 GUI 工具 运行 查询时,每个查询都应该是正确的 ANSI SQL 查询。 "while" 不是 ANSI SQL 的一部分。但是,它是 MSSQL 中的 T-SQL、Oracle 中的 PL/SQL 和 Postgres
  • 中的 PL/pgSQL 的一部分
  • 要在 Greenplum 4.3 及更早版本中使用 PL/pgSQL,您只有一个选择 - 创建一个函数。在 Greenplum 版本 5.0+(从 github 开发构建)中,您还可以在 PL/pgSQL
  • 中使用匿名代码块

这是一个示例:

CREATE OR REPLACE FUNCTION my_function (maxlevel int) returns void as $BODY$ 
DECLARE
    level int = 1;
BEGIN

    TRUNCATE CollectedReporters;

    WHILE (level <= maxlevel) LOOP
        RAISE NOTICE 'Processing level %', level;

        INSERT INTO CollectedReporters
            SELECT ct.spammer::text as specific_reporter 
                FROM reports_Table ct
                    INNER JOIN CollectedReporters cc ON ct.reporter = cc.specific_reporter::text
                    LEFT JOIN CollectedReporters cc2 ON ct.spammer = cc2.specific_reporter::text
                WHERE cc2.specific_reporter IS NULL;

        level = level + 1;
    END LOOP;
END;
$BODY$ LANGUAGE PLPGSQL VOLATILE;