如何使用group by仅选择一列?

时间:2012-12-28 19:35:38

标签: sql sql-server-2008 crud

我正在尝试从每个门户网站中选择一个行(按日期排序的最后一行),但我遇到的问题是/ by distinct

使用此代码,我只能选择我需要的portalId,但没有任何数据

Select relNews.PortalId
from news
left join relNews on relNews.NewsId= news.NewsId
group by relNews.PortalId

当我在此代码中添加一个或多个数据列时,select会显示所有信息,而不是每个门户一个

Select relNews.PortalId, news.NewsId
from news
left join relNews on relNews.NewsId= news.NewsId
group by relNews.PortalId, news.NewsId

我知道这是我在这里失踪的一个小技巧,但我不记得是什么......

更新

让我们为这个例子制作虚拟表。表格为newsrelNews(我在这里尽可能地缩短了表格)

表新闻

  • NewSID的
  • 标题
  • 描述
  • 日期

表格relNews

  • RelNewsId
  • NewSID的
  • PortalId

注意:

  • relNews可以拥有相同NewsId的N个注册表
  • 我需要为每个portalId选择最后一个寄存器(基于news.Date)。

让我们说:

表新闻

  • NewsId == 1
  • 标题=='测试'
  • 说明=='test'
  • 日期=='2013-01-01 00:00:00'

  • NewsId == 2

  • 标题=='test2'
  • 说明=='test2'
  • 日期=='2013-01-01 03:00:00'

  • NewsId == 3

  • 标题=='test3'
  • 说明=='test3'
  • 日期=='2013-01-02 00:00:00'

表格relNews

  • RelNewsId == 1
  • NewsId == 1
  • PortalId == 1

  • RelNewsId == 2

  • NewsId == 1
  • PortalId == 2

  • RelNewsId == 3

  • NewsId == 2
  • PortalId == 1

  • RelNewsId == 4

  • NewsId == 3
  • PortalId == 3

这些数据应该带来:

RelNewsId == 2 ; RelNewsId == 3 ; RelNewsId == 4 ;


我可以使用此代码获得我想要的结果:

Select top 1 relNews.PortalId, news.NewsId, news.date
from news
left join relNews on relNews.NewsId= news.NewsId
where relNews.PortalId == 1
group by relNews.PortalId, news.NewsId
order by news.date desc
UNION
Select top 1 relNews.PortalId, news.NewsId, news.date
from news
left join relNews on relNews.NewsId= news.NewsId
where relNews.PortalId == 2
group by relNews.PortalId, news.NewsId
order by news.date desc
UNION
Select top 1 relNews.PortalId, news.NewsId, news.date
from news
left join relNews on relNews.NewsId= news.NewsId
where relNews.PortalId == 3
group by relNews.PortalId, news.NewsId
order by news.date desc

然后我得到了所有3个结果。

4 个答案:

答案 0 :(得分:4)

当一行news行可能有多个时,您必须提供一些方法来指示哪个您想要的relNews行。正如您所发现的那样,当您GROUP BY一列可以为每个父项创建多个值时,您就会得到多行。有几种方法可以做到这一点。由于您使用的是SQL Server 2008,因此您有多种选择。

  1. CROSS / OUTER APPLY - 如果您想在没有匹配的{{1}时排除OUTER APPLY行,请将CROSS APPLY更改为relNews行。

    News
  2. <强> ROW_NUMBER()

    SELECT
       R.Whatever,
       N.Whatever
    FROM
       dbo.relNews R
       OUTER APPLY (
          SELECT TOP 1 *
          FROM dbo.News N
          WHERE R.newsId = N.Id
          ORDER BY N.newsDate DESC
       ) N
    
  3. 汇总 - 这比看起来要复杂得多,但我认为SELECT R.Whatever, N.Whatever FROM dbo.RelNews R LEFT JOIN ( SELECT *, Selector = Row_Number() OVER (PARTITION BY N.Id ORDER BY N.newsDate DESC), FROM dbo.News N ) N ON R.newsId = N.Id AND N.Selector = 1 并非newsDate唯一。如果 唯一,那么它就更简单了。此版本适用于SQL 2000.这也可能是我提供的所有选项中性能最差的查询。请注意,newsID是每个UniqueColumn保证唯一值的列,可用于选择newsID的关系。

    newsDate

    如果SELECT R.Whatever, N.Whatever FROM dbo.RelNews R LEFT JOIN ( dbo.News N INNER JOIN ( SELECT N.Id, MaxUnique = Max(UniqueColumn) FROM dbo.News N INNER JOIN ( SELECT N.Id, MaxDate = Max(N.newsDate) FROM dbo.News N GROUP BY N.Id ) X ON N.Id = X.Id AND N.newsDate = X.MaxDate GROUP BY N.Id ) X ON N.Id = X.Id AND X.UniqueColumn = X.MaxUnique ) ON R.newsId = N.Id 对于newsDate来说真的是唯一的,那么这就是查询:

    news.Id
  4. 子查询 - 问题是您一次只能拉一列但在SQL 2000中工作,并且应该在适当的索引中运行良好。对于多列可能执行得很糟糕,因为它可能会为每个列执行单独的查询。

    SELECT
       R.Whatever,
       N.Whatever
    FROM
       dbo.RelNews R
       LEFT JOIN (
          dbo.News N
          INNER JOIN (
             SELECT N.Id, MaxDate = Max(N.newsDate)
             FROM dbo.News N
             GROUP BY N.Id
          ) X ON N.Id = X.Id
          AND N.newsDate = X.MaxDate
       ) ON R.newsId = N.Id
    
  5. ON子句中的子查询 - 如果您要拉多列,可能是SQL 2000的最佳查询。必须两次点击新闻表,但使用适当的索引它不应该那么糟糕。请注意,SELECT R.Whatever, NWhatever = ( SELECT TOP 1 N.Whatever FROM dbo.News N WHERE R.newsId = N.Id ORDER BY N.newsDate DESC ) FROM dbo.relNews R 是每个UniqueColumn保证唯一值的列,可用于选择newsID的关系。

    newsDate
  6. Logical-Last - SQL 2000的另一个可能性能良好的表现。这与fo_x86的逻辑查询相同,但表达方式略有不同。

    SELECT
       R.Whatever,
       N.Whatever
    FROM
       dbo.relNews R
       LEFT JOIN dbo.News N
          ON R.newsId = N.Id
             -- above condition not absolutely required logically,
             -- but definitely for indexes
          AND N.UniqueColumn = (
             SELECT TOP 1 N.UniqueColumn
             FROM dbo.News N
             WHERE R.newsId = N.Id
             ORDER BY N.newsDate DESC
          )
    
  7. 在您使用的数据库版本中,选项#1可能是最适合您的。

    最后一点:一如既往,需要测试。所有这些不同查询的性能取决于很多因素:数据模式(每个新闻项目的日期很少或几个),确切的索引,表格的宽度以及是否添加额外条件(例如如果您在外SELECT R.Whatever, N.Whatever FROM dbo.relNews R LEFT JOIN dbo.News N ON R.newsId = N.Id AND NOT EXISTS ( SELECT * FROM dbo.News X WHERE N.Id = X.Id AND ( N.newsDate < X.newsDate OR ( N.newsDate = X.newsDate AND N.UniqueColumn < X.UniqueColumn ) ) ) 表上的条件只返回几行,那么我建议的第一行将不会很好。如果您发现一个查询未提供令人满意的执行时间,请尝试使用其他查询。

答案 1 :(得分:1)

假设按顺序创建了一个newsid,而“news”是一个“item”,则可能是您想要的:

Select relNews.PortalId, max(relnews.newsid) as MostRecentNews
from relNews
group by relNews.PortalId

如果您只想要身份证,则不需要加入。

答案 2 :(得分:1)

您将按日期对门户网站ID进行排名

 select relNews.PortalId
 from news
 left join relNews on relNews.newsId = news.Id
 -- select rows for which there is no greater date than this row's date
 left outer join relNews2 on relNews.newsId = relNews2.newsId and relNews.date < relNews2.date
 where relNews2.PortalId is null
 group by relNews.PortalId, relNews.newsId

答案 3 :(得分:0)

具有最高性能的最简单和最短的方式是使用ROW_NUMBER()Over(Partition by ...)如下:

SELECT TOP 20 News.*, 
ROW_NUMBER() Over(Partition by relNews.PortalId Order By news.Id DESC ) R
FROM news LEFT JOIN relNews on relNews.newsId = news.Id
ORDER BY R

我在这里使用前20名,因为这些是门户计数,以显示每个门户网站的第一个新闻。

这是显示和表达“Partition By”

用法的最佳示例