2009-09-30 62 views
2

我在查询sql查询时遇到了麻烦,并且想知道是否有人可以提供帮助。我有这些表:'订单','订单产品','产品'和'用户'。我想了解每个“用户”订购的产品数量(包含在ProductID中的“OrderProducts”中)的数量。用户的用户ID包含在'订单'表中,并且'订单产品'的订单ID与'订单'中的订单ID相关。最后的结果应该是这样的:Linq to Sql - 通过父表关联进行双重分组

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

所以它由两个用户名和产品ID分组并通过用户ID通吃分组ProductIDs的数量的总和。我希望这是有道理的。任何帮助非常感谢,谢谢!

这里有一个简单的数据库故障:
产品
- 产品ID
- 产品名称

订单
- 订单ID
- 用户名

OrderProducts
- 订单ID
- 产品ID
- ProductQty

用户
- 用户名
- 用户名

回答

2

这里有一个查询,这将使你的UsersProducts列表,以及Count

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()} 

原样,这应该给你直接的结果(产品名称,用户名,数量表)。注释掉的部分将按产品进行分组,这取决于您如何呈现结果,可能是您想要的。

+0

嗯,这看起来很有希望,但我没有得到太多的机会。我将如何修改它以返回零? (即用户已订购此产品0次)。最后,我将指定哪些产品和用户包含在结果中,因此我必须将它们作为0。非常感谢! – Ryan 2009-10-02 19:54:28

+0

另外,让我知道如果有人有提示清理我的疑问! – 2009-10-02 21:04:38

+0

嘿谢谢你!我一直在另一个项目,并回到这个。我必须在VB.NET中完成这个项目,而我只有一个问题将你的linq语句转换为vb.net。我不知道i.Sum(v => v)会转换为什么。我已经得到了这个:从产品_ 来自你在用户_ 让我=(从op在OrderProducts _ 其中op.Order.User.UserID = u.UserID和op.Product.ProductID = p .ProductID _ Select op.ProductQty)_ Let Qty = _ If(i.Count()= 0,0,i.Sum())_ 使用{p.ProductName,u.Username,Qty} 非常感谢瑞恩! – Ryan 2009-10-20 18:25:44

0

如果在数据库中设置了(因此,在DBML)适当的关系,你可能要考虑像这种(未经测试):

<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查询。

+0

的问题,这似乎是,它会运行一个单独的查询为每个用户每一件产品。如果有2500个产品和500个用户,那么就有125万次访问数据库......你将等待一天的结果。 – Ryan 2009-10-02 19:52:37

0

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

这里的技巧是将结果投影到“属性”可以在运行时变化的类型。我做了类似的东西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();