您的位置:首页 > 数据库

SQL Server二进制聚合运算性能的研究

2010-07-26 17:05 260 查看
程序开发中涉及权限管理,系统采用了整形数记录用户权限,权限级别设置为:

Level11
Level22
Level34
Level48
Level516
Level631
Level732
Level864
Level9127
Level10128
Level11255
Level1265535
 

 

在数据库中如果一个用户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 Cursor3.28s
Using While Loop1.34s
Bit wise Aggregate Sum12s
Bit wise Aggregate Sum20.4s
  
  
10,000 records 
Using Cursor0.33s
Using While Loop0.25s
Bit wise Aggregate Sum10.2s
Bit wise Aggregate Sum20.033s
  
  
1,000 records 
Using Cursor0.15s
Using While Loop0.12s
Bit wise Aggregate Sum10.02s
Bit wise Aggregate Sum20s
 

 

结论:使用二进制聚合方法中第二种方法,即定义包含权限数据的常量表,性能最佳。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息