3

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

示例:T1 = 人 T2 = 受欢迎程度(按年份)

每一年一个人都有一定的人气

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

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

其中 John Smith 的流行度表中有 3 条记录,John Doe 没有一条记录,Jane Smith 有一条记录,它们的值是上面表示的值。这可能吗?如何?

我正在使用 Oracle,但只想使用标准 SQL 来执行此操作。

4

3 回答 3

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              |
--------------------------------
于 2013-06-25T22:28:49.367 回答
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 标准,我认为他是正确的。我也和大卫一样,你不能在纯 sql 语句中实现这一点。

于 2013-06-25T22:00:40.060 回答
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 年前:参考https://www.databasestar.com/sql-cte-with/)。

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

于 2021-03-05T20:07:04.727 回答