C# 使用查找表和连接表选择许多列/字段。内存不足异常
我有一个高度规范化的房地产数据库,用于存储清单数据。由于字段数量巨大,主数据分布在3个表(Listings、ListingDataCommons和ListingDataOthers)之间,然后有几个连接表用于功能、类型等 应用程序的用户使用GUI定义标准/字段,系统将(使用动态linq)生成C# 使用查找表和连接表选择许多列/字段。内存不足异常,c#,linq,entity-framework,out-of-memory,entity-framework-6,C#,Linq,Entity Framework,Out Of Memory,Entity Framework 6,我有一个高度规范化的房地产数据库,用于存储清单数据。由于字段数量巨大,主数据分布在3个表(Listings、ListingDataCommons和ListingDataOthers)之间,然后有几个连接表用于功能、类型等 应用程序的用户使用GUI定义标准/字段,系统将(使用动态linq)生成Where、Select和OrderBy语句。问题是当Select语句使用许多连接表和/或查找表时,我会遇到内存不足异常 下面是一个强类型(为了便于阅读)的查询示例,该查询将抛出错误。即使只返回MLS编号的单
Where
、Select
和OrderBy
语句。问题是当Select
语句使用许多连接表和/或查找表时,我会遇到内存不足异常
下面是一个强类型(为了便于阅读)的查询示例,该查询将抛出错误。即使只返回MLS编号的单个记录,它也会抛出OOM异常
var ListingResult = context.Listings
.Where(a => a.MLSNumber == "123456" || a.MLSNumber == "654321")
.Select(a => new
{
//--- select some data from the Listings table
a.MLSNumber,
a.DateLastUpdated,
a.DateLastImageUpdated,
a.Address,
a.ZipCode,
a.DaysOnMarket,
a.DisplayOnInternet,
a.DisplayReviews,
a.AuctionYN,
a.ListPrice,
a.LeasePrice,
a.SystemID,
a.DateLastPriceChange,
a.DateLastStatusChange,
a.DisplayAddressOnlineYN,
a.ListingID,
Status = a.Status.Name,
PropertyType = a.PropertyType.Name,
PropertyStyle = a.PropertyStyle.Name,
Country = a.Country.Name,
State = a.State.Name,
County = a.County.Name,
City = a.City.Name,
SaleType = a.SaleType.Name,
//--- select some data from the ListingDataCommons table
BathsFull = a.ListingDataCommon.BathsFull,
BathsHalf = a.ListingDataCommon.BathsHalf,
Beds = a.ListingDataCommon.Beds,
FireplaceYN = a.ListingDataCommon.FireplaceYN,
GarageSpaces = a.ListingDataCommon.GarageSpaces,
HOAYN = a.ListingDataCommon.HOAYN,
LotAcres = a.ListingDataCommon.LotAcres,
NewConstructionYN = a.ListingDataCommon.NewConstructionYN,
PetsAllowedYN = a.ListingDataCommon.PetsAllowedYN,
PetsMaxWeight = a.ListingDataCommon.PetsMaxWeight,
PetsMaxNumber = a.ListingDataCommon.PetsMaxNumber,
RemarksPublic = a.ListingDataCommon.RemarksPublic,
SqftHeated = a.ListingDataCommon.SqftHeated,
SubdivisionName = a.ListingDataCommon.SubdivisionName,
Taxes = a.ListingDataCommon.Taxes,
TaxYear = a.ListingDataCommon.TaxYear,
YearBuilt = a.ListingDataCommon.YearBuilt,
AirConditioning = a.ListingDataCommon.AirConditioning.Name,
ConstructionStatus = a.ListingDataCommon.ConstructionStatus.Name,
HousingForOlder = a.ListingDataCommon.HousingForOlder.Name,
//--- select some data from the ListingDataOthers table
CDDFee = a.ListingDataOther.CDDFee,
CDDFeeYN = a.ListingDataOther.CDDFeeYN,
CondoFee = a.ListingDataOther.CondoFee,
HOAFee = a.ListingDataOther.HOAFee,
HomesteadYN = a.ListingDataOther.HomesteadYN,
LotDimensions = a.ListingDataOther.LotDimensions,
LotSqft = a.ListingDataOther.LotSqft,
NumberBays = a.ListingDataOther.NumberBays,
NumberBuildings = a.ListingDataOther.NumberBuildings,
NumberFloors = a.ListingDataOther.NumberFloors,
NumberHotelRooms = a.ListingDataOther.NumberHotelRooms,
NumberOffices = a.ListingDataOther.NumberOffices,
NumberRestrooms = a.ListingDataOther.NumberRestrooms,
ProjectedCompletionDate = a.ListingDataOther.ProjectedCompletionDate,
SchoolElementary = a.ListingDataOther.SchoolElementary,
SchoolMiddle = a.ListingDataOther.SchoolMiddle,
SchoolHigh = a.ListingDataOther.SchoolHigh,
SizePorch = a.ListingDataOther.SizePorch,
SizeBedMaster = a.ListingDataOther.SizeBedMaster,
SizeBed2 = a.ListingDataOther.SizeBed2,
SizeBed3 = a.ListingDataOther.SizeBed3,
SizeBed4 = a.ListingDataOther.SizeBed4,
SizeBed5 = a.ListingDataOther.SizeBed5,
SizeBonusRoom = a.ListingDataOther.SizeBonusRoom,
SizeDinette = a.ListingDataOther.SizeDinette,
SizeDiningRoom = a.ListingDataOther.SizeDiningRoom,
SizeFamilyRoom = a.ListingDataOther.SizeFamilyRoom,
SizeGreatRoom = a.ListingDataOther.SizeGreatRoom,
SizeKitchen = a.ListingDataOther.SizeKitchen,
SizeLivingRoom = a.ListingDataOther.SizeLivingRoom,
SizeStudio = a.ListingDataOther.SizeStudio,
SizeStudyDen = a.ListingDataOther.SizeStudyDen,
SqftTotalBldg = a.ListingDataOther.SqftTotalBldg,
TotalUnits = a.ListingDataOther.TotalUnits,
VirtualTourLink = a.ListingDataOther.VirtualTourLink,
WaterAccessYN = a.ListingDataOther.WaterAccessYN,
WaterExtrasYN = a.ListingDataOther.WaterExtrasYN,
WaterFrontageYN = a.ListingDataOther.WaterFrontageYN,
WaterViewYN = a.ListingDataOther.WaterViewYN,
ZipCodePlusFour = a.ListingDataOther.ZipCodePlusFour,
Zoning = a.ListingDataOther.Zoning,
AWCRemarks = a.ListingDataOther.AWCRemarks,
ArchitecturalStyle = a.ListingDataOther.ArchitecturalStyle.Name,
CondoFeeSchedule = a.ListingDataOther.TimeFrame.Name,
FrontExposure = a.ListingDataOther.FrontExposure.Name,
Foundation = a.ListingDataOther.Foundation.Name,
Furnishing = a.ListingDataOther.Furnishing.Name,
HOASchedule = a.ListingDataOther.TimeFrame.Name,
MobileHomeWidth = a.ListingDataOther.MobileHomeWidth.Name,
//--- select some data from the junction tables (which in turn use lookup tables)
AdditionalRooms = a.ListingAdditionalRooms.Select(b => b.AdditionalRoom.Name),
AppliancesIncluded = a.ListingAppliances.Select(b => b.Appliance.Name),
CommunityFeatures = a.ListingCommunityFeatures.Select(b => b.CommunityFeature.Name),
ExteriorConstructions = a.ListingExteriorConstructions.Select(b => b.ExteriorConstruction.Name),
ExteriorFeatures = a.ListingExteriorFeatures.Select(b => b.ExteriorFeature.Name),
Financings = a.ListingFinancings.Select(b => b.Financing.Name),
FireplaceDescriptions = a.ListingFireplaceDescriptions.Select(b => b.FireplaceDescription.Name),
FloorCoverings = a.ListingFloorCoverings.Select(b => b.FloorCovering.Name),
FuelTypes = a.ListingFuelTypes.Select(b => b.FuelType.Name),
GarageFeatures = a.ListingGarageFeatures.Select(b => b.GarageFeature.Name),
GarageTypes = a.ListingGarageTypes.Select(b => b.GarageType.Name),
HeatTypes = a.ListingHeatTypes.Select(b => b.HeatType.Name),
InteriorFeatures = a.ListingInteriorFeatures.Select(b => b.InteriorFeature.Name),
KitchenFeatures = a.ListingKitchenFeatures.Select(b => b.KitchenFeature.Name),
LeaseIncludes = a.ListingLeaseIncludes.Select(b => b.LeaseInclude.Name),
MasterBathFeatures = a.ListingMasterBathFeatures.Select(b => b.MasterBathFeature.Name),
ParkingOptions = a.ListingParkingOptions.Select(b => b.ParkingOption.Name),
PoolFeatures = a.ListingPoolFeatures.Select(b => b.PoolFeature.Name),
PoolTypes = a.ListingPoolTypes.Select(b => b.PoolType.Name),
PropertyUses = a.ListingPropertyUses.Select(b => b.PropertyUse.Name),
RoofTypes = a.ListingRoofTypes.Select(b => b.RoofType.Name),
WaterAccessTypes = a.ListingWaterAccessTypes.Select(b => b.WaterType.Name),
WaterExtraTypes = a.ListingWaterExtraTypes.Select(b => b.WaterExtraType.Name),
WaterFrontageTypes = a.ListingWaterFrontageTypes.Select(b => b.WaterType.Name),
WaterViewTypes = a.ListingWaterViewTypes.Select(b => b.WaterType.Name),
})
.OrderBy(a => a.MLSNumber)
.ToList();
有没有更好的方法来组织这项工作?即使对查询调用.ToString()
,以查看生成的SQL,也会抛出OOM异常
更新:
作为对@Gert Arnold的回应,您能否进一步解释为什么数据库没有规范化?让我们以Status
字段为例,其中我有Status=a.Status.Name
。数据库中有一个名为status
的表,它有两列StatusID
和Name
,数据类似于1 | Active
,2 | Pending
,3 | sell
。Listings
表上的字段是StatusID
,它包含对Statuses
表上的StatusID
字段的引用。为了获得实际名称而不是状态ID,我必须执行a.status.name
。这与PropertyType、PropertyStyle、Country、State、County、City、SaleType的结构完全相同
然后,对于ListingDataCommons
和ListingDataOthers
表,它们与Listings
表的关系为1:1。创建它们是因为列表中有数百个字段,而不是将它们转储到一个巨大的表中,而是根据每个字段的查询频率将它们拆分。在这些表中,有一些列引用了查找表的ID,而不是上面的状态所解释的重复字符串值
还有一些连接表,如ListingAdditionalRooms
,它们具有1:Many关系,其中1个列表可以有许多额外的房间。ListingAdditionalRooms
表(和所有其他连接表)有两列(ListingID | AdditionalRoomID),引用Listings
表和AdditionalRooms
表中的相应记录
如果这是您见过的最糟糕的数据库设计之一,您建议如何改进它?我应该有一个Listings
表,其中包含近300列,通过记录重复存储字符串值吗?这似乎不是一个好的解决方案。请简要描述您将如何进行此操作(列表中有数百万条记录)。不是要图表,只是简单的解释
根据建议,将其分成更小的数据块并在两个请求中请求数据似乎可以解决问题(一个请求中的连接表数据,另一个请求中的所有其他数据)
关于这些从未在任何UI中显示的数据量,这是不正确的。尽管此查询只是测试限制,但这对于向用户显示列表的完整详细信息是绝对必要的
我期待着您对数据库结构的建议。实体框架在它必须生成的所有连接中都阻塞了。只有在a.
部分,您才有35个不同的导航属性!除此之外,您还可以在嵌套的Select
语句中访问大量导航属性
核心问题是,这是迄今为止我见过的最糟糕的数据库设计之一。这些表格只是一堆不相关的重复数据。没有任何正常化
您唯一的希望是对数据模型进行一次大的修改,基本上是一种新的设计。实体框架是一个ORM、对象关系映射器,因此应该从一些关系开始,使其成为一个有用的工具
如果设计不在您手中,您可以考虑两件事:
- 逐块获取内存中的数据,并从这些构建块构建客户端对象
- 必须能够使用较小的型号。我无法想象有一个UI视图可以同时显示所有这些数据。为每个视图构建专用视图模型
重新设计这不是几句话就能做到的。它需要大量的领域知识。有一件事可以是有一个features表,因此列表
可以有一个features
集合,而不是底部的众多导航属性。但要开始辨别域实体。比如,一个列表
什么都不是,它不是一个实体。你谈论的是建筑物(物业)、房间(或零件)、地段、价格、地址、家具。。。这些都应该是模型中的实体,而不是一个大bucket的属性(或字段)。阅读“无处不在的语言”。您建议的功能表是否为EAV模式?我对DDD了解不多,但就是看不到像清单的价格
或地址
这样的单个实体,而不是清单对象的属性,尤其是因为每个清单只有一个价格和一个地址。谢谢你的建议,我不知道。只要你能把特征描述成一种“has-a”的关联就行了。但是,就这一点而言,当“事物”发生变化时,EAV可能是唯一的选择