将复杂的sql存储过程转换为linq

时间:2011-01-24 09:00:07

标签: sql-server vb.net linq linq-to-sql

我正在使用Linq到Sql并且有一个不会生成类的存储过程。存储过程将多个表中的数据绘制到平面文件结果集中。

返回的数据量必须尽可能小,需要限制到Sql Server的往返次数,并且必须限制服务器端处理的数量,因为这是针对ASP.NET MVC项目的

所以,我正在尝试将Linq写入Sql Query,但是我很难复制并限制返回的数据。

这是我想要转换的存储过程:

SELECT AdShops.shop_id as ID, Users.image_url_75x75, AdShops.Advertised,
    Shops.shop_name, Shops.title, Shops.num_favorers as hearts, Users.transaction_sold_count as sold,
    (select sum(L4.num_favorers) from Listings as L4 where L4.shop_id = L.shop_id) as listings_hearts, 
    (select sum(L4.views) from Listings as L4 where L4.shop_id = L.shop_id) as listings_views, 
    L.title AS listing_title, L.price as price, L.listing_id AS listing_id, L.tags, L.materials, L.currency_code, 
    L.url_170x135 as listing_image_url_170x135, L.url AS listing_url, l.views as listing_views, l.num_favorers as listing_hearts

FROM    AdShops INNER JOIN
    Shops ON AdShops.shop_id = Shops.shop_id INNER JOIN
    Users ON Shops.user_id = Users.user_id INNER JOIN
    Listings AS L ON Shops.shop_id = L.shop_id

WHERE   (Shops.is_vacation = 0 AND 
    L.listing_id IN
    (
        SELECT  listing_id
        FROM    (SELECT  l2.user_id , l2.listing_id, RowNumber = ROW_NUMBER() OVER (PARTITION BY l2.user_id ORDER BY NEWID())
                    FROM    Listings l2
                            INNER JOIN (
                            SELECT  user_id
                            FROM    Listings
                            GROUP BY
                                    user_id
                            HAVING  COUNT(*) >= 3
                            ) cnt ON cnt.user_id = l2.user_id  
                ) l2 
        WHERE   l2.RowNumber <= 3 and L2.user_id = L.user_id
    )
    )
ORDER BY Shops.shop_name

现在,到目前为止,我可以返回一个平面文件,但无法限制列表的数量。这就是我被困住的地方:

Dim query As IEnumerable = From x In db.AdShops
                       Join y In (From y1 In db.Shops
                                  Where y1.Shop_name Like _Search + "*" AndAlso y1.Is_vacation = False
                                  Order By y1.Shop_name
                                  Select y1) On y.Shop_id Equals x.shop_id
                       Join z In db.Users On x.user_id Equals z.User_id
                       Join l In db.Listings On l.Shop_id Equals y.Shop_id
                       Select New With {
                            .shop_id = y.Shop_id,
                            .user_id = z.user_id,
                            .listing_id = l.Listing_id
                            } Take 24 ' Fields ommitted for briefity...

我假设每个商店选择一个随机的3个列表,我需要使用lambda表达式,但不知道如何做到这一点。此外,需要在某个地方添加合并的总计,以便针对各个商店列出fieelds ......

有人有任何想法吗?

更新:

以下是我正在考虑的当前解决方案:

  1. 结果类包装器:
  2.     Public Class NewShops
            Public Property Shop_id As Integer
            Public Property listing_id As Integer
            Public Property tl_listing_hearts As Integer?
            Public Property tl_listing_views As Integer?
            Public Property listing_creation As Date
        End Class
    
    1. Linq +代码:
    2. Using db As New Ads.DB(Ads.DB.Conn)
          Dim query As IEnumerable(Of IGrouping(Of Integer, NewShops)) =
              (From x In db.AdShops
               Join y In (From y1 In db.Shops
                          Where (y1.Shop_name Like _Search + "*" AndAlso y1.Is_vacation = False)
                          Select y1 
                          Skip ((_Paging.CurrentPage - 1) * _Paging.ItemsPerPage)
                          Take (_Paging.ItemsPerPage))
                    On y.Shop_id Equals x.shop_id
               Join z In db.Users On x.user_id Equals z.User_id
               Join l In db.Listings On l.Shop_id Equals y.Shop_id
               Join lt In (From l2 In db.Listings _
                           Group By id = l2.Shop_id Into Hearts = Sum(l2.Num_favorers), Views = Sum(l2.Views), Count() _
                           Select New NewShops With {.tl_listing_views = Views,
                                                     .tl_listing_hearts = Hearts,
                                                     .Shop_id = id})
                    On lt.Shop_id Equals y.Shop_id
               Select New NewShops With {.Shop_id = y.Shop_id,
                                         .tl_listing_views = lt.tl_listing_views,
                                         .tl_listing_hearts = lt.tl_listing_hearts,
                                         .listing_creation = l.Creation,
                                         .listing_id = l.Listing_id
                                        }).GroupBy(Function(s) s.Shop_id).OrderByDescending(Function(s) s(0).tl_listing_views)
      
          Dim Shops as New Dictionary(Of String, List(Of NewShops))
      
          For Each item As IEnumerable(Of NewShops) In query
              Shops.Add(item(0).shop_name, (From i As NewShops In item
                                            Order By i.listing_creation Descending
                                            Select i Take 3).ToList)
          Next
      End Using
      

      有人有任何其他建议吗?

1 个答案:

答案 0 :(得分:1)

从SQL和代码的外​​观来看,我不会把它变成LINQ查询。它只会模糊逻辑,可能需要几天时间才能使其正确。

如果SQLMetal没有正确生成它,您是否考虑过使用ExecuteQueryDataContext方法返回您所追踪的项目列表?

假设您尝试转换的sproc称为sp_complicated,并且接受一个参数,类似下面的内容应该可以解决这个问题

Protected Class TheResults
    Public Property ID as Integer
    Public Property image_url_75x75 as String
    '... and so on and so forth for all the returned columns. Be careful with nulls
End Class

'then, when you want to use it

Using db As New Ads.DB(Ads.DB.Conn)
    dim results = db.ExecuteQuery(Of TheResults)("exec sp_complicated {0}", _Search)
End Using

在您惊慌失措之前,易受SQL注入影响。 L2SQL uses proper SQLParameters,只要你使用波浪形,而不是自己连接弦。