【问题标题】:Extract the sum of two things from datatable从数据表中提取两件事的总和
【发布时间】:2014-09-27 23:09:14
【问题描述】:

我在 SQL Server 数据库中有一个包含许多列的表,但重要的列是 LoggedStateInteractionType

我需要找出中断代理的数量和空闲代理的数量。

我尝试过的

SqlCommand GraphCmd = new SqlCommand("getAgentStatues", Graphsqlcon);
SqlParameter tdate = new SqlParameter();
GraphCmd.CommandType = CommandType.StoredProcedure; ;
SqlDataAdapter DAGraph = new SqlDataAdapter(GraphCmd);
DataSet DSGraph = new DataSet();
DSGraph.Clear();
DAGraph.Fill(DSGraph);
DataTable DTgraph = new DataTable();
DTgraph = DSGraph.Tables[0];

int numberOfBreakAgents = 0;
int numberOfIdelAgents = 0;
foreach (DataRow row in DTgraph.Rows)
{
     String LoggedState = row["LoggedState"].ToString().Trim().ToLower();
     String InteractionType = row["InteractionType"].ToString();
     if (LoggedState == "break")
     {
          numberOfBreakAgents++;
     }
     else if ((LoggedState == "activo") && (row["InteractionType"] == DBNull.Value))
     {
          numberOfIdelAgents++;
     }
}

它工作得很好,但我想问是否有办法(比如分组)来避免foreach 声明

【问题讨论】:

  • 这可能不关我的事,但你似乎在第 3 行多了一个不应该出现的分号。
  • @Matthijs 谢谢你的权利

标签: c# sql sql-server datatable


【解决方案1】:

您可以使用 Linq 中的 Group 函数:

var loggedStateGroups = dt.AsEnumerable().GroupBy(d => d["LoggedState"].ToString(), (group, row) => new
{
    LoggedState = group,
    AllCount = row.Count(),
    NullCount = row.Where(r => r["InteractionType"] == DBNull.Value).Count()
});

这将按 LoggedState 分组,每个匹配行的计数 (AllCount) 和 InteractionType 为 DBNull.Value 的行计数 (NullCount)。

然后我们可以通过以下方式选择我们所追求的计数:

int numberOfBreakAgents = loggedStateGroups.Where(y => y.LoggedState == "break").First().AllCount;
int numberOfIdelAgents = loggedStateGroups.Where(y => y.LoggedState == "activo").First().NullCount;

请注意,我只使用First 假设您总会有结果。如果您不总是有结果,您应该使用FirstOrDefault 并执行空检查。

您可以在使用Group 之前进行过滤,方法是根据您的数据添加以下Where

.Where(r => r["LoggedState"].ToString() == "break" || r["LoggedState"].ToString() == "activo")

我已使用以下设置对此进行了测试:

DataTable dt = new DataTable();
dt.Columns.Add("LoggedState");
dt.Columns.Add("InteractionType");

dt.Rows.Add("break", "inter1");
dt.Rows.Add("activo", DBNull.Value);
dt.Rows.Add("break", "inter1");
dt.Rows.Add("break", "inter2");
dt.Rows.Add("activo", "inter2");

我得到31 分别对应numberOfBreakAgentsnumberOfIdelAgents

使用 FirstOrDefault 进行编辑:

如果您想执行上面提到的空检查,可以将上面的两个 int 声明行替换为:

var breakAgents = loggedStateGroups.Where(y => y.LoggedState == "break").FirstOrDefault();
var idelAgents = loggedStateGroups.Where(y => y.LoggedState == "activo").FirstOrDefault();

int numberOfBreakAgents = breakAgents != null ? breakAgents.AllCount : 0;
int numberOfIdelAgents = idelAgents != null ? idelAgents.NullCount : 0;

这将采用 LoggedState 为“break”或null(如果没有)的第一个组。然后,如果组不为 null,则分配 numberOfBreakAgents AllCount 属性,如果为 null,则分配 0

numberOfIdelAgents 做了类似的事情,除了我们过滤“activo”组并使用NullCount 属性,因为我们对所有行不感兴趣,我们只对那些行感兴趣其中 InteractionType 是 DBNull.Value,我们在 NullCount 属性中捕获。

如果结果集包含零行且 LoggedState 为“activo”或零行且 LoggedState 为“break”,则需要进行空检查。在这种情况下,.First() 将返回 null 并从中访问 AllCountNullCount 将导致“序列不包含元素”异常。

使用以下 DataTable 定义将突出显示差异,因为它会导致使用 First()numberOfBreakAgents 出现异常,但使用 FirstOrDefault 时会正确返回 0

DataTable dt = new DataTable();
dt.Columns.Add("LoggedState");
dt.Columns.Add("InteractionType");

dt.Rows.Add("activo", "inter1");
dt.Rows.Add("activo", DBNull.Value);
dt.Rows.Add("activo", "inter1");
dt.Rows.Add("activo", "inter2");
dt.Rows.Add("activo", "inter2");

【讨论】:

  • 您能否更新您的答案并使用第一个或默认请求? +1
  • 当您使用 First of default 时,您删除了 activo 条件中对 null 的检查。这对我很重要
  • 我认为不需要第一个默认值,因为现在结果是空的,尽管你的代码给了我零,这很好
  • 我想我没有。对activo 的空检查位于填充组的NullCount 属性的连接中。在我的FirstOrDefault 代码中,我正在检查是否有任何与“activo”匹配的 LoggedStates 以及是否存在 NullCount (其中和不像我为“break”组所做的 AllCount
  • @MarcoDinatsoli 我再次更新了答案以添加更多说明。我认为您可能确实需要进行空值检查,但我对您的数据知之甚少,无法确定。
【解决方案2】:

你不能这样做

var breakAgents = from row in DTgraph.AsEnumerable()
where row["LoggedState"].ToString().Trim().ToLower() == "break"
select row;
var breakAgentsCount = breakAgents.Count();

var idleAgents = from row in DTgraph.AsEnumerable()
where row["LoggedState"].ToString().Trim().ToLower() == "activo"
&& row["InteractionType"] == DBNull.Value
select row;
var idleAgentsCount = idleAgents.Count();

【讨论】:

  • 在您的第一行中,Rows 中没有 Where
  • 更改为使 DTgraph 成为 IEumerable
  • DBNull.Value 之后你有)。为什么是)
  • 抱歉,弄错了 - 把它拿出来。
  • +1 的努力。我还没有找到写答案,因为我的队友删除了一些数据。
【解决方案3】:

使用 LINQ 提供的 Count 函数,以下解决方案应该可以工作:

// Cast the rows to a collection of DataRows.
IEnumerable<DataRow> collection = DTgraph.Rows.Cast<DataRow>();

// Get the number of Break Agents.
int numberOfBreakAgents = collection.Count(row => row["LoggedState"].ToString().Trim().ToLower() == "break");
// Get the number of Idel Agents.
int numberOfIdelAgents = collection.Count(row => row["LoggedState"].ToString().Trim().ToLower() == "activo" && row["InteractionType"] == DBNull.Value);

强制转换用于允许在 DataRow-collection 上使用 LINQ。


另一种选择是将 DataRow 集合转换为 DataRow 类型的列表。然后使用 ForEach(也是 LINQ)来确定代理类型:

List<DataRow> collection = DTgraph.Rows.Cast<DataRow>().ToList();

collection.ForEach(row =>
{
    if (row["LoggedState"].ToString().Trim().ToLower() == "break")
        numberOfBreakAgents++;
    else if (row["LoggedState"].ToString().Trim().ToLower() == "activo" && row["InteractionType"] == DBNull.Value)
        numberOfIdelAgents++;
});

上面的示例与您的示例非常相似,但写得更短一些,并且没有使用两个字符串(LoggedState 和 InteractionType)。

【讨论】:

  • 我尝试了你的第一种方法,但我一直得到零零,虽然我可以看到结果有值
  • 你能检查集合是否被填满吗?所以在演员表之后。
【解决方案4】:

你可以这样执行sql查询:

select 
   sum(case when LoggedState = "break" then 1 else 0 end) break_count,
   sum(case when LoggedState = "activo" and InteractionType is null then 1 else 0 end) active_count
from table_name

【讨论】:

  • 我没有表,我只有存储过程。
  • 这个查询做你需要的,你可以在存储过程中使用它
  • 我只能使用存储过程,我在组工作,我没有太多权限
猜你喜欢
  • 2013-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-18
  • 1970-01-01
相关资源
最近更新 更多