我的LINQ效率很低

时间:2009-12-30 18:21:46

标签: linq-to-sql group-by

我正在使用LINQPad来学习LINQ,而且我遇到了绊脚石。

目标是获取网络ID,网络名称和每个站点的列表。

这是我原来的SQL:

SELECT n.iStationId AS NetworkID, n.sPrettyName AS NetworkName, COUNT(s.iStationID) AS StationCount
FROM T_StationInfo AS s, T_StationInfo as n
WHERE s.iNetworkId = n.iStationId
GROUP BY n.sPrettyName, n.iStationId
ORDER BY COUNT(s.iStationID) DESC

这是我的LINQ:

from s in T_stationInfo
from n in T_stationInfo
where s.INetworkID == n.IStationID
group s by s.INetworkID into stations
orderby stations.Count(x => x.INetworkID == stations.Key) descending
select new {
    NetworkId = stations.Key,
    NetworkName = T_stationInfo.Single(x => x.IStationID == stations.Key).SPrettyName,
    StationCount = stations.Count(x => x.INetworkID == stations.Key)
};

LINQ的执行时间要长5倍。我正在查看linq语句生成的SQL,它会将t_stationInfo表拉入7次。

我相信这是因为我滥用LINQ但我看不到在哪里或如何。

什么LINQ语句会创建等效的SQL,或者至少是SQL表现不佳的SQL?

一对夫妇注意到:

  1. 无法更改表/数据库的结构。
  2. 这个问题更多的是学习使用LINQ而不是获取ID,名称和计数列表。
  3. 我很感激! :)
  4. - 编辑 -

    只是为了澄清结构: 表中的每一行都是具有各种信息(名称,联系人等)并且可以拥有父级的实体。那些父母也在桌旁。在这种情况下,父母不能有父母。它们的父字段为NULL或0。

    因此,要获取工作站的父级名称(在表中称为网络),我将工作站信息表拉两次,并将父ID(网络ID)加入实体ID(工作站ID),以便单行我有电台的信息和父母的信息。因此,同桌的两个起源。

    这有意义吗?

    - EDIT2 -

    这是原始LINQ查询生成的sql:

    SELECT [t2].[iNetworkID] AS [NetworkId], (
        SELECT [t5].[sPrettyName]
        FROM [t_stationInfo] AS [t5]
        WHERE (CONVERT(Decimal(29,4),[t5].[iStationID])) = [t2].[iNetworkID]
        ) AS [NetworkName], (
        SELECT COUNT(*)
        FROM [t_stationInfo] AS [t6], [t_stationInfo] AS [t7]
        WHERE ([t6].[iNetworkID] = [t2].[iNetworkID]) AND ([t2].[iNetworkID] = [t6].[iNetworkID]) AND ([t6].[iNetworkID] = (CONVERT(Decimal(29,4),[t7].[iStationID])))
        ) AS [StationCount]
    FROM (
        SELECT [t0].[iNetworkID]
        FROM [t_stationInfo] AS [t0], [t_stationInfo] AS [t1]
        WHERE [t0].[iNetworkID] = (CONVERT(Decimal(29,4),[t1].[iStationID]))
        GROUP BY [t0].[iNetworkID]
        ) AS [t2]
    ORDER BY (
        SELECT COUNT(*)
        FROM [t_stationInfo] AS [t3], [t_stationInfo] AS [t4]
        WHERE ([t3].[iNetworkID] = [t2].[iNetworkID]) AND ([t2].[iNetworkID] = [t3].[iNetworkID]) AND ([t3].[iNetworkID] = (CONVERT(Decimal(29,4),[t4].[iStationID])))
        ) DESC
    

2 个答案:

答案 0 :(得分:1)

如果有的话,我不会对你的表现产生多大的影响。但是当我查看你的查询时,我看到一个函数被声明了两次:

stations.Count(s => s.INetworkID == stations.Key)

使用let子句是否可以提高性能?

from station in T_stationInfo
from network in T_stationInfo
where station.INetworkID == network.IStationID
group station by station.INetworkID into stations
let stationCount = stations.Count(x => x.INetworkID == stations.Key)
orderby stationCount descending
select new
{
    NetworkId = stations.Key,
    NetworkName = T_stationInfo.First(x => x.IStationID == stations.Key).sPrettyName,
    StationCount = stationCount
};

我觉得应该有更好的方法来分配NetworkName属性,但我不确定。

哦,抱歉重命名变量。我将s更改为station,将n更改为network,以帮助我更好地遵循它。

答案 1 :(得分:0)

感谢Alexander Taran的评论,我重新调查了组语法,最后了解了如何按多个字段进行分组。这让我想到了这个:

from s in T_stationInfo
from n in T_stationInfo
where s.INetworkID == n.IStationID
group s by new { s.INetworkID, n.SPrettyName } into stations
orderby stations.Count() descending
select new {
    NetworkId = stations.Key.INetworkID,
    NetworkName = stations.Key.SPrettyName,
    StationCount = stations.Count()
};

生成以下SQL:

SELECT [t2].[iNetworkID] AS [NetworkId], [t2].[sPrettyName] AS [NetworkName], [t2].[value2] AS [StationCount]
FROM (
    SELECT COUNT(*) AS [value], COUNT(*) AS [value2], [t0].[iNetworkID], [t1].[sPrettyName]
    FROM [t_stationInfo] AS [t0], [t_stationInfo] AS [t1]
    WHERE [t0].[iNetworkID] = (CONVERT(Decimal(29,4),[t1].[iStationID]))
    GROUP BY [t0].[iNetworkID], [t1].[sPrettyName]
    ) AS [t2]
ORDER BY [t2].[value] DESC

外部选择看起来像简单的重命名和重新排序。如果我将ORDER BY移动到内部选择并剥离外部选择,它将以与我手工制作的SQL相同的速度运行。

作为奖励,新查询似乎更容易理解。

- 编辑 -

根据Justin Rusbatch的回答,我将Stations.Count()拉出一个let,它确实有一个小的性能增加(大约10%)。它还在某种程度上清理了渲染的sql。

截至目前,这是我所取得的最好成绩:

from station in T_stationInfo 
from network in T_stationInfo 
where station.INetworkID == network.IStationID 
group station by new { station.INetworkID, network.SPrettyName } into stations 
let stationCount = stations.Count()
orderby stationCount descending 
select new 
{ 
    NetworkId = stations.Key.INetworkID, 
    NetworkName = stations.Key.SPrettyName, 
    StationCount = stationCount
}; 

这会创建以下SQL:

SELECT [t2].[iNetworkID] AS [NetworkId], [t2].[sPrettyName] AS [NetworkName], [t2].[value] AS [StationCount]
FROM (
    SELECT COUNT(*) AS [value], [t0].[iNetworkID], [t1].[sPrettyName]
    FROM [t_stationInfo] AS [t0], [t_stationInfo] AS [t1]
    WHERE [t0].[iNetworkID] = (CONVERT(Decimal(29,4),[t1].[iStationID]))
    GROUP BY [t0].[iNetworkID], [t1].[sPrettyName]
    ) AS [t2]
ORDER BY [t2].[value] DESC

与我开始的地方相比,这是一个巨大的改进。

相关问题