组合公式(用于设计和质量保证)与连接(用于执行)的强大功能。





0/5 (0投票)
使用组合算术来提高 SQL 表设计和执行的质量
引言
本文介绍了组合公式算术在表和查询设计以及后续执行和分析的质量保证中的实际应用的好处。
背景
设想我们有四个存储库,用于存放企业资产。资产可以是计算机,通过 GUID(全局唯一标识符)标识,或者至少在企业或其他限制域内是唯一的。这些存储库的例子包括 Microsoft Active Directory、Microsoft System Center Configuration Manager、配置管理数据库 (CMDB)、防病毒管理资产存储库等。如果所有存储库都完全同步,那么在任何给定时间点,每个存储库都会包含每个资产的信息。实际上,通常并非如此,我们经常需要协调、分析和报告一个资产在所有存储库中的情况,以及在其他存储库中缺失(或记录)该资产的情况。具体来说,一个资产可能在一个存储库中被删除或添加,由于操作或流程延迟,其他存储库可能尚未“及时”反映此更改。因此,需要分析和报告存储库之间的“相同性”或“差异”。
实现
(本文提供的文件 RepositorySetCompare.sql 和 HeatMap.xlsx 包含所有提到的项目。)
具体到本文,即 4 个存储库,我们最终将创建一个“4-up”表,显示每个唯一的资产以及它所在的每个存储库。行将包含 null
或 AssetName
,列为存储库名称。一个资产可能只存在于 1 个、2 个、3 个、全部 4 个存储库中,或者这些组合。如果它不在任何一个存储库中,那么我们可能不知道该资产,这又是另一回事。此外,我在此断言,任何存储库只列出一次资产。如果一个资产在任何存储库中出现多次,那么这也是我在这篇文章中不考虑的另一件事。(尽管附带的 SQL 代码可以防止这种情况。)
我们可以轻易地辨别出有几种组合,并且我们可以急切地列出所有组合(通常是这样),或者更好地应用组合公式算术来确保我们可以列出适当的独特组合。不多,不少,不重复,用不同的措辞。精确地列出适当数量的独特组合。
由于资产可以存在于所有 4 个存储库中,或者只存在于其中一些存储库中,那么有多少种不同的情况?这是一个组合问题,而不是排列问题,因为我们不关心选择或排列的顺序。(当选择的顺序相关时,排列及其相关公式非常有用。)
我们肯定想知道一个资产是否在所有 4 个存储库中。那将是 1 种情况。用于计算这种情况的公式是
(number of repositories)factorial
divided by
(
number of repositories
less (number of repositories 'wherein we expect the asset to be recorded')factorial
times
(number of repositories 'wherein we expect the asset to be recorded')factorial
)
= 4! / (4 - 4)!4! = 24 / 24 = 1. Please recall that 0! = 1.
接下来,资产存在于 4 个存储库中的 3 个,但不在其中 1 个中的情况。这种情况会有多少种?用于计算这种情况的公式是:4! / (4 - 1)!1! = 24 / 6 = 4。现在我们知道至少有 5 种(1 + 4)不同的情况。
接下来,资产存在于 4 个存储库中的 2 个,但不在其他 2 个中的情况。这种情况会有多少种?用于计算这种情况的公式是:4! / (4 - 2)!2! = 24 / 4 = 6。现在我们至少有 11 种(1 + 4 + 6)不同的情况。
最后一种情况是资产仅记录在 4 个存储库中的 1 个中,而不在其他 3 个中。此数量情况的公式是:4! / (4 - 1)!1! = 24 / 6 = 4。现在我们有 15 种(1 + 4 + 6 + 4)不同的情况。
总之,一个资产可以表示在 4 个存储库(表)中的至少 1 个中,并且可以只存在于 1 个、2 个、3 个或全部 4 个存储库中。通过应用组合算术,我们可以确保知道可以设计的不同组合的确切数量。
那么,我们必须确保我们的表和查询设计和执行能够处理 15 种情况或组合。(1 + 4 + 6 + 4)
我将存储库标记为 A、B、C 和 D 来描述组合的细节。
组合列表
组合集 1
- 资产记录在 A、B、C 和 D(所有 4 个存储库)中。
组合集 2
- 资产记录在 A、B 和 C 中,但不在 D 中。
- 资产记录在 A、B 和 D 中,但不在 C 中。
- 资产记录在 A、C 和 D 中,但不在 B 中。
- 资产记录在 B、C 和 D 中,但不在 A 中。
组合集 3
- 资产记录在 A 和 B 中,但不在 C 和 D 中。
- 资产记录在 A 和 D 中,但不在 B 和 C 中。
- 资产记录在 C 和 D 中,但不在 A 和 B 中。
- 资产记录在 A 和 C 中,但不在 B 和 D 中。
- 资产记录在 B 和 C 中,但不在 A 和 D 中。
- 资产记录在 B 和 D 中,但不在 A 和 C 中。
组合集 4
- 资产记录在 A 中,但不在 B、C 或 D 中。
- 资产记录在 D 中,但不在 A、B 或 C 中。
- 资产记录在 C 中,但不在 A、B 或 D 中。
- 资产记录在 B 中,但不在 A、C 或 D 中。
我可以简单地列出所有不同的组合,预料我没有遗漏任何一个,没有重复列出任何一个(可能用不同的措辞),或者恰好全部列出。通过进行组合算术,我可以确定共有 15 种完全相同的组合。不多不少。这是检查我的设计需求和执行结果的绝佳方式。
创建和填充表的 SQL(最少数据)。
创建并填充存储库表,确保基于组合算术的最少测试数据。在实际应用中,这些存储库很可能包含成千上万行。稍后我们将生成更多数据。
if OBJECT_ID('tempdb..#AssetRepository_A', 'U') is not null
drop table #AssetRepository_A
if OBJECT_ID('tempdb..#AssetRepository_B', 'U') is not null
drop table #AssetRepository_B
if OBJECT_ID('tempdb..#AssetRepository_C', 'U') is not null
drop table #AssetRepository_C
if OBJECT_ID('tempdb..#AssetRepository_D', 'U') is not null
drop table #AssetRepository_D
if OBJECT_ID('tempdb..#4up', 'U') is not null
drop table #4up
create table #AssetRepository_A
(
AssetID varchar(30)
,constraint PK_AssetRepository_A primary key (AssetID)
)
create table #AssetRepository_B
(
AssetID varchar(30)
,constraint PK_AssetRepository_B primary key (AssetID)
)
create table #AssetRepository_C
(
AssetID varchar(30)
,constraint PK_AssetRepository_C primary key (AssetID)
)
create table #AssetRepository_D
(
AssetID varchar(30)
,constraint PK_AssetRepository_D primary key (AssetID)
)
create table #4up
(
[AssetRepository_A AssetID] varchar(30)
,[AssetRepository_B AssetID] varchar(30)
,[AssetRepository_C AssetID] varchar(30)
,[AssetRepository_D AssetID] varchar(30)
)
-- From here to next *** creates the minimum set of test data to cover all combinations.
-- 1. In All
insert into #AssetRepository_A(AssetID) values('1Yabcd')
insert into #AssetRepository_B(AssetID) values('1Yabcd')
insert into #AssetRepository_C(AssetID) values('1Yabcd')
insert into #AssetRepository_D(AssetID) values('1Yabcd')
-- 2. In A B C Not in D
insert into #AssetRepository_A(AssetID) values('2YabcNd')
insert into #AssetRepository_B(AssetID) values('2YabcNd')
insert into #AssetRepository_C(AssetID) values('2YabcNd')
-- 3. In A B D Not in C
insert into #AssetRepository_A(AssetID) values('3YabdNc')
insert into #AssetRepository_B(AssetID) values('3YabdNc')
insert into #AssetRepository_D(AssetID) values('3YabdNc')
-- 4. In A C D Not in B
insert into #AssetRepository_A(AssetID) values('4YacdNb')
insert into #AssetRepository_C(AssetID) values('4YacdNb')
insert into #AssetRepository_D(AssetID) values('4YacdNb')
-- 5. In B C D Not in A
insert into #AssetRepository_B(AssetID) values('5YbcdNa')
insert into #AssetRepository_C(AssetID) values('5YbcdNa')
insert into #AssetRepository_D(AssetID) values('5YbcdNa')
-- 6. In A B Not in C D
insert into #AssetRepository_A(AssetID) values('6YabNcd')
insert into #AssetRepository_B(AssetID) values('6YabNcd')
-- 7. In A D Not in B C
insert into #AssetRepository_A(AssetID) values('7YadNbc')
insert into #AssetRepository_D(AssetID) values('7YadNbc')
-- 8. In C D Not in A B
insert into #AssetRepository_C(AssetID) values('8YcdNab')
insert into #AssetRepository_D(AssetID) values('8YcdNab')
-- 9. In A C Not in B D
insert into #AssetRepository_A(AssetID) values('9YacNbd')
insert into #AssetRepository_C(AssetID) values('9Yacnbd')
-- 10. In B C Not in A D
insert into #AssetRepository_B(AssetID) values('10YbcNad')
insert into #AssetRepository_C(AssetID) values('10YbcNad')
-- 11. In B D Not in A C
insert into #AssetRepository_B(AssetID) values('11YbdNac')
insert into #AssetRepository_D(AssetID) values('11YbdNac')
-- 12. In A Not in B C D
insert into #AssetRepository_A(AssetID) values('12YaNbcd')
-- 13. In D Not in A B C
insert into #AssetRepository_D(AssetID) values('13YdNabc')
-- 14. In C Not in A B D
insert into #AssetRepository_C(AssetID) values('14YcNabd')
-- 15. In B Not in A C D
insert into #AssetRepository_B(AssetID) values('15YbNacd')
-- ***
填充 4up 表的 SQL
使用左 [外] 连接填充 4up 表。我们可以受益于组合算术和所有可能组合的详细列表,以确保任何连接的 **最左侧表是合适的表。非常重要:** 包含 null
的 where
子句。具体来说,使用 A 作为 **最左侧** 表,生成 15 种组合中的 8 种组合的正确行;B 用于 15 种中的 4 种;C 用于 15 种中的 2 种;D 用于 15 种中的 1 种,覆盖所有 15 种组合。
-- From here to next *** populates our 4up table.
-- Find conditions 1..4, 6..7, 9, and 12 (8 combinations)
insert into #4up
select A.AssetID
,B.AssetID
,C.AssetID
,D.AssetID
from
#AssetRepository_A A
left join #AssetRepository_B B on A.AssetID = B.AssetID
left join #AssetRepository_C C on A.AssetID = C.AssetID
left join #AssetRepository_D D on A.AssetID = D.AssetID
-- Find conditions 5, 10..11, and 15 (4 combinations)
insert into #4up
select A.AssetID
,B.AssetID
,C.AssetID
,D.AssetID
from
#AssetRepository_B B
left join #AssetRepository_A A on B.AssetID = A.AssetID
left join #AssetRepository_C C on B.AssetID = C.AssetID
left join #AssetRepository_D D on B.AssetID = D.AssetID
where A.AssetID is null
-- Find conditions 8 and 14 (2 combinations)
insert into #4up
select A.AssetID
,B.AssetID
,C.AssetID
,D.AssetID
from
#AssetRepository_C C
left join #AssetRepository_A A on C.AssetID = A.AssetID
left join #AssetRepository_B B on C.AssetID = B.AssetID
left join #AssetRepository_D D on C.AssetID = D.AssetID
where A.AssetID is null
and
B.AssetID is null
-- Find condition 13 (1 combination)
insert into #4up
select A.AssetID
,B.AssetID
,C.AssetID
,D.AssetID
from
#AssetRepository_D D
left join #AssetRepository_A A on D.AssetID = A.AssetID
left join #AssetRepository_B B on D.AssetID = B.AssetID
left join #AssetRepository_C C on D.AssetID = C.AssetID
where A.AssetID is null
and
B.AssetID is null
and
C.AssetID is null
-- ***
显示 4up 表的 SQL
我生成的测试数据专门用于清晰地显示所有可能的组合。此 SQL 将这些数据分为“组合集 1”..“组合集 4”。
-- Display 4up
select * from #4up
-- Display combination set 1 (assets that reside in each repository).
select 'Combination Set 1' 'Set Name',* from #4up
where [AssetRepository_A AssetID] is not null
and
[AssetRepository_B AssetID] is not null
and
[AssetRepository_C AssetID] is not null
and
[AssetRepository_D AssetID] is not null
-- Display combination set 2 (assets in 3 repositories but not in the remaining).
select 'Combination Set 2' 'Set Name', * from #4up
where ([AssetRepository_A AssetID] = [AssetRepository_B AssetID]
and
[AssetRepository_A AssetID] = [AssetRepository_C AssetID]
and
[AssetRepository_D AssetID] is null)
or
([AssetRepository_A AssetID] = [AssetRepository_B AssetID]
and
[AssetRepository_A AssetID] = [AssetRepository_D AssetID]
and
[AssetRepository_C AssetID] is null)
or
([AssetRepository_A AssetID] = [AssetRepository_C AssetID]
and
[AssetRepository_A AssetID] = [AssetRepository_D AssetID]
and
[AssetRepository_B AssetID] is null)
or
([AssetRepository_B AssetID] = [AssetRepository_C AssetID]
and
[AssetRepository_B AssetID] = [AssetRepository_D AssetID]
and
[AssetRepository_A AssetID] is null)
-- Display combination set 3 (assets in 2 repositories but not in the remaining).
select 'Combination Set 3' 'Set Name', * from #4up
where ([AssetRepository_A AssetID] = [AssetRepository_B AssetID]
and
[AssetRepository_C AssetID] is null
and
[AssetRepository_D AssetID] is null)
or
([AssetRepository_A AssetID] = [AssetRepository_D AssetID]
and
[AssetRepository_B AssetID] is null
and
[AssetRepository_C AssetID] is null)
or
([AssetRepository_C AssetID] = [AssetRepository_D AssetID]
and
[AssetRepository_A AssetID] is null
and
[AssetRepository_B AssetID] is null)
or
([AssetRepository_A AssetID] = [AssetRepository_C AssetID]
and
[AssetRepository_B AssetID] is null
and
[AssetRepository_D AssetID] is null)
or
([AssetRepository_B AssetID] = [AssetRepository_C AssetID]
and
[AssetRepository_A AssetID] is null
and
[AssetRepository_D AssetID] is null)
or
([AssetRepository_B AssetID] = [AssetRepository_D AssetID]
and
[AssetRepository_A AssetID] is null
and
[AssetRepository_C AssetID] is null)
-- Display combination set 4 (assets in 1 repository but not in the remaining).
select 'Combination Set 4' 'Set Name', * from #4up
where ([AssetRepository_A AssetID] is not null
and
[AssetRepository_B AssetID] is null
and
[AssetRepository_C AssetID] is null
and
[AssetRepository_D AssetID] is null)
or
([AssetRepository_D AssetID] is not null
and
[AssetRepository_A AssetID] is null
and
[AssetRepository_B AssetID] is null
and
[assetRepository_C AssetID] is null)
or
([AssetRepository_C AssetID] is not null
and
[AssetRepository_A AssetID] is null
and
[AssetRepository_B AssetID] is null
and
[assetRepository_D AssetID] is null)
or
([AssetRepository_B AssetID] is not null
and
[AssetRepository_A AssetID] is null
and
[AssetRepository_C AssetID] is null
and
[assetRepository_D AssetID] is null)
生成和显示 5,000 行随机资产的 SQL
请注意,随机生成的 AssetIDs 范围为 111 111 111 .. 999 999 999。我的多次测试表明,对于 5,000 行,通常会得到 5,000 行。对于更多行,由于明确的 SQL 代码 **不允许** 重复的 AssetIDs,您可能会收到较少的行。
在上述范围内(包括端点)生成值的构造是 cast((@omega - @alpha + 1) * RAND() + @alpha as integer)
,其中 @alpha = 111111111
,@omega = 999999999
。这可以以通用方式使用,其中 @alpha
指定范围的最小值,@omega
指定范围的最大值。
请注意,用于填充 4up 表的 SQL 与之前用于最少测试数据的 SQL 相同。
-- Generating and using test data.
truncate table #AssetRepository_A
truncate table #AssetRepository_B
truncate table #AssetRepository_C
truncate table #AssetRepository_D
truncate table #4up
declare @lcv int = 5000
declare @v int
declare @AssetSerialNumber int
declare @alpha int = 111111111
declare @omega int = 999999999
while (@lcv > 0)
begin
set @AssetSerialNumber = cast((@omega - @alpha + 1) * RAND() + @alpha as integer)
set @v = cast(15 * RAND() + 1 as integer)
if @v = 1
begin
if not exists (select * from #AssetRepository_A where AssetID = @AssetSerialNumber)
insert into #AssetRepository_A(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_B where AssetID = @AssetSerialNumber)
insert into #AssetRepository_B(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_C where AssetID = @AssetSerialNumber)
insert into #AssetRepository_C(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_D where AssetID = @AssetSerialNumber)
insert into #AssetRepository_D(AssetID) values(@AssetSerialNumber)
end
else if @v = 2
begin
if not exists (select * from #AssetRepository_A where AssetID = @AssetSerialNumber)
insert into #AssetRepository_A(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_B where AssetID = @AssetSerialNumber)
insert into #AssetRepository_B(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_C where AssetID = @AssetSerialNumber)
insert into #AssetRepository_C(AssetID) values(@AssetSerialNumber)
end
else if @v = 3
begin
if not exists (select * from #AssetRepository_A where AssetID = @AssetSerialNumber)
insert into #AssetRepository_A(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_B where AssetID = @AssetSerialNumber)
insert into #AssetRepository_B(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_D where AssetID = @AssetSerialNumber)
insert into #AssetRepository_D(AssetID) values(@AssetSerialNumber)
end
else if @v = 4
begin
if not exists (select * from #AssetRepository_A where AssetID = @AssetSerialNumber)
insert into #AssetRepository_A(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_C where AssetID = @AssetSerialNumber)
insert into #AssetRepository_C(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_D where AssetID = @AssetSerialNumber)
insert into #AssetRepository_D(AssetID) values(@AssetSerialNumber)
end
else if @v = 5
begin
if not exists (select * from #AssetRepository_B where AssetID = @AssetSerialNumber)
insert into #AssetRepository_B(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_C where AssetID = @AssetSerialNumber)
insert into #AssetRepository_C(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_D where AssetID = @AssetSerialNumber)
insert into #AssetRepository_D(AssetID) values(@AssetSerialNumber)
end
else if @v = 6
begin
if not exists (select * from #AssetRepository_A where AssetID = @AssetSerialNumber)
insert into #AssetRepository_A(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_B where AssetID = @AssetSerialNumber)
insert into #AssetRepository_B(AssetID) values(@AssetSerialNumber)
end
else if @v = 7
begin
if not exists (select * from #AssetRepository_A where AssetID = @AssetSerialNumber)
insert into #AssetRepository_A(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_D where AssetID = @AssetSerialNumber)
insert into #AssetRepository_D(AssetID) values(@AssetSerialNumber)
end
else if @v = 8
begin
if not exists (select * from #AssetRepository_C where AssetID = @AssetSerialNumber)
insert into #AssetRepository_C(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_D where AssetID = @AssetSerialNumber)
insert into #AssetRepository_D(AssetID) values(@AssetSerialNumber)
end
else if @v = 9
begin
if not exists (select * from #AssetRepository_A where AssetID = @AssetSerialNumber)
insert into #AssetRepository_A(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_C where AssetID = @AssetSerialNumber)
insert into #AssetRepository_C(AssetID) values(@AssetSerialNumber)
end
else if @v = 10
begin
if not exists (select * from #AssetRepository_B where AssetID = @AssetSerialNumber)
insert into #AssetRepository_B(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_C where AssetID = @AssetSerialNumber)
insert into #AssetRepository_C(AssetID) values(@AssetSerialNumber)
end
else if @v = 11
begin
if not exists (select * from #AssetRepository_B where AssetID = @AssetSerialNumber)
insert into #AssetRepository_B(AssetID) values(@AssetSerialNumber)
if not exists (select * from #AssetRepository_D where AssetID = @AssetSerialNumber)
insert into #AssetRepository_D(AssetID) values(@AssetSerialNumber)
end
else if @v = 12
begin
if not exists (select * from #AssetRepository_A where AssetID = @AssetSerialNumber)
insert into #AssetRepository_A(AssetID) values(@AssetSerialNumber)
end
else if @v = 13
begin
if not exists (select * from #AssetRepository_D where AssetID = @AssetSerialNumber)
insert into #AssetRepository_D(AssetID) values(@AssetSerialNumber)
end
else if @v = 14
begin
if not exists (select * from #AssetRepository_C where AssetID = @AssetSerialNumber)
insert into #AssetRepository_C(AssetID) values(@AssetSerialNumber)
end
else if @v = 15
begin
if not exists (select * from #AssetRepository_B where AssetID = @AssetSerialNumber)
insert into #AssetRepository_B(AssetID) values(@AssetSerialNumber)
end
set @lcv = @lcv - 1
end
-- From here to next *** populates our 4up table.
-- Find conditions 1..4, 6..7, 9, and 12 (8 combinations)
insert into #4up
select A.AssetID
,B.AssetID
,C.AssetID
,D.AssetID
from
#AssetRepository_A A
left join #AssetRepository_B B on A.AssetID = B.AssetID
left join #AssetRepository_C C on A.AssetID = C.AssetID
left join #AssetRepository_D D on A.AssetID = D.AssetID
-- Find conditions 5, 10..11, and 15 (4 combinations)
insert into #4up
select A.AssetID
,B.AssetID
,C.AssetID
,D.AssetID
from
#AssetRepository_B B
left join #AssetRepository_A A on B.AssetID = A.AssetID
left join #AssetRepository_C C on B.AssetID = C.AssetID
left join #AssetRepository_D D on B.AssetID = D.AssetID
where A.AssetID is null
-- Find conditions 8 and 14 (2 combinations)
insert into #4up
select A.AssetID
,B.AssetID
,C.AssetID
,D.AssetID
from
#AssetRepository_C C
left join #AssetRepository_A A on C.AssetID = A.AssetID
left join #AssetRepository_B B on C.AssetID = B.AssetID
left join #AssetRepository_D D on C.AssetID = D.AssetID
where A.AssetID is null
and
B.AssetID is null
-- Find condition 13 (1 combination)
insert into #4up
select A.AssetID
,B.AssetID
,C.AssetID
,D.AssetID
from
#AssetRepository_D D
left join #AssetRepository_A A on D.AssetID = A.AssetID
left join #AssetRepository_B B on D.AssetID = B.AssetID
left join #AssetRepository_C C on D.AssetID = C.AssetID
where A.AssetID is null
and
B.AssetID is null
and
C.AssetID is null
-- ***
-- Display 4up
select * from #4up
使用 Excel 的基本热力图
提供的文件 HeatMap.xlsx 显示了针对最少测试数据和 5,000 行随机生成的 AssetIDs 的基本“热力图”。诸如 Microsoft Excel、Microsoft PowerBI、Tableau、MicroStrategy、SAS、SPSS、R 等工具可以用于对这些数据进行更广泛的分析。
Excel 工作表对 4up 数据集应用了条件格式。请注意“已排序”选项卡。
关注点
本文推荐
- 应用组合公式来确定设计和执行需要考虑的正确组合数量
- 在 SQL 中使用左 [外] 连接以确保正确的组合
- 使用 SQL
RAND
函数生成随机测试数据
历史
- 2018 年 9 月 11 日:原创文章