繁体   English   中英

在一个LINQ-to-Entities查询中限制来自多个表的结果。 结果T-SQL错误

[英]Limit results from multiple individual tables in a single LINQ-to-Entities query. Resultant T-SQL is wrong

我需要用一个查询查询多个表,并且需要分别限制每个表的结果。

一个例子 ...

我有一个ContentItem,零售商和产品表。

ContentItem具有一个Type(int)字段,该字段与诸如“零售商”和“产品”之类的内容类型的枚举相对应。 我正在为每个子查询使用此字段过滤ContentItem。

ContentItem具有一个Id(pkey)字段。

零售商和产品都有一个ID(pkey)字段。 ID也是ContentItem.Id的FK。

我可以使用LEFT JOIN查询从所有三个表中进行选择。 从那里,我可以限制返回的行总数,比如说总共6行。

我要做的是分别限制从零售商和产品返回的行数。 这样,我将总共有12行(最多):零售商有6行,产品有6行。

我已经可以使用SQL完成此操作,但是我很难让LINQ-to-Entities来“做正确的事”。


这是我的SQL

SELECT * From
    (
            (SELECT * FROM (SELECT * FROM [dbo].[ContentItem] WHERE Type = 0 ORDER BY ContentItem.mtime OFFSET 0 ROWS FETCH NEXT 6 ROWS ONLY) Retailers)
        UNION ALL
            (SELECT * FROM (SELECT * FROM [dbo].[ContentItem] WHERE Type = 1 ORDER BY ContentItem.mtime OFFSET 0 ROWS FETCH NEXT 6 ROWS ONLY) Brands)
        UNION ALL
            (SELECT * FROM (SELECT * FROM [dbo].[ContentItem] WHERE Type = 2 ORDER BY ContentItem.mtime OFFSET 0 ROWS FETCH NEXT 6 ROWS ONLY) Products)
        UNION ALL
            (SELECT * FROM (SELECT * FROM [dbo].[ContentItem] WHERE Type = 3 ORDER BY ContentItem.mtime OFFSET 0 ROWS FETCH NEXT 6 ROWS ONLY) Certifications)
        UNION ALL
            (SELECT * FROM (SELECT * FROM [dbo].[ContentItem] WHERE Type = 4 ORDER BY ContentItem.mtime OFFSET 0 ROWS FETCH NEXT 6 ROWS ONLY) Claims)
    ) as ContentItem

    LEFT JOIN [dbo].[Retailer] ON (Retailer.Id = ContentItem.Id)
    LEFT JOIN [dbo].[Brand] ON (Brand.Id = ContentItem.Id)
    LEFT JOIN [dbo].[Product] ON (Product.Id = ContentItem.Id)
    LEFT JOIN [dbo].[Certification] ON (Certification.Id = ContentItem.Id)
    LEFT JOIN [dbo].[Claim] ON (Claim.Id = ContentItem.Id);

这是我的LINQ查询的许多迭代之一(没有返回期望的结果)。

var queryRetailers = contentItemModel
    .Where(contentItem => contentItem.Type == ContentTypeEnum.Retailer)
    .OrderByDescending(o => o.mtime).Skip(skip).Take(take).Select(o => new { Id = o.Id });
var queryBrands = contentItemModel
    .Where(contentItem => contentItem.Type == ContentTypeEnum.Brand)
    .OrderByDescending(o => o.mtime).Skip(skip).Take(take).Select(o => new { Id = o.Id });
var queryProducts = contentItemModel
    .Where(contentItem => contentItem.Type == ContentTypeEnum.Product)
    .OrderByDescending(o => o.mtime).Skip(skip).Take(take).Select(o => new { Id = o.Id });
var queryCertifications = contentItemModel
    .Where(contentItem => contentItem.Type == ContentTypeEnum.Certification)
    .OrderByDescending(o => o.mtime).Skip(skip).Take(take).Select(o => new { Id = o.Id });
var queryClaims = contentItemModel
    .Where(contentItem => contentItem.Type == ContentTypeEnum.Claim)
    .OrderByDescending(o => o.mtime).Skip(skip).Take(take).Select(o => new { Id = o.Id });

var query = from contentItem in
    queryRetailers
    .Concat(queryBrands)
    .Concat(queryProducts)
    .Concat(queryCertifications)
    .Concat(queryClaims)

join item in context.Retailer on contentItem.Id equals item.Id into retailerGroup
    from retailer in retailerGroup.DefaultIfEmpty(null)

join item in context.Brand on contentItem.Id equals item.Id into brandGroup
    from brand in brandGroup.DefaultIfEmpty(null)

join item in context.Product on contentItem.Id equals item.Id into productGroup
    from product in productGroup.DefaultIfEmpty(null)

join item in context.Certification on contentItem.Id equals item.Id into certificationGroup
    from certification in certificationGroup.DefaultIfEmpty(null)

join item in context.Claim on contentItem.Id equals item.Id into claimGroup
    from claim in claimGroup.DefaultIfEmpty(null)

select new
{
    contentItem,
    retailer,
    brand,
    product,
    certification,
    claim
};

var results = query.ToList();

该查询返回的SQL本质上“嵌套”了我的UNION ALL语句,并且服务器从数据库返回了所有行。

SELECT 
    [Distinct4].[C1] AS [C1], 
    [Distinct4].[C2] AS [C2], 
    [Extent6].[Id] AS [Id], 
    [Extent6].[RowVersion] AS [RowVersion], 
    [Extent6].[ctime] AS [ctime], 
    [Extent6].[mtime] AS [mtime], 
    [Extent7].[Id] AS [Id1], 
    [Extent7].[Recommended] AS [Recommended], 
    [Extent7].[RowVersion] AS [RowVersion1], 
    [Extent7].[ctime] AS [ctime1], 
    [Extent7].[mtime] AS [mtime1], 
    [Extent8].[Id] AS [Id2], 
    [Extent8].[OverrideGrade] AS [OverrideGrade], 
    [Extent8].[PlantBased] AS [PlantBased], 
    [Extent8].[Recommended] AS [Recommended1], 
    [Extent8].[RowVersion] AS [RowVersion2], 
    [Extent8].[ctime] AS [ctime2], 
    [Extent8].[mtime] AS [mtime2], 
    [Extent8].[Brand_Id] AS [Brand_Id], 
    [Extent8].[Grade_Name] AS [Grade_Name], 
    [Extent8].[Grade_Value] AS [Grade_Value], 
    [Extent9].[Id] AS [Id3], 
    [Extent9].[RowVersion] AS [RowVersion3], 
    [Extent9].[ctime] AS [ctime3], 
    [Extent9].[mtime] AS [mtime3], 
    [Extent9].[Grade_Name] AS [Grade_Name1], 
    [Extent9].[Grade_Value] AS [Grade_Value1], 
    [Extent10].[Id] AS [Id4], 
    [Extent10].[RowVersion] AS [RowVersion4], 
    [Extent10].[ctime] AS [ctime4], 
    [Extent10].[mtime] AS [mtime4], 
    [Extent10].[Grade_Name] AS [Grade_Name2], 
    [Extent10].[Grade_Value] AS [Grade_Value2]
    FROM       (SELECT DISTINCT 
        [UnionAll4].[C1] AS [C1], 
        [UnionAll4].[C2] AS [C2]
        FROM  (SELECT 
            [Distinct3].[C1] AS [C1], 
            [Distinct3].[C2] AS [C2]
            FROM ( SELECT DISTINCT 
                [UnionAll3].[C1] AS [C1], 
                [UnionAll3].[C2] AS [C2]
                FROM  (SELECT 
                    [Distinct2].[C1] AS [C1], 
                    [Distinct2].[C2] AS [C2]
                    FROM ( SELECT DISTINCT 
                        [UnionAll2].[C1] AS [C1], 
                        [UnionAll2].[C2] AS [C2]
                        FROM  (SELECT 
                            [Distinct1].[C1] AS [C1], 
                            [Distinct1].[C2] AS [C2]
                            FROM ( SELECT DISTINCT 
                                [UnionAll1].[C1] AS [C1], 
                                [UnionAll1].[Id] AS [C2]
                                FROM  (SELECT TOP (1000) 
                                    [Project1].[C1] AS [C1], 
                                    [Project1].[Id] AS [Id]
                                    FROM ( SELECT [Project1].[Id] AS [Id], [Project1].[mtime] AS [mtime], [Project1].[C1] AS [C1], row_number() OVER (ORDER BY [Project1].[mtime] DESC) AS [row_number]
                                        FROM ( SELECT 
                                            [Extent1].[Id] AS [Id], 
                                            [Extent1].[mtime] AS [mtime], 
                                            1 AS [C1]
                                            FROM [dbo].[ContentItem] AS [Extent1]
                                            WHERE 0 =  CAST( [Extent1].[Type] AS int)
                                        )  AS [Project1]
                                    )  AS [Project1]
                                    WHERE [Project1].[row_number] > 0
                                    ORDER BY [Project1].[mtime] DESC
                                UNION ALL
                                    SELECT TOP (1000) 
                                    [Project3].[C1] AS [C1], 
                                    [Project3].[Id] AS [Id]
                                    FROM ( SELECT [Project3].[Id] AS [Id], [Project3].[mtime] AS [mtime], [Project3].[C1] AS [C1], row_number() OVER (ORDER BY [Project3].[mtime] DESC) AS [row_number]
                                        FROM ( SELECT 
                                            [Extent2].[Id] AS [Id], 
                                            [Extent2].[mtime] AS [mtime], 
                                            1 AS [C1]
                                            FROM [dbo].[ContentItem] AS [Extent2]
                                            WHERE 1 =  CAST( [Extent2].[Type] AS int)
                                        )  AS [Project3]
                                    )  AS [Project3]
                                    WHERE [Project3].[row_number] > 0
                                    ORDER BY [Project3].[mtime] DESC) AS [UnionAll1]
                            )  AS [Distinct1]
                        UNION ALL
                            SELECT TOP (1000) 
                            [Project7].[C1] AS [C1], 
                            [Project7].[Id] AS [Id]
                            FROM ( SELECT [Project7].[Id] AS [Id], [Project7].[mtime] AS [mtime], [Project7].[C1] AS [C1], row_number() OVER (ORDER BY [Project7].[mtime] DESC) AS [row_number]
                                FROM ( SELECT 
                                    [Extent3].[Id] AS [Id], 
                                    [Extent3].[mtime] AS [mtime], 
                                    1 AS [C1]
                                    FROM [dbo].[ContentItem] AS [Extent3]
                                    WHERE 2 =  CAST( [Extent3].[Type] AS int)
                                )  AS [Project7]
                            )  AS [Project7]
                            WHERE [Project7].[row_number] > 0
                            ORDER BY [Project7].[mtime] DESC) AS [UnionAll2]
                    )  AS [Distinct2]
                UNION ALL
                    SELECT TOP (1000) 
                    [Project11].[C1] AS [C1], 
                    [Project11].[Id] AS [Id]
                    FROM ( SELECT [Project11].[Id] AS [Id], [Project11].[mtime] AS [mtime], [Project11].[C1] AS [C1], row_number() OVER (ORDER BY [Project11].[mtime] DESC) AS [row_number]
                        FROM ( SELECT 
                            [Extent4].[Id] AS [Id], 
                            [Extent4].[mtime] AS [mtime], 
                            1 AS [C1]
                            FROM [dbo].[ContentItem] AS [Extent4]
                            WHERE 3 =  CAST( [Extent4].[Type] AS int)
                        )  AS [Project11]
                    )  AS [Project11]
                    WHERE [Project11].[row_number] > 0
                    ORDER BY [Project11].[mtime] DESC) AS [UnionAll3]
            )  AS [Distinct3]
        UNION ALL
            SELECT TOP (1000) 
            [Project15].[C1] AS [C1], 
            [Project15].[Id] AS [Id]
            FROM ( SELECT [Project15].[Id] AS [Id], [Project15].[mtime] AS [mtime], [Project15].[C1] AS [C1], row_number() OVER (ORDER BY [Project15].[mtime] DESC) AS [row_number]
                FROM ( SELECT 
                    [Extent5].[Id] AS [Id], 
                    [Extent5].[mtime] AS [mtime], 
                    1 AS [C1]
                    FROM [dbo].[ContentItem] AS [Extent5]
                    WHERE 4 =  CAST( [Extent5].[Type] AS int)
                )  AS [Project15]
            )  AS [Project15]
            WHERE [Project15].[row_number] > 0
            ORDER BY [Project15].[mtime] DESC) AS [UnionAll4] ) AS [Distinct4]
    LEFT OUTER JOIN [dbo].[Retailer] AS [Extent6] ON [Distinct4].[C2] = [Extent6].[Id]
    LEFT OUTER JOIN [dbo].[Brand] AS [Extent7] ON [Distinct4].[C2] = [Extent7].[Id]
    LEFT OUTER JOIN [dbo].[Product] AS [Extent8] ON [Distinct4].[C2] = [Extent8].[Id]
    LEFT OUTER JOIN [dbo].[Certification] AS [Extent9] ON [Distinct4].[C2] = [Extent9].[Id]
    LEFT OUTER JOIN [dbo].[Claim] AS [Extent10] ON [Distinct4].[C2] = [Extent10].[Id]

所以我的总体问题是:

1)是否可以执行更简单的SQL查询以获得相同的结果? 我知道T-SQL不支持子查询中每个表的偏移量,因此子查询包装。

2)如果没有,我在LINQ查询中做什么错了? LINQ甚至有可能吗?


我想从@radar此处添加所有不错的SQL并格式化。 至少看起来是避免子子查询的理想解决方案,并且仍然完成了偏移/获取。

SELECT *
    FROM (SELECT
        [ContentItem].*,
        row_number() OVER ( PARTITION BY Type ORDER BY ContentItem.mtime ) as rn
        FROM [dbo].[ContentItem]
        LEFT JOIN [dbo].[Retailer] ON (Retailer.Id = ContentItem.Id)
        LEFT JOIN [dbo].[Brand] ON (Brand.Id = ContentItem.Id)
        LEFT JOIN [dbo].[Product] ON (Product.Id = ContentItem.Id)
        LEFT JOIN [dbo].[Certification] ON (Certification.Id = ContentItem.Id)
        LEFT JOIN [dbo].[Claim] ON (Claim.Id = ContentItem.Id)
    ) as x
WHERE x.rn >= a AND x.rn <= b;

a是较低的阈值(偏移),b是较高的阈值(获取)。 唯一的问题是b现在等于fetch + a而不是fetch 第一组结果为WHERE x.rn >= 0 AND x.rn <= 6 ,第二组结果WHERE x.rn >= 6 AND x.rn <= 12 ,第三组结果为WHERE x.rn >= 12 AND x.rn <= 18 ,依此类推。

当您查找更简单的SQL时,可以使用row_number分析函数,该函数会更快

您需要尝试查看是否有许多left joins并且这些表中还需要存在适当的索引。

select * 
from (
select *, row_number() over ( partition by Type order by ContentItem.mtime ) as rn
from [dbo].[ContentItem]
LEFT JOIN [dbo].[Retailer] ON (Retailer.Id = ContentItem.Id)
LEFT JOIN [dbo].[Brand] ON (Brand.Id = ContentItem.Id)
LEFT JOIN [dbo].[Product] ON (Product.Id = ContentItem.Id)
LEFT JOIN [dbo].[Certification] ON (Certification.Id = ContentItem.Id)
LEFT JOIN [dbo].[Claim] ON (Claim.Id = ContentItem.Id);
)
where rn <= 6

好吧,看来我是个白痴。 TOP(1000)电话应该已经通知了我。 我以为我的take变量设置为6,但实际上设置为1000。结果证明我的巨型LINQ查询可以按预期工作,但是嵌套的UNION ALL语句使我失望。

不过,我将进一步调查@radar的答案。 很难说有更好的性能。

在此处输入图片说明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM