SQL 视图显示即使在多对多关系中没有匹配的记录
SQL view to display records even no matching in many to many relation
我有 3 个表:
Role
------
[Id],
[Name]
Scope
------
[Id],
[Name]
RoleScope
------
[RoleId],
[ScopeId],
[View],
[Add],
[Edit],
[Delete],
这是多对多的关系。
我也有这样的观点
CREATE VIEW View_RolesWithScopes
AS
SELECT
[Role].Id AS RoleId,
[Role].[Name] AS RoleName,
[RoleScope].ScopeId,
[Scope].[Name] AS ScopeName,
[RoleScope].[View],
[RoleScope].[Add],
[RoleScope].[Edit],
[RoleScope].[Delete]
FROM
dbo.[Role] [Role]
LEFT JOIN dbo.RoleScope [RoleScope] ON [RoleScope].RoleId = [Role].Id
LEFT JOIN dbo.Scope [Scope] ON [Scope].Id = [RoleScope].ScopeId
即使 RoleScope 中没有关系,如何更新此视图以显示记录
示例:
Role
------
Id | Name
1 | Role1
2 | Role2
Scope
------
Id | Name
1 | Scope1
2 | Scope2
3 | Scope3
RoleScope
------
RoleId | ScopeId | View | Add | Edit | Delete
1 | 1 | 1 | 1 | 1 | 0
1 | 2 | 1 | 0 | 1 | 0
当前结果:
View_RolesWithScopes
------
RoleId | ScopeId | View | Add | Edit | Delete
1 | 1 | 1 | 1 | 1 | 0
1 | 2 | 1 | 0 | 1 | 0
2 | NULL | NULL | NULL| NULL | NULL
预期结果:
View_RolesWithScopes
------
RoleId | ScopeId | View | Add | Edit | Delete
1 | 1 | 1 | 1 | 1 | 0
1 | 2 | 1 | 0 | 1 | 0
1 | 3 | 0 | 0 | 0 | 0
2 | 1 | 0 | 0 | 0 | 0
2 | 2 | 0 | 0 | 0 | 0
2 | 3 | 0 | 0 | 0 | 0
要显示所有值,您可以在 ROLES table 和 SCOPES table 之间使用 CROSS JOIN,然后使用 LEFT JOIN 到 ROLESCOPES。要将 ROLESCOPES 中的适当列归零,您可以使用 ISNULL。
drop table if exists #roles;
go
create table #roles (
r_id int primary key not null,
[name] varchar(10) not null);
insert #roles(r_id, [name]) values
(1, 'Role1'),
(2, 'Role2');
drop table if exists #scopes;
go
create table #scopes (
s_id int primary key not null,
[name] varchar(10) not null);
insert #scopes(s_id, [name]) values
(1, 'Scope1'),
(2, 'Scope2'),
(3, 'Scope3');
drop table if exists #rolescopes;
go
create table #rolescopes (
r_id int not null,
s_id int not null,
[View] int not null,
[Add] int not null,
[Edit] int not null,
[Delete] int not null);
insert #rolescopes(r_id, s_id, [View], [Add], [Edit], [Delete]) values
(1, 1, 1, 1, 1, 0),
(1, 2, 1, 0, 1, 0);
select r.r_id, s.s_id,
isnull(rs.[View],0) [View],
isnull(rs.[Add],0) [Add],
isnull(rs.[Edit],0) [Edit],
isnull(rs.[Delete],0) [Delete]
from #roles r
cross join #scopes s
left join #rolescopes rs on r.r_id=rs.r_id
and s.s_id=rs.s_id;
r_id s_id View Add Edit Delete
1 1 1 1 1 0
1 2 1 0 1 0
1 3 0 0 0 0
2 1 0 0 0 0
2 2 0 0 0 0
2 3 0 0 0 0
使用Subquery
和cross join
获取roleid和scopeid的笛卡尔连接,Full Join
产生空值,IIF
区分空值如下
SELECT RS.roleid,
RS.scopeid,
IIF(RSS.[view] IS NULL, 0, RSS.[view]) [View],
IIF(RSS.[add] IS NULL, 0, RSS.[add]) [Add],
IIF(RSS.[edit] IS NULL, 0, RSS.[edit]) [Edit],
IIF(RSS.[delete] IS NULL, 0, RSS.[delete]) [Delete]
FROM (SELECT r.id AS RoleId,
s.id AS ScopeId
FROM roles r
CROSS JOIN scopes s) RS
FULL JOIN rolescopes RSS
ON RS.roleid = RSS.roleid
AND RS.scopeid = RSS.scopeid
有一种方法可以像这样填充缺失的行:
SELECT * FROM RoleScope
UNION SELECT
Role.Id, Scope.Id, 0, 0, 0, 0
FROM Role CROSS JOIN Scope
WHERE NOT EXISTS (
SELECT 1 FROM RoleScope
WHERE Role.id=RoleScope.RoleId AND
Scope.id=RoleScope.ScopeId);
我的查询不完全相同。
drop table if exists #roles;
go
create table #roles (
RoleId int primary key not null,
[name] varchar(10) not null);
insert #roles(RoleId, [name]) values
(1, 'Role1'),
(2, 'Role2');
drop table if exists #scopes;
go
create table #scopes (
ScopeId int primary key not null,
[name] varchar(10) not null);
insert #scopes(ScopeId, [name]) values
(1, 'Scope1'),
(2, 'Scope2'),
(3, 'Scope3');
drop table if exists #rolescopes;
go
create table #rolescopes (
roleScopeId int identity(1,1) primary key not null,
RoleId int not null,
ScopeId int not null,
[View] int not null,
[Add] int not null,
[Edit] int not null,
[Delete] int not null);
create nonclustered index ix_RID_SID on #rolescopes(RoleId,ScopeId) include([View],[Add],[Edit],[Delete] )
insert #rolescopes(RoleId, ScopeId, [View], [Add], [Edit], [Delete]) values
(1, 1, 1, 1, 1, 0),
(1, 2, 1, 0, 1, 0);
SELECT
[Role].RoleId AS RoleId,
[Role].[Name] AS RoleName,
[Scope].ScopeId,
[Scope].[Name] AS ScopeName
into #temp
FROM
#scopes [Scope]
join
#roles [Role] on 1=1
select t.* ,
isnull([RoleScope].[View],0) [View],
isnull([RoleScope].[Add],0) [Add],
isnull([RoleScope].[Edit],0) [Edit],
isnull([RoleScope].[Delete],0) [Delete]
from #temp t
LEFT JOIN #rolescopes [RoleScope] ON t.ScopeId = [RoleScope].ScopeId
drop table #temp
- 在 table rolescope 上创建了索引。
- 我将
cross join
的结果放在 role
和 Scope
之间,放入 temp
table 然后加入 temp
table和rolescope
,认为performance
如果有百万条记录就好了。
- 我忘记将数据类型从
INT
更改为 TinyINT
。如果有数百万条记录,这很重要。
我有 3 个表:
Role
------
[Id],
[Name]
Scope
------
[Id],
[Name]
RoleScope
------
[RoleId],
[ScopeId],
[View],
[Add],
[Edit],
[Delete],
这是多对多的关系。
我也有这样的观点
CREATE VIEW View_RolesWithScopes
AS
SELECT
[Role].Id AS RoleId,
[Role].[Name] AS RoleName,
[RoleScope].ScopeId,
[Scope].[Name] AS ScopeName,
[RoleScope].[View],
[RoleScope].[Add],
[RoleScope].[Edit],
[RoleScope].[Delete]
FROM
dbo.[Role] [Role]
LEFT JOIN dbo.RoleScope [RoleScope] ON [RoleScope].RoleId = [Role].Id
LEFT JOIN dbo.Scope [Scope] ON [Scope].Id = [RoleScope].ScopeId
即使 RoleScope 中没有关系,如何更新此视图以显示记录
示例:
Role
------
Id | Name
1 | Role1
2 | Role2
Scope
------
Id | Name
1 | Scope1
2 | Scope2
3 | Scope3
RoleScope
------
RoleId | ScopeId | View | Add | Edit | Delete
1 | 1 | 1 | 1 | 1 | 0
1 | 2 | 1 | 0 | 1 | 0
当前结果:
View_RolesWithScopes
------
RoleId | ScopeId | View | Add | Edit | Delete
1 | 1 | 1 | 1 | 1 | 0
1 | 2 | 1 | 0 | 1 | 0
2 | NULL | NULL | NULL| NULL | NULL
预期结果:
View_RolesWithScopes
------
RoleId | ScopeId | View | Add | Edit | Delete
1 | 1 | 1 | 1 | 1 | 0
1 | 2 | 1 | 0 | 1 | 0
1 | 3 | 0 | 0 | 0 | 0
2 | 1 | 0 | 0 | 0 | 0
2 | 2 | 0 | 0 | 0 | 0
2 | 3 | 0 | 0 | 0 | 0
要显示所有值,您可以在 ROLES table 和 SCOPES table 之间使用 CROSS JOIN,然后使用 LEFT JOIN 到 ROLESCOPES。要将 ROLESCOPES 中的适当列归零,您可以使用 ISNULL。
drop table if exists #roles;
go
create table #roles (
r_id int primary key not null,
[name] varchar(10) not null);
insert #roles(r_id, [name]) values
(1, 'Role1'),
(2, 'Role2');
drop table if exists #scopes;
go
create table #scopes (
s_id int primary key not null,
[name] varchar(10) not null);
insert #scopes(s_id, [name]) values
(1, 'Scope1'),
(2, 'Scope2'),
(3, 'Scope3');
drop table if exists #rolescopes;
go
create table #rolescopes (
r_id int not null,
s_id int not null,
[View] int not null,
[Add] int not null,
[Edit] int not null,
[Delete] int not null);
insert #rolescopes(r_id, s_id, [View], [Add], [Edit], [Delete]) values
(1, 1, 1, 1, 1, 0),
(1, 2, 1, 0, 1, 0);
select r.r_id, s.s_id,
isnull(rs.[View],0) [View],
isnull(rs.[Add],0) [Add],
isnull(rs.[Edit],0) [Edit],
isnull(rs.[Delete],0) [Delete]
from #roles r
cross join #scopes s
left join #rolescopes rs on r.r_id=rs.r_id
and s.s_id=rs.s_id;
r_id s_id View Add Edit Delete
1 1 1 1 1 0
1 2 1 0 1 0
1 3 0 0 0 0
2 1 0 0 0 0
2 2 0 0 0 0
2 3 0 0 0 0
使用Subquery
和cross join
获取roleid和scopeid的笛卡尔连接,Full Join
产生空值,IIF
区分空值如下
SELECT RS.roleid,
RS.scopeid,
IIF(RSS.[view] IS NULL, 0, RSS.[view]) [View],
IIF(RSS.[add] IS NULL, 0, RSS.[add]) [Add],
IIF(RSS.[edit] IS NULL, 0, RSS.[edit]) [Edit],
IIF(RSS.[delete] IS NULL, 0, RSS.[delete]) [Delete]
FROM (SELECT r.id AS RoleId,
s.id AS ScopeId
FROM roles r
CROSS JOIN scopes s) RS
FULL JOIN rolescopes RSS
ON RS.roleid = RSS.roleid
AND RS.scopeid = RSS.scopeid
有一种方法可以像这样填充缺失的行:
SELECT * FROM RoleScope
UNION SELECT
Role.Id, Scope.Id, 0, 0, 0, 0
FROM Role CROSS JOIN Scope
WHERE NOT EXISTS (
SELECT 1 FROM RoleScope
WHERE Role.id=RoleScope.RoleId AND
Scope.id=RoleScope.ScopeId);
我的查询不完全相同。
drop table if exists #roles;
go
create table #roles (
RoleId int primary key not null,
[name] varchar(10) not null);
insert #roles(RoleId, [name]) values
(1, 'Role1'),
(2, 'Role2');
drop table if exists #scopes;
go
create table #scopes (
ScopeId int primary key not null,
[name] varchar(10) not null);
insert #scopes(ScopeId, [name]) values
(1, 'Scope1'),
(2, 'Scope2'),
(3, 'Scope3');
drop table if exists #rolescopes;
go
create table #rolescopes (
roleScopeId int identity(1,1) primary key not null,
RoleId int not null,
ScopeId int not null,
[View] int not null,
[Add] int not null,
[Edit] int not null,
[Delete] int not null);
create nonclustered index ix_RID_SID on #rolescopes(RoleId,ScopeId) include([View],[Add],[Edit],[Delete] )
insert #rolescopes(RoleId, ScopeId, [View], [Add], [Edit], [Delete]) values
(1, 1, 1, 1, 1, 0),
(1, 2, 1, 0, 1, 0);
SELECT
[Role].RoleId AS RoleId,
[Role].[Name] AS RoleName,
[Scope].ScopeId,
[Scope].[Name] AS ScopeName
into #temp
FROM
#scopes [Scope]
join
#roles [Role] on 1=1
select t.* ,
isnull([RoleScope].[View],0) [View],
isnull([RoleScope].[Add],0) [Add],
isnull([RoleScope].[Edit],0) [Edit],
isnull([RoleScope].[Delete],0) [Delete]
from #temp t
LEFT JOIN #rolescopes [RoleScope] ON t.ScopeId = [RoleScope].ScopeId
drop table #temp
- 在 table rolescope 上创建了索引。
- 我将
cross join
的结果放在role
和Scope
之间,放入temp
table 然后加入temp
table和rolescope
,认为performance
如果有百万条记录就好了。 - 我忘记将数据类型从
INT
更改为TinyINT
。如果有数百万条记录,这很重要。