SQL Server二进制聚合运算性能的研究
2010-07-26 17:05
260 查看
程序开发中涉及权限管理,系统采用了整形数记录用户权限,权限级别设置为:
在数据库中如果一个用户a权限级别为1, 他的三个角色分别被授予权限2,2,4
则用户的权限应该返回7,即1|2|2|4.
由于T-SQL中没有二进制的聚合函数,我们的存储过程中起先使用游标循环记录逐条进行OR运算,然后输出结果。在进行压力测试时发现,当系统压力达到一定程度时SQL Server服务器CPU会达到100%,而且一直居高不下。
大家知道,臭名昭著的游标一直是SQL编程人员声讨的对象,于是我改用循环读取表记录逐条进行OR运算,然后输出结果。
在同样的压力下进行测试,结果仍然不理想。
根据网上帖子http://www.eggheadcafe.com/software/aspnet/33139293/bitwise-aggregate-functio.aspx,有如下两种方法可以对int进行二进制聚合运算。
If you have a numbers table:
SELECT SUM(mytable.mycolumn),
SUM(DISTINCT Bits.bitval)
FROM mytable
INNER JOIN (SELECT POWER(2, n-1) AS bitval
FROM dbo.Numbers
WHERE n <= 31) AS Bits
ON mytable.value & Bits.bitval = Bits.bitval;
Or if you don't have one (why not?) and don't want to create one (why
not??????), use this instead:
SELECT SUM(mycolumn),
MAX(mycolum & 1)
+ MAX(mycolum & 2)
+ MAX(mycolum & 4)
+ MAX(mycolum & 8)
+ MAX(mycolum & 16)
(....)
+ MAX(mycolum & 1073741824)
FROM mytable;
于是又分别按照这两种方法写了一遍权限查询。
测试脚本和结果如下:
DECLARE @Access table
(Id INT identity primary key,
Access INT)
DECLARE @insertCount INT
SET @insertCount=10000
WHILE @insertCount>0
BEGIN
INSERT INTO @Access(Access)
SELECT 0 Access
UNION ALL
SELECT 1 Access
UNION ALL
SELECT 2 Access
UNION ALL
SELECT 4 Access
UNION ALL
SELECT 8 Access
UNION ALL
SELECT 16 Access
UNION ALL
SELECT 31 Access
UNION ALL
SELECT 32 Access
UNION ALL
SELECT 64 Access
UNION ALL
SELECT 127 Access
UNION ALL
SELECT 128 Access
UNION ALL
SELECT 65535 Access
SET @insertCount = @insertCount - 1
END
DECLARE @SumAccess int
SET @SumAccess=0
--方法1,使用游标
SELECT getdate()
DECLARE @CurrentAccess INT
DECLARE access_cursor CURSOR FOR
SELECT Access
FROM @Access
OPEN access_cursor
FETCH NEXT FROM access_cursor INTO @CurrentAccess
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SumAccess=@SumAccess|@CurrentAccess
FETCH NEXT FROM access_cursor INTO @CurrentAccess
END
CLOSE access_cursor
DEALLOCATE access_cursor
SELECT @SumAccess AS UsingCursor
SELECT getdate()
--方法2,使用循环
DECLARE @MinId INT
DECLARE @MaxId INT
SET @SumAccess=0
SELECT @MinId=Min(Id)
FROM @Access
SELECT @MaxId=Max(Id)
FROM @Access
WHILE @MinId<=@MaxId
BEGIN
SELECT @SumAccess=@SumAccess|Access
FROM @Access
WHERE Id=@MinId
SET @MinId=@MinId+1
END
SELECT @SumAccess AS UsingWhileLoop
SELECT GETDATE()
--方法3,使用二进制聚合方法1
SELECT
(SUM(DISTINCT(Access & 0x000001))
+ SUM(DISTINCT(Access & 0x000002))
+ SUM(DISTINCT(Access & 0x000004))
+ SUM(DISTINCT(Access & 0x000008))
+ SUM(DISTINCT(Access & 0x000010))
+ SUM(DISTINCT(Access & 0x000020))
+ SUM(DISTINCT(Access & 0x000040))
+ SUM(DISTINCT(Access & 0x000080))
+ SUM(DISTINCT(Access & 0x000100))
+ SUM(DISTINCT(Access & 0x000200))
+ SUM(DISTINCT(Access & 0x000400))
+ SUM(DISTINCT(Access & 0x000800))
+ SUM(DISTINCT(Access & 0x001000))
+ SUM(DISTINCT(Access & 0x002000))
+ SUM(DISTINCT(Access & 0x004000))
+ SUM(DISTINCT(Access & 0x008000))
--可根据权限的最大值继续增加,我们的系统中0x008000为最大值
) as BitWiseAggre1
FROM @Access;
SELECT GETDATE();
--方法3,使用二进制聚合方法2
WITH AccessValue (Access)
AS
(
SELECT 1 --必须写成1否则如果写成0x000001 SQL Server会将数据类型默认为varbinary,这样,下面的处理中将无法进行二进制OR运算
UNION ALL
SELECT 0x000002
UNION
SELECT 0x000004
UNION ALL
SELECT 0x000008
UNION ALL
SELECT 0x000010
UNION ALL
SELECT 0x000020
UNION ALL
SELECT 0x000040
UNION ALL
SELECT 0x000080
UNION ALL
SELECT 0x000100
UNION ALL
SELECT 0x000200
UNION ALL
SELECT 0x000400
UNION ALL
SELECT 0x000800
UNION ALL
SELECT 0x001000
UNION ALL
SELECT 0x002000
UNION ALL
SELECT 0x004000
UNION ALL
SELECT 0x008000
--可根据权限的最大值继续增加,我们的系统中0x008000为最大值
)
SELECT
SUM(DISTINCT a.Access) AS BitWiseAggre2
FROM Access_test t
INNER JOIN AccessValue a
ON t.Access & a.Access = a.Access
SELECT GETDATE()
结论:使用二进制聚合方法中第二种方法,即定义包含权限数据的常量表,性能最佳。
Level1 | 1 |
Level2 | 2 |
Level3 | 4 |
Level4 | 8 |
Level5 | 16 |
Level6 | 31 |
Level7 | 32 |
Level8 | 64 |
Level9 | 127 |
Level10 | 128 |
Level11 | 255 |
Level12 | 65535 |
在数据库中如果一个用户a权限级别为1, 他的三个角色分别被授予权限2,2,4
则用户的权限应该返回7,即1|2|2|4.
由于T-SQL中没有二进制的聚合函数,我们的存储过程中起先使用游标循环记录逐条进行OR运算,然后输出结果。在进行压力测试时发现,当系统压力达到一定程度时SQL Server服务器CPU会达到100%,而且一直居高不下。
大家知道,臭名昭著的游标一直是SQL编程人员声讨的对象,于是我改用循环读取表记录逐条进行OR运算,然后输出结果。
在同样的压力下进行测试,结果仍然不理想。
根据网上帖子http://www.eggheadcafe.com/software/aspnet/33139293/bitwise-aggregate-functio.aspx,有如下两种方法可以对int进行二进制聚合运算。
If you have a numbers table:
SELECT SUM(mytable.mycolumn),
SUM(DISTINCT Bits.bitval)
FROM mytable
INNER JOIN (SELECT POWER(2, n-1) AS bitval
FROM dbo.Numbers
WHERE n <= 31) AS Bits
ON mytable.value & Bits.bitval = Bits.bitval;
Or if you don't have one (why not?) and don't want to create one (why
not??????), use this instead:
SELECT SUM(mycolumn),
MAX(mycolum & 1)
+ MAX(mycolum & 2)
+ MAX(mycolum & 4)
+ MAX(mycolum & 8)
+ MAX(mycolum & 16)
(....)
+ MAX(mycolum & 1073741824)
FROM mytable;
于是又分别按照这两种方法写了一遍权限查询。
测试脚本和结果如下:
DECLARE @Access table
(Id INT identity primary key,
Access INT)
DECLARE @insertCount INT
SET @insertCount=10000
WHILE @insertCount>0
BEGIN
INSERT INTO @Access(Access)
SELECT 0 Access
UNION ALL
SELECT 1 Access
UNION ALL
SELECT 2 Access
UNION ALL
SELECT 4 Access
UNION ALL
SELECT 8 Access
UNION ALL
SELECT 16 Access
UNION ALL
SELECT 31 Access
UNION ALL
SELECT 32 Access
UNION ALL
SELECT 64 Access
UNION ALL
SELECT 127 Access
UNION ALL
SELECT 128 Access
UNION ALL
SELECT 65535 Access
SET @insertCount = @insertCount - 1
END
DECLARE @SumAccess int
SET @SumAccess=0
--方法1,使用游标
SELECT getdate()
DECLARE @CurrentAccess INT
DECLARE access_cursor CURSOR FOR
SELECT Access
FROM @Access
OPEN access_cursor
FETCH NEXT FROM access_cursor INTO @CurrentAccess
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SumAccess=@SumAccess|@CurrentAccess
FETCH NEXT FROM access_cursor INTO @CurrentAccess
END
CLOSE access_cursor
DEALLOCATE access_cursor
SELECT @SumAccess AS UsingCursor
SELECT getdate()
--方法2,使用循环
DECLARE @MinId INT
DECLARE @MaxId INT
SET @SumAccess=0
SELECT @MinId=Min(Id)
FROM @Access
SELECT @MaxId=Max(Id)
FROM @Access
WHILE @MinId<=@MaxId
BEGIN
SELECT @SumAccess=@SumAccess|Access
FROM @Access
WHERE Id=@MinId
SET @MinId=@MinId+1
END
SELECT @SumAccess AS UsingWhileLoop
SELECT GETDATE()
--方法3,使用二进制聚合方法1
SELECT
(SUM(DISTINCT(Access & 0x000001))
+ SUM(DISTINCT(Access & 0x000002))
+ SUM(DISTINCT(Access & 0x000004))
+ SUM(DISTINCT(Access & 0x000008))
+ SUM(DISTINCT(Access & 0x000010))
+ SUM(DISTINCT(Access & 0x000020))
+ SUM(DISTINCT(Access & 0x000040))
+ SUM(DISTINCT(Access & 0x000080))
+ SUM(DISTINCT(Access & 0x000100))
+ SUM(DISTINCT(Access & 0x000200))
+ SUM(DISTINCT(Access & 0x000400))
+ SUM(DISTINCT(Access & 0x000800))
+ SUM(DISTINCT(Access & 0x001000))
+ SUM(DISTINCT(Access & 0x002000))
+ SUM(DISTINCT(Access & 0x004000))
+ SUM(DISTINCT(Access & 0x008000))
--可根据权限的最大值继续增加,我们的系统中0x008000为最大值
) as BitWiseAggre1
FROM @Access;
SELECT GETDATE();
--方法3,使用二进制聚合方法2
WITH AccessValue (Access)
AS
(
SELECT 1 --必须写成1否则如果写成0x000001 SQL Server会将数据类型默认为varbinary,这样,下面的处理中将无法进行二进制OR运算
UNION ALL
SELECT 0x000002
UNION
SELECT 0x000004
UNION ALL
SELECT 0x000008
UNION ALL
SELECT 0x000010
UNION ALL
SELECT 0x000020
UNION ALL
SELECT 0x000040
UNION ALL
SELECT 0x000080
UNION ALL
SELECT 0x000100
UNION ALL
SELECT 0x000200
UNION ALL
SELECT 0x000400
UNION ALL
SELECT 0x000800
UNION ALL
SELECT 0x001000
UNION ALL
SELECT 0x002000
UNION ALL
SELECT 0x004000
UNION ALL
SELECT 0x008000
--可根据权限的最大值继续增加,我们的系统中0x008000为最大值
)
SELECT
SUM(DISTINCT a.Access) AS BitWiseAggre2
FROM Access_test t
INNER JOIN AccessValue a
ON t.Access & a.Access = a.Access
SELECT GETDATE()
100,000 records | |
Using Cursor | 3.28s |
Using While Loop | 1.34s |
Bit wise Aggregate Sum1 | 2s |
Bit wise Aggregate Sum2 | 0.4s |
10,000 records | |
Using Cursor | 0.33s |
Using While Loop | 0.25s |
Bit wise Aggregate Sum1 | 0.2s |
Bit wise Aggregate Sum2 | 0.033s |
1,000 records | |
Using Cursor | 0.15s |
Using While Loop | 0.12s |
Bit wise Aggregate Sum1 | 0.02s |
Bit wise Aggregate Sum2 | 0s |
结论:使用二进制聚合方法中第二种方法,即定义包含权限数据的常量表,性能最佳。
相关文章推荐
- 关于count(*)得 非聚合索引 性能大于聚合索引性能研究
- [转翻译]SQL Server 2005 Analysis Services性能指南 Part 3 - 聚合最大化
- 二进制法求子集的原理(来自基于二进制的集合运算研究)
- SQL Server 2005 Analysis Services性能指南 Part 3 - 聚合最大化
- SQL Server 2005 Analysis Services性能指南 Part 3 - 聚合最大化
- SQL SERVER性能优化综述
- (转载)SQL Server 2008 R2 “性能计数器注册表配置单元一致性”检查失败 问题的解决方法
- 通过SQL Server的位运算功能巧妙解决多选查询
- 模拟二进制运算
- sql server性能分析--索引使用效率评估
- 深入理解计算机系统(2.5)---二进制整数的加、减法运算(重要)
- DBCC DBREINDEX重建索引提高SQL Server性能
- Sql Server 2005 row_number()分页性能测试
- 初涉SQL Server性能问题(1/4):服务器概况
- SQL Server 2008 R2 性能计数器详细列表(三)
- 初涉SQL Server性能问题(4/4):列出最耗资源的会话
- Swift库二进制接口(ABI)兼容性研究
- 安装sql server 2005 遇到问题性能监视器计数器要求解决方案
- 安装SQL Server 2008时可能遇到性能计数器不一致的情况
- SQL Server性能调优之缓存