我正在尝试从每个门户网站中选择一个行(按日期排序的最后一行),但我遇到的问题是/ 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
我知道这是我在这里失踪的一个小技巧,但我不记得是什么......
让我们为这个例子制作虚拟表。表格为news
和relNews
(我在这里尽可能地缩短了表格)
表新闻
表格relNews
注意:
N
个注册表让我们说:
表新闻
日期=='2013-01-01 00:00:00'
NewsId == 2
日期=='2013-01-01 03:00:00'
NewsId == 3
表格relNews
PortalId == 1
RelNewsId == 2
PortalId == 2
RelNewsId == 3
PortalId == 1
RelNewsId == 4
这些数据应该带来:
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个结果。
答案 0 :(得分:4)
当一行news
行可能有多个时,您必须提供一些方法来指示哪个您想要的relNews
行。正如您所发现的那样,当您GROUP BY
一列可以为每个父项创建多个值时,您就会得到多行。有几种方法可以做到这一点。由于您使用的是SQL Server 2008,因此您有多种选择。
CROSS / OUTER APPLY - 如果您想在没有匹配的{{1}时排除OUTER APPLY
行,请将CROSS APPLY
更改为relNews
行。
News
<强> 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
汇总 - 这比看起来要复杂得多,但我认为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
子查询 - 问题是您一次只能拉一列但在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
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
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
)
在您使用的数据库版本中,选项#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”
用法的最佳示例