Reputation: 607
I have legacy sql query that selects bit masks (among other data), something like:
1
2
1
How do I group this output like:
1 or 2 or 1
That should be 3
Upvotes: 1
Views: 334
Reputation: 17944
As of Oracle 20c, you can do bitwise aggregates using the following functions:
Upvotes: 0
Reputation: 17944
If I understand you correctly, you want an aggregate bit-or function. Oracle doesn't provide one, to my knowledge, so you have to roll your own using their ODCI (Oracle Data Cartridge Interface). Here's a working example:
CREATE OR REPLACE TYPE matt_bitor_aggregate_impl AS OBJECT
(
result NUMBER,
CONSTRUCTOR FUNCTION matt_bitor_aggregate_impl(SELF IN OUT NOCOPY matt_bitor_aggregate_impl ) RETURN SELF AS RESULT,
-- Called to initialize a new aggregation context
-- For analytic functions, the aggregation context of the *previous* window is passed in, so we only need to adjust as needed instead
-- of creating the new aggregation context from scratch
STATIC FUNCTION ODCIAggregateInitialize (sctx IN OUT matt_bitor_aggregate_impl) RETURN NUMBER,
-- Called when a new data point is added to an aggregation context
MEMBER FUNCTION ODCIAggregateIterate (self IN OUT matt_bitor_aggregate_impl, value IN NUMBER ) RETURN NUMBER,
-- Called to return the computed aggragate from an aggregation context
MEMBER FUNCTION ODCIAggregateTerminate (self IN matt_bitor_aggregate_impl, returnValue OUT NUMBER, flags IN NUMBER) RETURN NUMBER,
-- Called to merge to two aggregation contexts into one (e.g., merging results of parallel slaves)
MEMBER FUNCTION ODCIAggregateMerge (self IN OUT matt_bitor_aggregate_impl, ctx2 IN matt_bitor_aggregate_impl) RETURN NUMBER --,
);
/
CREATE OR REPLACE TYPE BODY matt_bitor_aggregate_impl IS
CONSTRUCTOR FUNCTION matt_bitor_aggregate_impl(SELF IN OUT NOCOPY matt_bitor_aggregate_impl ) RETURN SELF AS RESULT IS
BEGIN
SELF.result := null;
RETURN;
END;
STATIC FUNCTION ODCIAggregateInitialize (sctx IN OUT matt_bitor_aggregate_impl) RETURN NUMBER IS
BEGIN
sctx := matt_bitor_aggregate_impl ();
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate (self IN OUT matt_bitor_aggregate_impl, value IN NUMBER ) RETURN NUMBER IS
BEGIN
IF self.result IS NULL THEN
self.result := value;
ELSE
-- Logic for bitwise OR
-- see also: http://www.oracledba.co.uk/tips/bitwise_ops.htm
self.result := self.result - BITAND(self.result, value) + value;
END IF;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate (self IN matt_bitor_aggregate_impl, returnValue OUT NUMBER, flags IN NUMBER) RETURN NUMBER IS
BEGIN
returnValue := result;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge (self IN OUT matt_bitor_aggregate_impl, ctx2 IN matt_bitor_aggregate_impl) RETURN NUMBER IS
BEGIN
-- Logic for bitwise OR
-- see also: http://www.oracledba.co.uk/tips/bitwise_ops.htm
self.result := self.result - BITAND(self.result, ctx2.result) + ctx2.result;
RETURN ODCIConst.Success;
END;
END;
/
-- Now that you have a TYPE to implement the logic, here is where you define the new aggregate function
CREATE OR REPLACE FUNCTION matt_bitor_aggregate ( input NUMBER) RETURN NUMBER
PARALLEL_ENABLE AGGREGATE USING matt_bitor_aggregate_impl;
/
-- Here's a simple test with your test data set...
with test_data as (
SELECT 1 a FROM dual UNION ALL
select 2 from dual union all
select 1 from dual
)
select matt_bitor_aggregate(a)
from test_data;
-- Here is a more complex test that also highlights the fact that you can use ODCI custom aggregates with window clauses.
with test_data as (
SELECT 1 a FROM dual UNION ALL
select 2 from dual union all
select 1 from dual union all
select 16 from dual union all
SELECT 18 from dual)
select a, matt_bitor_aggregate(a) over ( partition by null order by rownum rows between 1 preceding and 1 following ) from
test_data;
Upvotes: 1
Reputation: 1270583
There are no aggregate bit operations in Oracle. One method is an explicit aggregation, bit by bit:
select ((case when max(bitand(bits, 1)) > 0 then 1 else 0 end) +
(case when max(bitand(bits, 2)) > 0 then 2 else 0 end) +
(case when max(bitand(bits, 4)) > 0 then 4 else 0 end) +
(case when max(bitand(bits, 8)) > 0 then 8 else 0 end) +
(case when max(bitand(bits, 16)) > 0 then 16 else 0 end) +
(case when max(bitand(bits, 32)) > 0 then 32 else 0 end) +
(case when max(bitand(bits, 64)) > 0 then 64 else 0 end) +
(case when max(bitand(bits, 128)) > 0 then 128 else 0 end) +
)
If you wanted an aggregate bitwise AND, then you would use min()
instead of max()
.
Upvotes: 1
Reputation: 1736
You can use the BITAND operator for AND operation. Using this you can build logic for writing a function for BITOR. Checkout this link http://www.oracledba.co.uk/tips/bitwise_ops.htm
Upvotes: 1
Reputation: 50047
In order to do bit-wise logic you have to do a "bit" of math. (Bad puns are free around here :-).
Oracle defines the BITAND function. To get a bitwise 'or' you can define your own function as:
FUNCTION BITOR(n1 IN NUMBER, n2 IN NUMBER)
RETURN NUMBER
IS
BEGIN
RETURN n1 - BITAND(n1, n2) + n2;
END BITOR;
And for completeness, BITXOR is
FUNCTION BITXOR(n1 IN NUMBER, n2 IN NUMBER)
RETURN NUMBER
IS
BEGIN
RETURN BITOR(n1, n2) - BITAND(n1, n2);
END BITXOR;
Best of luck.
Upvotes: 4