Linq to Sql - 具有父表关系的双组

时间:2009-09-30 14:01:27

标签: asp.net linq linq-to-sql

我在获取linq到SQL查询时遇到了麻烦,并且想知道是否有人可以提供帮助。我有这些表:'订单','订单产品','产品'和'用户'。我希望得到每个'用户'订购的产品数量(包含在ProductID产品'中的ProductID产品)数量的详细信息。用户的UserID包含在“订单”表中,“OrderProducts”orderID与“订单”中的orderID相关。最终结果应该是这样的:

            User1   User2    User3   
Product1     100     75       17
Product2     24      78       18
Product3     82      48       35

因此,它按UserID和ProductID分组,并使用该userID获取所有分组ProductID的数量之和。我希望这是有道理的。非常感谢任何帮助,谢谢!

这是一个简化的数据库细分:
产品
- 产品ID
- 产品名称

订单
- 订单ID
- 用户ID

订购产品
- 订单ID
- 产品ID
- ProductQty

用户
- 用户ID
- 用户名

3 个答案:

答案 0 :(得分:2)

这是一个查询,它会为您提供UsersProductsCount的列表:

from op in OrderProducts
group op by new {op.Order.User, op.Product} into g
select new
{
  g.Key.User.UserName,
  g.Key.Product.ProductName,
  Count=g.Sum(op => op.ProductQty)
}

它不会以您想要的确切格式提供结果,但它只会让您使用Linq。

另请注意,这不会提取任何额外数据 - (UsersProducts计数为0)。这可能是也可能不是你想要的。

[编辑] 这是另一个查看(实际上是两个)的查询:

from p in Products
from u in Users
let i = (from op in OrderProducts
         where op.Order.User == u && op.Product == p
         select op.ProductQty)
let quant = i.Count() == 0 ? 0 : i.Sum(v=>v)
select new {p.ProductName, u.UserName, Quantity = quant}
//group new {u.UserName, Quantity = quant} by p into g
//select new {g.Key.ProductName, Users = g.AsEnumerable()}

原样,这应该给你直接的结果(ProductName,UserName,Quantity表)。注释掉的部分将按产品分组,这取决于您呈现结果的方式,可能就是您想要的。

答案 1 :(得分:0)

如果您在数据库中设置了适当的关系(因此,在DBML中),您可能需要考虑这样的事情(UNTESTED):

<table>
<tr>
  <asp:Repeater ID="_users" runat="server">
    <ItemTemplate>
      <td><%# Eval("UserName") %></td>
    </ItemTemplate>
  </asp:Repeater>
</tr>
<asp:Repeater ID="_products" runat="server">
  <ItemTemplate>
    <tr>
      <td><%# Eval("Product.ProductName") %></td>
      <asp:Repeater ID="_users" runat="server" DataSource='<%# GetUsersProducts(Eval("ProductID")) %>'>
        <ItemTemplate>
          <td><%# Eval("count") %></td>
        </ItemTemplate>
      </asp:Repeater>
   </ItemTemplate>
</asp:Repeater>
</table>

代码隐藏:

protected void Page_Load(object sender, EventArgs e)
{
   _users.DataSource = context.Users.OrderBy(u => u.UserName);
   _users.DataBind();

   _products.DataSource = context.Products;
   _products.DataBind();
}
protected object GetUsersProducts(string ProductID)
{
  var prodord = from op in context.OrderProducts where op.ProductID == ProductID select op;
  return from u in context.Users.OrderBy(u2 => u2.UserName) select new {
    count = prodord.Where(op => op.Order.User.UserID == u.UserID).Sum(op => op.ProductQty)
  };
}

你显然想要改变你的标记和一些对象类型,但我希望这会让你前进。我尝试尽可能多地使用自动生成的类对象结构,以避免创建不可读的LINQ查询。

答案 2 :(得分:0)

您要求的结果形状称为 pivot 。它具有数据驱动的列,而不是在查询运行之前声明的列。

这里的技巧是将结果投影到一个类型,其中“属性”可以在运行时变化。我做了类似here

的事情

这是针对您的特定问题:

// grab relationally-shaped results from database
var queryResult = 
(
  from u in myDataContext.Users
  from o in u.Orders
  from op in o.OrderProducts
  select new
  {
    UserName = u.UserName,
    ProductName = op.ProductName,
    Quantity = op.Quantity
  }
).ToList()

// now use a LinqToObjects query to pivot
List<XElement> pivotedResults =
(
  from x in queryResult
  group by x.ProductName into g
  select new XElement(g.Key,
    from y in g
    group by y.UserName into subg
    select new XAttribute(subg.Key, subg.Sum(z => z.Quantity))
).ToList();