繁体   English   中英

使用SQL Server EF6保存静态列表的最佳方法

[英]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.SqlQueryAsNoTracking()关闭就足够。
  • 不要打扰列表表上的外键-这会减慢插入速度。
  • 不要尝试同步清理不需要的旧列表搜索-将它们排入删除队列,并有一个后台进程来进行删除。
  • 结果,由于表搜索量很大, 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.

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