Can I perform a bitwise and on a set of numbers in Sql Server?
I have a security table containing a list of groups and users with a bitwise integer permission for each. For 开发者_运维问答each given user, I would like to perform a bitwise AND on all of their groups and of their personal permission record, if present.
Of course, I can easily do this in my code, but I'd rather do it in the database as there could be thousands of items I am querying the rights for.
I would prefer a set-based solution over a cursor.
Note than I do not have control over the schema.
A set-based solution is possible if all the values that you want to bitwise or are single-bit values: Performing a bitwise sum
Alternatively, you can use a less-elegant method for vaguely-set-based bitwise operations on sets of non-unique values:
DECLARE @BitSum INT
SET @BitSum = 0
SELECT @BitSum = @BitSum | BitValue
FROM (
SELECT 1 AS BitValue
UNION SELECT 7
UNION SELECT 16
) AS SampleValues
SELECT @BitSum
Edit: Hugo Kornelis answers the question pretty comprehensively in this other post: http://www.eggheadcafe.com/software/aspnet/33139293/bitwise-aggregate-function.aspx
SQL Server natively supports bitwise math. For example, &
is bitwise AND:
select 10 & 3
-->
2
Written out:
10 = 1010
3 = 0011
& = bitwise and
2 = 0010
You can use this in a query like any other operator:
select ss.SecurityBits & cs.CustomerBits
from SecuritySettings ss
join CustomerSettings cs
on ss.ID = cs.SecuritySettingsID
You can use a recursive CTE to perform a bitwise AND.
DECLARE @table TABLE (UserID int, AccessRights int)
INSERT @table (UserID, AccessRights) VALUES (2, 0x3)
INSERT @table (UserID, AccessRights) VALUES (2, 0x2)
INSERT @table (UserID, AccessRights) VALUES (2, 0x7)
;WITH Ranked AS
(
SELECT UserID, AccessRights
, DENSE_RANK() OVER (PARTITION BY UserID ORDER BY AccessRights) RankNum
, CASE WHEN DENSE_RANK()
OVER (PARTITION BY UserID ORDER BY AccessRights DESC) = 1
THEN 1
ELSE 0
END IsLastItem
FROM @table
),
RecursiveCTE AS
(
SELECT R.UserID, RankNum, IsLastItem, R.AccessRights
FROM Ranked R
WHERE R.RankNum = 1
UNION ALL
SELECT R.UserID, R.RankNum, R.IsLastItem
, R.AccessRights & RecursiveCTE.AccessRights
FROM RecursiveCTE
INNER JOIN Ranked R ON R.UserID = RecursiveCTE.UserID
WHERE R.RankNum = RecursiveCTE.RankNum + 1
)
SELECT UserID, AccessRights
FROM RecursiveCTE
WHERE IsLastItem = 1
You can create SQL Server Aggregate functions in .NET that you can then implement in SQL server inline. I think this requires a minimum of SQL server 2005 and Visual Studio 2010. I did one using Visual Studio 2013 Community Edition (free even for commercial use) for use with .NET 2 and SQL Server 2005.
See the MSDN article: https://msdn.microsoft.com/en-us/library/91e6taax(v=vs.90).aspx
First you'll need to enable the CLR feature in SQL server: https://msdn.microsoft.com/en-us/library/ms131048.aspx
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'clr enabled', 1;
GO
RECONFIGURE;
GO
- Create a SQL Server -> SQL Server Database Project
- Right-click on the new project and select Properties
- Configure the targeted SQL Server version under Project Settings
- Configure the targeted CLR language under SQL CLR (such as VB)
- Right-click on the new project and select Add -> New Item...
- When the dialog pops up, select SQL Server -> SQL CLR VB -> SQL CLR VB Aggregate
Now you can write your bitwise code in VB:
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
<Serializable()> _
<Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.Native)> _
Public Structure AggregateBitwiseOR
Private CurrentAggregate As SqlTypes.SqlInt32
Public Sub Init()
CurrentAggregate = 0
End Sub
Public Sub Accumulate(ByVal value As SqlTypes.SqlInt32)
'Perform Bitwise OR against aggregate memory
CurrentAggregate = CurrentAggregate OR value
End Sub
Public Sub Merge(ByVal value as AggregateBitwiseOR)
Accumulate(value.Terminate())
End Sub
Public Function Terminate() As SqlInt32
Return CurrentAggregate
End Function
End Structure
Now deploy it: https://msdn.microsoft.com/en-us/library/dahcx0ww(v=vs.90).aspx
- Build the project using the menu bar: Build -> Build ProjectName (if the build fails with error 04018 then download a new version of the data tools @ http://msdn.microsoft.com/en-US/data/hh297027 or by going to the menu bar: Tools -> Extensions And Updates, then under updates select update for Microsoft SQL Server Update For Database Tooling)
- Copy your compiled DLL to C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn and to C:\
Register the DLL:
CREATE ASSEMBLY [CLRTools] FROM ‘c:CLRTools.dll’ WITH PERMISSION_SET = SAFE
Create the aggregate in SQL:
CREATE AGGREGATE [dbo].[AggregateBitwiseOR](@value INT) RETURNS INT EXTERNAL NAME [CLRTools].[CLRTools.AggregateBitwiseOR];
If you get the error "Incorrect syntax near 'EXTERNAL'" then change the database compatibility level using following commands:
For SQL Server 2005: EXEC sp_dbcmptlevel 'DatabaseName', 90
For SQL Server 2008: EXEC sp_dbcmptlevel 'DatabaseName', 100
Test your code:
SELECT dbo.AggregateBitwiseOR(Foo) AS Foo FROM Bar
I found this article helpful: http://www.codeproject.com/Articles/37377/SQL-Server-CLR-Functions
精彩评论