簡體   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