linqtosql:使用linqquery从每个组的顶行选择多个属性

hfwmuf9z  于 2021-07-26  发布在  Java
关注(0)|答案(2)|浏览(325)

我有两个表,apps和appgoogleplaymetadatas[agpm],有许多agpm到一个apps行。我想选择一组应用程序,对于每个应用程序,还返回每个应用程序的最近agpm行中的一些属性。我可以用这个代码:

Apps
.Select(a => new
{
    a.Name,
    DatePublished = a.AppGooglePlayMetadatas.OrderByDescending(agpm => agpm.DatePublished).Select(agpm => agpm.DatePublished).First(),
    CollectionDate = a.AppGooglePlayMetadatas.OrderByDescending(agpm => agpm.DatePublished).Select(agpm => agpm.CollectionDate).First(),
})

但这很难看,扩展性不好,可能会导致sql效率低下,排序会执行两次:

SELECT [t0].[Name], (
    SELECT TOP (1) [t1].[DatePublished]
    FROM [AppGooglePlayMetadata] AS [t1]
    WHERE [t1].[AppId] = [t0].[AppId]
    ORDER BY [t1].[DatePublished] DESC
    ) AS [DatePublished], (
    SELECT TOP (1) [t2].[CollectionDate]
    FROM [AppGooglePlayMetadata] AS [t2]
    WHERE [t2].[AppId] = [t0].[AppId]
    ORDER BY [t2].[DatePublished] DESC
    ) AS [CollectionDate]
FROM [Apps] AS [t0]

这样可以写得更清楚一点,但仍然会产生两个order by语句:

Apps
.Select(a => new
{
    App = a,
    Latest = a.AppGooglePlayMetadatas.OrderByDescending(agpm => agpm.DatePublished).First()
})
.Select(a => new {
    a.App.Name,
    a.Latest.CollectionDate,
    a.Latest.DatePublished,
})

我正在寻找一种方法来查询连接表中特定行的多个属性,这种方法可以生成高效的sql查询。如果我要用sql写这篇文章,我相信我可以这样做:

SELECT TOP 100 a.Name, agpm.CollectionDate, agpm.DatePublished
FROM Apps AS a
CROSS APPLY (
    SELECT TOP 1 DatePublished, CollectionDate
    FROM AppGooglePlayMetadata AS agpm
    WHERE a.AppId = agpm.AppId
    ORDER BY DatePublished DESC
) AS agpm

不确定在这种情况下是否有可能让linq生成如此高效的sql,但希望有天才能帮我,谢谢!

smtd7mpg

smtd7mpg1#

您可以尝试以下操作:

var result = 
    Apps.Join(
        AppGooglePlayMetadata
            .GroupBy(x => x.AppId)
            .Select(grp => grp
                .OrderByDescending(x => x.DatePublished)
                .First()),
        x => x.AppId,
        x => x.AppId,
        (app, meta) => new { app.AppId, meta.DatePublished, meta.CollectionDate });

这能让你得到一个高效的sql语句吗?

hts6caw3

hts6caw32#

在实体框架中,要获取“项目及其子项目”,如“学校及其学生”、“客户及其订单”、“应用程序及其AGPM”,通常最好使用 virtual ICollection<...> 这是你一对多关系的一方面。实体框架将在正确的groupjoin中对此进行转换。
如果你不想用 virtual ICollection<...> ,例如,因为您认为您可以更高效地进行,请考虑自己进行groupjoin:

var result = Apps.GroupJoin(         // GroupJoin Apps
AppGooglePlayMetadatas,              // with AGPMs

app => app.Id,                       // from every App take the primary key
apgm => apgm.AppId,                  // from every Agpm take the foreign key to App

(app, AgpmsOfThisApp) => new         // for every App with its zero or more AGPMs
{                                    // make one new
    AppName = app.Name,
    LatestAgpm = AgpmsOfThisApp.OrderByDescending(agpm => agpm.DatePublished)
        .Select(agpm => new
        {
            CollectionDate = agpm.CollectionDate,
            DatePublished = agpm.DatePublished,
        }}
        .FirstOrDefault(),
})

如果应用程序没有agpms,则根本没有latestagpm,属性katestagpm值将为空
imho的“应用程序及其最新的agpm”的概念是明确和直截了当的。用法如下:

textBoxAppName.Text = result.AppName,
testBoxAgpmLastPublished.Text =
   result.LatestAgpm.DatePublished;

如果您真的想混合应用程序和agpm的属性,您需要添加一个额外的select:

.Select(appWithItsLatestAgpm => new
{
    Name = appWithItsLatestAgpm.Name,
    CollectionDate = appWithItsLatestAgpm?.CollectionDate,
    DatePublished = appWithItsLatestAgpm?.DatePublished,
})

我不确定这是否能让结果对结果的用户更清楚。datepublished是应用程序的发布日期吗?
用法如下:

textBoxAppName.Text = result.AppName,
testBoxAgpmLastPublished.Text = result.DatePublished;

这能证明额外的选择吗?

相关问题