Я хочу получить пользователей из таблицы пользователей, где
- Пользователи не удаляются
- Пользователи, которые имеют идентификатор пользователя в предоставленном списке идентификаторов пользователей
- Пользователи, у которых есть идентификатор команды в предоставленном списке идентификаторов команды
- Пользователи, у которых есть идентификатор роли и идентификатор команды в предоставленном списке кортежей роли и команды.
Идентификаторы
Для этого я пишу хранимую процедуру с тремя табличными параметрами, как показано ниже:
create type RoleTeamTableType as table
(
RoleId bigint,
TeamId bigint
);
go
create type UserTableType as table
(
UserId bigint
);
go
create type TeamTableType as table
(
TeamId bigint
);
go
create proc spCurrentUsersInRolesAndTeams
@roleTeamTableType RoleTeamTableType readonly,
@userTableType UserTableType readonly,
@teamTableType TeamTableType readonly
as
set nocount on
select * from Users as U where U.Deleted<>1 and
(
exists
(select * from @roleTeamTableType as V
where (
(V.TeamId <> 0 and V.TeamId = U.TeamId and V.RoleId = U.RoleId) or
(V.TeamId = 0 and V.RoleId = U.RoleId)
)) /*if team id is 0 then only check for role id match else match both*/
or exists
(select * from @userTableType as W
where ( W.UserId = U.Id))
or exists
(select * from @teamTableType as X
where ( X.TeamId = U.TeamId))
)
return
go
/*Usage*/
declare @roleTeamTableType as RoleTeamTableType;
insert into @roleTeamTableType (RoleId, TeamId)
values (1,0), (1,1), (2,1)
declare @userTableType as UserTableType
insert into @userTableType (UserId) values (1),(2),(3)
declare @teamTableType as TeamTableType
insert into @teamTableType (TeamId) values (1),(2),(3)
exec spCurrentUsersInRolesAndTeams @roleTeamTableType, @userTableType, @teamTableType
Хотя приведенный выше код работает, у меня есть следующие вопросы:
- Учитывая, что я не являюсь экспертом в написании эффективных SQL-запросов, это правильный способ достижения того, чего я хочу, или есть какой-то лучший способ?
- Я написал версию этого запроса LINQ to Entities, используя EF, но я хочу оптимизировать ее, вызвав вместо этого эту хранимую процедуру. Итак, как мне создать табличные параметры в коде .Net?
- Рассмотрим сценарий, в котором предоставленные списки параметров с табличными значениями содержат тысячи значений, скажем, 100000, это, безусловно, приведет к снижению производительности. Каков предпочтительный и эффективный способ получения строк таблицы путем предоставления списка идентификаторов?
Обновление
Комментарии ниже предлагают добавить проблемный запрос, поэтому вот он
public IEnumerable<long> GetCurrentUserIds(IEnumerable<long> userIds, IEnumerable<Tuple<long, long>> roleTeamIds, IEnumerable<long> teamIds)
{
//setting timeout to avoid timeout exceptions
Context.Database.CommandTimeout = 180;
var users = Context.Users.AsNoTracking().Where(user => !user.Deleted);
//following loop is only for testing with userIds1
//working at i = 30,000
var userIds1 = new List<long>();
for (long i = 1; i < 30000; i++)
userIds1.Add(i);
//at i = 35,000
//Internal error: An expression services limit has been reached. Please look for potentially complex expressions in your query, and try to simplify them.
//at i = 40,000
//"The query processor ran out of internal resources and could not produce a query plan.
//This is a rare event and only expected for extremely complex queries or queries that reference a very large number of tables or partitions.
//Please simplify the query. If you believe you have received this message in error, contact Customer Support Services for more information."
var result = users.Where(user => (userIds1.Contains(user.Id) ||
(user.TeamId.HasValue && teamIds.Contains(user.TeamId.Value)))).Select(u=>u.Id);
foreach (var roleTeamId in roleTeamIds)
{
var filtered = users.Where(user => (roleTeamId.Item2 != 0 && roleTeamId.Item2 == user.TeamId && roleTeamId.Item1 == user.RoleId) ||
(roleTeamId.Item2 == 0 && roleTeamId.Item1 == user.RoleId)).Select(u => u.Id);
result = result.Concat(filtered);
}
return result.Distinct().ToList();
}
Запрос SQL Profiler, полученный со значениями параметра
userIds: 6,7
roleTeamIds: (4,1), (6,1)
teamIds: 0
exec sp_executesql N'SELECT
[Distinct1].[C1] AS [C1]
FROM ( SELECT DISTINCT
[UnionAll2].[Id] AS [C1]
FROM (SELECT
[Extent1].[Id] AS [Id]
FROM [dbo].[Users] AS [Extent1]
WHERE ([Extent1].[Deleted] <> 1) AND ([Extent1].[Id] IN (cast(6 as bigint), cast(11 as bigint)))
UNION ALL
SELECT
[Extent2].[Id] AS [Id]
FROM [dbo].[Users] AS [Extent2]
WHERE ([Extent2].[Deleted] <> 1) AND (((0 <> @p__linq__0) AND (@p__linq__1 = [Extent2].[TeamId]) AND (@p__linq__2 = [Extent2].[RoleId])) OR ((0 = @p__linq__3) AND (@p__linq__4 = [Extent2].[RoleId])))
UNION ALL
SELECT
[Extent3].[Id] AS [Id]
FROM [dbo].[Users] AS [Extent3]
WHERE ([Extent3].[Deleted] <> 1) AND (((0 <> @p__linq__5) AND (@p__linq__6 = [Extent3].[TeamId]) AND (@p__linq__7 = [Extent3].[RoleId])) OR ((0 = @p__linq__8) AND (@p__linq__9 = [Extent3].[RoleId])))) AS [UnionAll2]
) AS [Distinct1]',N'@p__linq__0 bigint,@p__linq__1 bigint,@p__linq__2 bigint,@p__linq__3 bigint,@p__linq__4 bigint,@p__linq__5 bigint,@p__linq__6 bigint,@p__linq__7 bigint,@p__linq__8 bigint,@p__linq__9 bigint',@p__linq__0=1,@p__linq__1=1,@p__linq__2=4,@p__linq__3=1,@p__linq__4=4,@p__linq__5=1,@p__linq__6=1,@p__linq__7=6,@p__linq__8=1,@p__linq__9=6