2013-06-25 30 views
3

假设我们有一个表T1和一个表T2。 T1和T2之间有1:n的关系。我想选择所有的T1以及它们的所有T2,每一行对应于T2值连接的T1记录,只使用SQL标准操作。如何连接使用标准SQL以逗号分隔的行?

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

每年为一个人有一定的知名度

我想用SQL标准的作业写一个选择,导致东西像这样:

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

那里有3条记录中的普及表约翰·史密斯,没有对李四和一个简·史密斯,他们的价值观是上述表示的值。这可能吗?怎么样?

我正在使用Oracle,但希望仅使用标准SQL来执行此操作。

+0

不要以为你可以在纯SQL92实现它,而不诉诸供应商的特定技巧。想要一个仅有Oracle的解决方案? –

+0

可能的重复[如何将多行组合到Oracle中的逗号分隔列表中?](http://stackoverflow.com/questions/468990/how-can-i-combine-multiple-rows-into-a-逗号分隔列表在oracle中) – dang

+0

我想在Oracle中实现这一点,但是使用SQL标准操作。 –

回答

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语句中实现这一点。

+0

谢谢,解决了这个问题,但它不是与技术无关的,因为你不能在任何SQL实现者技术中使用listagg,它不在SQL标准中。如果我以这种方式解决问题,那么我就不能将我的项目迁移到PostgreSQL。所以这不回答我的问题,但无论如何,我感谢你的尝试。 –

+0

欢迎您,再次我告诉那个如我所知,没有工作围绕在纯SQL做... 但如果你发现任何东西,请更新我也... –

+0

目前,我有N + 1个数据库请求而不是一个,因为只有Oracle才能运行的解决方案在这种情况下是不可接受的。福伊特斯向我们保证他会提供解决方案,所以我正在等待那一个。同时我也在想这个,所以也许我会提供一个解决方案。我们会看到。 –

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    | 
-------------------------------- 
+0

谢谢voithos,我会尽快实现你的想法,并会让你知道结果。如果它符合SQL标准并且有效,我会接受它。非常感谢。 –

+0

@LajosArpad:请注意性能。如果您实施它,请务必使用实际的数据集大小进行测试。 – voithos

+2

我刚刚想出了类似的东西,并在编写代码的过程中表现出我是多么毫无意义的写“数据库无关的”代码。即使像使用'from dual'创建测试数据或者使用'||','+'或'concat'连接字符串这样简单的事情显示它是多么困难。比它的价值更麻烦! –