我有一个要求,我需要检查eventsLogs表中的登录和注销对。一对包含一个登录和一个注销。在eventLogs表中有许多事件,我对eventType 11和12感兴趣,这意味着用户登录(事件11)和注销(事件12)。该计算机可能存在重复的事件。例如,登录,登录,注销,所以我在C#中所做的是首次登录,然后找到它的对。问题是我不知道在SQL中这样做,不幸的是我正在加载所有登录和注销到内存!我想要一种方法,以便我可以调用存储过程并获得相同的结果,我当前绑定到C#中的类。我将非常感谢你的帮助。如果我没有很好地解释我的问题,请询问解释。
// C#
var departmentEventsDic = new Dictionary<string, List<Sessions>>();
//get all departments and run the loop
foreach (var department in departments)
{
// create a list to push a pairs
var sessionPairsList = new List<Sessions>();
// for each computer in the department
foreach (var computer in department.Computers)
{
var tempLogs= eventLogs.Where(x => x.ComputerId == computer.ComputerId) .OrderBy(x => x.EventDateTime).ToList();
var tempUnlock = DateTime.MinValue;
//for each log for the computer
foreach (var log in tempLogs)
{
// if the event is login store it in temp varible
if (log.EventType == 11)
{
tempUnlock = log.EventDateTime;
}
// if its logout and its time is greater than tempUnlock , found a pair create class and add to list
if (log.EventType == 12 && (tempUnlock != DateTime.MinValue) && log.EventDateTime > tempUnlock)
{
var sessionPair = new Sessions
{
DepartmentId = department.DepartmentId,
DepartmentName = department.DepartmentName,
ComputerId = computer.ComputerId,
ComputerName = computer.Name,
LoginTime = tempUnlock,
LogOutTime = log.EventDateTime,
UserId = department.Users.FirstOrDefault(x => x.UserId == log.UserId)?.UserId,
UserName = department.Users.FirstOrDefault(x => x.UserId == log.UserId)?.Name,
Difference = (log.EventDateTime - temUnlock)
};
sessionPairsList.Add(sessionPair);
}
}
}
// add to dictionary with department name as key and list of sessionspairs as values
departmentEventsDic.Add(department.DepartmentName, sessionPairsList);
}
public class Results
{
public Guid DepartmentId { get; set; }
public string DepartmentName { get; set; }
public Guid ComputerId { get; set; }
public string ComputerName { get; set; }
public DateTime LoginTime { get; set; }
public DateTime LogOutTime { get; set; }
public Guid? UserId { get; set; }
public string UserName { get; set; }
public TimeSpan Difference { get; set; }
}
//Tables
CREATE TABLE [dbo].[Departments](
[DepartmentId] [uniqueidentifier] NOT NULL PrimaryKey,
[DepartmentName] [nvarchar](max) NULL
);
CREATE TABLE [dbo].[Computers](
[ComputerId] [uniqueidentifier] NOT NULL PrimaryKey,
[DepartmentId] [uniqueidentifier] NOT NULL,
[Name] [nvarchar](max) NULL );
ALTER TABLE [dbo].[Computers] WITH CHECK ADD CONSTRAINT [FK_Computers_Departments_DepartmentId] FOREIGN KEY([DepartmentId])
REFERENCES [dbo].[Departments] ([DepartmentId])
ON DELETE CASCADE
CREATE TABLE [dbo].[EventLogs](
[EventLogId] [uniqueidentifier] NOT NULL PrimaryKey,
[ComputerId] [uniqueidentifier] NOT NULL,
[EventDateTime] [datetime2](7) NOT NULL,
[EventType] [int] NOT NULL,
[UserId] [uniqueidentifier] NULL);
ALTER TABLE [dbo].[EventLogs] WITH CHECK ADD CONSTRAINT [FK_EventLogs_Computers_ComputerId] FOREIGN KEY([ComputerId])
REFERENCES [dbo].[Computers] ([ComputerId])
ON DELETE CASCADE
CREATE TABLE [dbo].[Users](
[UserId] [uniqueidentifier] NOT NULL,
[DepartmentId] [uniqueidentifier] NOT NULL,
[Name] [nvarchar](max) NULL);
ALTER TABLE [dbo].[Users] WITH CHECK ADD CONSTRAINT [FK_Users_Departments_DepartmentId] FOREIGN KEY([DepartmentId])
REFERENCES [dbo].[Departments] ([DepartmentId])
ON DELETE CASCADE
答案 0 :(得分:2)
通过使用面向您想要查询方式的关系的DBContext,可以极大地优化此类操作。在您的情况下,您有Departments / w计算机和一个松散的EventLogs DbSet。计算机实体通常不需要事件日志集合,但就本报告而言,1-many关系将是有用的。
如果您的主要上下文没有映射出这些关系,那么您可以考虑使用有限上下文来检索此数据,使用实体地图将计算机视为顶级实体,并且每台计算机都有一个部门和一个集合EventLogs。每个事件都有一个基于UserId的用户引用。
从那里,EF可以完成所有繁重的任务:
var sessionDataQuery = dbContext.Computers
.Select(x => new
{
x.Department.DepartmentId,
x.Department.DepartmentName,
x.ComputerId,
ComputerName = x.Name,
LoginEvents = x.EventLogs
.OrderBy(e => e.EventDateTime)
.Where(e => e.EventType = 11)
.Select(e => new
{
e.EventId,
e.User.UserId,
e.User.UserName,
e.EventDateTime
}.ToList(),
LogoutEvents = x.EventLogs
.OrderBy(e => e.EventDateTime)
.Where(e => e.EventType = 12)
.Select(e => new
{
e.EventId,
e.User.UserId,
e.User.UserName,
e.EventDateTime
}.ToList()
});
该查询应该为您提供可用于开始构建会话详细信息的IQueryable数据。因为它会在所有计算机上运行,所以您可能希望避免在其上使用.ToList(),而是使用Take&amp; amp; skip / w ToList获取一批它们(即每次50个)或迭代并在Foreach中一次选择一个。
这假设事件的用户引用将与计算机部门匹配。
从这里,您可以评估每台计算机的LoginEvents和LogoutEvents,并为这些计算机构建会话视图模型。您可能需要逻辑来根据时间将Logins与Logouts结合起来,并处理您可能在没有注销的情况下登录的情况,以及vise-verse。
假设有一对一登录注销你的人口比例如下:
foreach(var computer in sessionDataQuery)
{
for(int count = 0; count < computer.LoginEvents.Count; count++)
{
var loginEvent = computer.LoginEvents[count];
var logoutEvent = computer.LogoutEvents[count];
sessionPairs.Add( new Sessons
{
DepartmentId = computer.DepartmentId,
DepartmentName = computer.DepartmentName,
ComputerId = computer.ComputerId,
ComputerName = computer.ComputerName,
LoginTime = loginEvent.EventDateTime,
LogoutTime = logoutEvent.EventDateTime,
UserId = loginEvent.UserId,
UserName = loginEvent.UserName,
Difference = logoutEvent.EventDateTime - loginEvent.EventDateTime
}
}
}
对查询进行迭代,然后跨登录&amp;注销事件,假设它们已正确配对。您可能需要对如何考虑事件而不是依赖于for循环更有选择性。
sessionPairs将是List<Sessions>
,可以按DepartmentID分组,以获取每个部门的一组事件。
答案 1 :(得分:0)
您的标题声明您要将其转换为SQL代码。这在TSQL中都是可行的,但首先您需要知道将C#代码转换为TSQL的语法。在这里,我将提供您到达那里所需的所有元素。
如果需要从存储过程返回值,请使用带有OUTPUT参数的创建过程。存储过程中的所有参数看起来都像带有数据类型的@parameter。
在TSQL中没有foreach语句,但你可以用while循环替换它们并嵌套它们 - 但是你需要首先用Select Count(*)来获取要解析的行数,然后存储在变量中,然后每次通过while循环递增它。 (注意:在SQL Server存储过程中注意while循环,如果没有来自while循环的清除退出路径,它们可能会使服务器瘫痪。)
您在C#中的lamda语句需要转换为一系列IF / Else语句或TSQL中的CASE语句。
临时表可以通过在名称前用#命名表名来在存储过程中使用,并且可以将值抛出到内存中,然后以某种方式操作它们以获得所需的结果。
希望这能为您提供更多关于TSQL存储过程解决方案的想法。