[英]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.