1

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

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

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

当我在此代码中添加一个或多个数据列时,选择会带来所有信息,而不仅仅是每个门户的信息

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

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

更新

让我们为这个例子制作虚拟表。桌子是newsrelNews(我在这里尽可能短)

表新闻

  • 新闻ID
  • 标题
  • 描述
  • 日期

表相关新闻

  • RelNewsId
  • 新闻ID
  • PortalId

笔记:

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

让我们说:

表新闻

  • 新闻 ID == 1
  • 标题 == '测试'
  • 描述 == '测试'
  • 日期 == '2013-01-01 00:00:00'

  • 新闻 ID == 2

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

  • 新闻 ID == 3

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

表相关新闻

  • RelNewsId == 1
  • 新闻 ID == 1
  • PortalId == 1

  • RelNewsId == 2

  • 新闻 ID == 1
  • PortalId == 2

  • RelNewsId == 3

  • 新闻 ID == 2
  • PortalId == 1

  • RelNewsId == 4

  • 新闻 ID == 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

4 回答 4

4

当一行可能有多个时,您必须提供某种方式来指示您想要哪一。正如您所发现的,当您有一列可以为每个父级包含多个值时,您就会获得多行。有几种方法可以做到这一点。由于您使用的是 SQL Server 2008,因此您有多种选择。newsrelNewsGROUP BY

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

    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
    
  2. 行数()

    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
    
  3. 聚合- 这比它看起来需要的要复杂,但我认为这newsDate不是唯一的newsID。如果它唯一的,那么它就更简单了。此版本适用于 SQL 2000。这也可能是我提供的所有选项中性能最差的查询。请注意,这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那么这里就是那个查询:

    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
    
  4. 子查询- 问题是您一次只能拉一列,但可以在 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
    
  5. ON 子句中的子查询- 如果您要提取多个列,这可能是 SQL 2000 的最佳查询。必须两次访问 News 表,但使用适当的索引应该不会那么糟糕。请注意,这UniqueColumn是任何具有保证唯一值的列,newsID并且可用于在关系中进行选择newsDate

    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
          )
    
  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
          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 可能是最适合您的。

最后一点:与往常一样,需要进行测试。所有这些不同查询的性能将取决于很多因素:数据的模式(每个新闻项目的多个日期或少数几个)、确切的索引、表的宽度以及是否添加额外的条件(例如,如果您在外部relNews表上的条件仅返回几行,那么我建议的第一行将不会做得很好)。如果您发现一个查询没有提供令人满意的执行时间,请尝试另一个查询。

于 2012-12-28T19:46:16.650 回答
1

假设 newsid 是按顺序创建的,并且“news”是一个“item”,那么您可能需要以下内容:

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

如果您只想要 id,则不需要加入。

于 2012-12-28T19:42:46.900 回答
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
于 2012-12-28T19:43:51.017 回答
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 个,因为这些是显示每个 Portal 中第一个新闻的 Portal 计数。

这是展示和表达“Partition By”用法的最佳示例

于 2012-12-28T21:16:47.970 回答