![](/img/trans.png)
[英]Is there an alternative to reference tables when using EF6 and SQL Server 2012?
[英]Best way to save a static list using SQL Server, EF6
我正在开始重写现有的应用程序。 当前的绩效瓶颈之一是将用户生成的雇员列表保存到静态列表中,以便他们以后可以回来查看相同的雇员列表。
以下是我要寻找的功能的简单示例。 生成的列表将比示例中的查询更为复杂。
场景: 一个用户搜索所有夜班的雇员,并希望保存此列表以便以后加载。 他们希望列表始终返回结果,因为这是他们第一次运行搜索。 即,如果将新员工添加到夜班中,则当他们拉起夜班时,他们不应出现在列表中。
我尝试过的
当前,存在一个非常差的解决方案,即将所有ID都存储在结果列表中作为字符串数组,然后使用这些ID重建查询。 这是非常低效的,并且会导致具有过多参数的大型列表出现问题。
我还尝试过从一个保存的ID数组构造一个表然后再加入,但是,这在大型列表(超过20,000名员工)上非常慢,并且经常导致超时。
我当前的想法是为每个列表创建一个新表,然后在该表和Employee表上调用JOIN。 但是,如果100个用户每个保存10个大型列表(超过20,000名员工),那么它很快就会成为存储和表管理的噩梦。
我认为这是解决方案中一个相当普遍的问题。 但是,我无法找到有关如何存储静态列表的任何示例或最佳做法(我可能正在搜索错误的内容)。 是否有人对如何最好地处理这种用例场景有任何一般性的概念?
更新:我想我以前尝试过以下设置,但由于几年前,它出于某种原因无法正常工作; 但是回头看似乎最有意义。 我认为我遇到的问题是NHibernate在Linq中存在子选择问题。 但是,这不再是限制。
我在想我有一个StaticSavedLists
表和一个索引表,该表将Person(在前面的示例中为Employee)链接到在多对多映射中将该List链接到Employee。 C#中的类如下所示:
public class StaticSavedList : BaseModel
{
public string Name { get; set; }
public IList<StaticSavedListPersonIdx> PersonsIdx { get; set; } //Has many persons
}
public class StaticSavedListPersonIdx : BaseModel
{
public StaticSavedList StaticSavedList { get; set; }
public Person Person { get; set; }
}
您可能需要有一个包含搜索“表头”详细信息的表,其中将包含搜索的ID,然后是包含每个搜索条目的第二个表。 然后,您只需要以某种方式获取ID(可能是通过用户ID和轮班日期),然后使用该ID来连接结果和employee表。
这是架构的外观
ShiftSearches
SearchID int (PK)
ShiftDate datetime
搜索结果
SearchID int (PK)
EmployeeID int (PK)
雇员
EmployeeID int (PK)
FirstName varchar
etc ...
可能的LINQ查询
DateTime shiftDate = new DateTime(2014,11,26);
int searchId = db.ShiftSearches.Single(s => s.ShiftDate == shiftDate).SearchID;
var results = from r in db.SearchResults where r.SearchID == searchId
join e in Employees on r.EmployeeID equals e.EmployeeID
select e;
这样,您只需要一个表,并且它非常薄,因此不应该占用太多空间-因为您仅存储所需的搜索和员工ID,所以无论如何您都无法真正减小数据量。
您发布的课程结构与此概念非常匹配。
实体框架可能并非在所有情况下都是适当的技术选择,并且一次处理2万行的批处理可能是其中一种情况。
但是,我相信您对数据模型的设计是一个不错的选择。 下面,仅在Sql上,可以显示可以在不到一秒钟的时间内将60k包含(ListId, EmployeeId)
对的行插入到合理聚集的ListSearchEmployee
表中,随后,可以将20k行的列表之一重新连接到从冷启动开始的1.8秒内完整的Employee
行。
性能瓶颈很可能是原始用户搜索-大概这可能是对Employees +相关表执行的几乎任意查询,这将很难为所有排列建立索引。
一些性能建议(用于列表保存+刷新):
SqlBulkCopy
将EmployeeId批量转储到List表中 SqlReader
到列表中的数据取回来的UI(虽然我想这将分页- ?如果是这样,一个DbSet.SqlQuery
与AsNoTracking()
关闭就足够。 ListSearchEmployee
表经常需要重新索引。 -- Sample data setup - not considered in the timing
CREATE TABLE Employee
(
EmployeeID INT identity(1,1),
Name NVARCHAR(100),
SomeOtherFieldToLessenTheDensityOfEmployee CHAR(500),
PRIMARY KEY(EmployeeID)
);
CREATE TABLE ListSearch
(
ListSearchID INT IDENTITY(1,1) PRIMARY KEY
-- Other fields you may want to identify the search, e.g. date, which user, which filters etc
)
CREATE TABLE ListSearchEmployee
(
ListSearchID INT,
EmployeeID INT, -- Don't bother Foreign Keying for performance
PRIMARY KEY CLUSTERED (ListSearchID, EmployeeID)
);
-- Insert 1M Employees
WITH cteData AS
(
SELECT top 1000000 sc1.name, ROW_NUMBER() OVER (ORDER BY sc1.object_id) AS rn
FROM sys.columns sc1 CROSS JOIN sys.columns sc2 CROSS JOIN sys.columns sc3
)
INSERT INTO Employee(Name)
SELECT name + CAST(rn AS VARCHAR)
FROM cteData;
-- Timing : 0.972 seconds on SQLExpress 2012 on an i3
-- Inserting 3 x 20 k lists of pseudo random employees (but not contigious on the EmployeeId Cluster)
WITH cteData AS
(
SELECT top 20000 1 as listid, ROW_NUMBER() OVER (ORDER BY sc1.object_id) * 50 AS empid
FROM sys.columns sc1 CROSS JOIN sys.columns sc2
UNION ALL
SELECT top 20000 2 as listid, ROW_NUMBER() OVER (ORDER BY sc1.object_id) * 30 AS empid
FROM sys.columns sc1 CROSS JOIN sys.columns sc2
UNION ALL
SELECT top 20000 3 as listid, ROW_NUMBER() OVER (ORDER BY sc1.object_id) * 41 AS empid
FROM sys.columns sc1 CROSS JOIN sys.columns sc2
)
INSERT INTO ListSearchEmployee(ListSearchID, EmployeeID)
SELECT listid, empid
FROM cteData;
DBCC DROPCLEANBUFFERS;
-- Timing : 1.751 seconds on SQLExpress 2012 on an i3
-- Joining 20k rows
SELECT *
FROM ListSearchEmployee el INNER JOIN Employee e on el.EmployeeID = e.EmployeeID
WHERE el.ListSearchID = 2
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.