如何使用标准SQL连接用逗号分隔的行?

时间:2013-06-25 21:19:17

标签: sql ansi-sql

假设我们有一个表T1和一个表T2。 T1和T2之间存在1:n的关系。我想选择所有T1及其所有T2,每行对应于T2值连接的T1记录,仅使用SQL标准操作。

实施例: T1 =人 T2 =人气(按年)

每个人都有一定的人气

我想使用SQL标准操作编写一个选项,结果如下:

Person.Name    Popularity.Value
John Smith     1.2,5,4.2
John Doe       NULL
Jane Smith     8

其中约翰史密斯的受欢迎表中有3条记录,John Doe和Jane Smith没有记录,它们的值是上面表示的值。这可能吗?怎么样?

我正在使用Oracle,但只想使用标准SQL。

3 个答案:

答案 0 :(得分:2)

这是一种使用递归公用表表达式的技术。不幸的是,我对它的表现并不自信。

我确信有很多方法可以改进这些代码,但它表明似乎没有一种简单的方法可以使用SQL标准来做这样的事情。

据我所知,确实应该有某种STRINGJOIN聚合函数与GROUP BY一起使用。这会让这样的事情变得更容易......


此查询假定某种PersonID加入了这两种关系,但Name也会起作用。

WITH cte (id, Name, Value, ValueCount) AS (
    SELECT id,
        Name,
        CAST(Value AS VARCHAR(MAX)) AS Value,
        1 AS ValueCount
    FROM (
        SELECT ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Name) AS id,
            Name,
            Value
        FROM Person AS per
        INNER JOIN Popularity AS pop
            ON per.PersonID = pop.PersonID
    ) AS e
    WHERE id = 1

    UNION ALL

    SELECT e.id,
        e.Name,
        cte.Value + ',' + CAST(e.Value AS VARCHAR(MAX)) AS Value,
        cte.ValueCount + 1 AS ValueCount
    FROM (
        SELECT ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Name) AS id,
            Name,
            Value
        FROM Person AS per
        INNER JOIN Popularity AS pop
            ON per.PersonID = pop.PersonID
    ) AS e
    INNER JOIN cte
        ON e.id = cte.id + 1
            AND e.Name = cte.Name
)
SELECT p.Name, agg.Value
FROM Person p
LEFT JOIN (
    SELECT Name, Value
    FROM (
        SELECT Name,
            Value,
            ROW_NUMBER() OVER (PARTITION BY Name ORDER BY ValueCount DESC)AS id
        FROM cte
    ) AS p
    WHERE id = 1
) AS agg
    ON p.Name = agg.Name

这是一个示例结果:

--------------------------------
| Name        | Value          |
--------------------------------
| John Smith  | 1.2,5,4.2      |
--------------------------------
| John Doe    | NULL           |
--------------------------------
| Jane Smith  | 8              |
--------------------------------

答案 1 :(得分:1)

根据Oracle中的说法,您可以使用listagg来实现此目标 -

select t1.Person_Name, listagg(t2.Popularity_Value)
                        within group(order by t2.Popularity_Value) 
 from t1, t2
where t1.Person_Name = t2.Person_Name (+)
group by t1.Person_Name

我希望这能解决你的问题。

但你在@DavidJashi问题之后给出的评论......这不是sql标准,我认为他是正确的。我也和David一起在纯sql语句中无法实现这一点。

答案 2 :(得分:1)

我知道我参加聚会已经超级迟到了,但是对于可能发现这一点的其他人,我不相信使用纯 SQL92 是可能的。正如我在过去几个月与 NetSuite 争吵以试图弄清楚我可以和不能与他们的 ODBC 驱动程序一起使用的 Oracle 方法时发现的那样,我发现他们只“支持和保证”SQL92 标准。

我发现了这一点,因为我需要执行 LISTAGG()。一旦我发现我只能使用 SQL92,我就对历史记录进行了一些挖掘,而 SQL92 根本不支持 LISTAGG() 和递归查询(公共表表达式)。

LISTAGG() 在 Oracle SQL 11g 第 2 版(2009 – 11 年前:参考 https://oracle-base.com/articles/misc/string-aggregation-techniques#listagg)中添加,CTE 在 Oracle SQL 9.2 版中添加(2007 – 13 年前:参考 {{3 }}).

非常郁闷的是,在纯SQL92中完全不可能实现这种效果,所以我不得不在我拉出大量多余的不必要数据后,在我的C#代码中解决了这个问题。非常令人沮丧。