我有一个表, int 值被用作位域(每个位都是一个标志)。
现在我想用二进制操作(在我的例子中为OR)聚合它们,以便:
SELECT 1 AS bitfield
INTO #TABLE
UNION ALL SELECT 1 + 2 + 8 + 32
UNION ALL SELECT 2 + 128
UNION ALL SELECT 2 + 32
SELECT AND_AGGR(bitfield) -- Invalid because AND_AGGR doesn't exist
FROM #TABLE
DROP #TABLE
会产生值171
这样做的好方法是希望不需要很多|
和MAX
(但如果必须,你必须这样做)?
我自己使用MS SQL Server 2008,但其他服务器上的解决方案也很有用。
答案 0 :(得分:2)
在MySQL和PostgreSQL上,您可以使用BIT_OR
。
我认为SQL Server没有这个聚合函数。
如你所说,你可以用很多MAX
和&
执行此操作:
MAX(x & 1) + MAX(x & 2) + ... + MAX(x & 128)
答案 1 :(得分:1)
如果您期望结果为171
,那么您的意思是二进制OR
而不是AND
吗?
在任何情况下,此解决方案都会将值聚合到变量中:
SELECT 1 AS bitfield
INTO #TABLE
UNION ALL SELECT 1 + 2 + 8 + 32
UNION ALL SELECT 2 + 128
UNION ALL SELECT 2 + 32
DECLARE @i int = 0
SELECT @i = @i | bitfield
FROM #TABLE
SELECT @i
DROP TABLE #table
如果您希望按其他字段对聚合进行分组,则可能无法满足您的要求。
在大桌子上也不太可能表现良好。
答案 2 :(得分:0)
在 MS SQL Server 中
let i = 0, j = s.length - 1;
while (i < j) {
[s[i], s[j]] = [s[j], s[i]];
i++;
j--;
}
return s;
// **Run time 120 ms and memory 46B**
印刷品
DECLARE @agg VARCHAR(MAX) = '0001,0011,0101,0101,0101'
SELECT CONVERT(binary(4), VALUE, 2) , VALUE FROM STRING_SPLIT( @agg , ',')
DECLARE @sum AS BIGINT = 0
DECLARE @mul AS BIGINT = 0xffffffff
SELECT @sum |= v
, @mul &= v
FROM STRING_SPLIT( @agg , ',')
CROSS APPLY (VALUES (CONVERT(binary(4), VALUE, 2))) _(v)
PRINT FORMAT(@sum,'X8')
PRINT FORMAT(@mul,'X8')
你需要更复杂的词:
VALUE
---------- ------------
0x00010000 0001
0x00110000 0011
0x01010000 0101
0x01010000 0101
0x01010000 0101
01110000
00010000
使用付款期位图时
CREATE OR ALTER FUNCTION dbo.BOR( @agg VARCHAR(MAX))
RETURNS BIGINT
AS
BEGIN
DECLARE @sum AS BIGINT = 0
SELECT @sum |= CONVERT(BIGINT, VALUE)
FROM STRING_SPLIT( @agg , ',')
RETURN @sum
END
GO
CREATE OR ALTER FUNCTION dbo.BAND( @agg VARCHAR(MAX))
RETURNS BIGINT
AS
BEGIN
DECLARE @mul AS BIGINT = 0xffffffffffffffff
SELECT @mul &= CONVERT(BIGINT, VALUE)
FROM STRING_SPLIT( @agg , ',')
RETURN @mul
END
GO
但有时只需要一个人思考,你就可以用 SUM 做到这一点
;WITH delayedPayment AS
(SELECT * FROM ( VALUES
( 123, 67, '2020-2-1')
,( 123, 67, '2020-4-1')
,( 123, 67, '2020-5-1')
,( 123, 67, '2020-6-1')
,( 123, 68, '2020-6-1') -- another agreement
,( 123, 67, '2020-12-1')
,( 456, 69, '2020-4-1')
,( 456, 69, '2020-8-1')
,( 456, 69, '2020-10-1')
,( 456, 69, '2020-11-1')) _(cuno, loan, missedDuedate)
)
, delayPattern AS
(SELECT cuno
, sum_months
, bor_months
, IIF( FORMAT( CAST(bor_months AS BIGINT), 'X16') LIKE '%111%', 'dalyad 3+ month in row', NULL) delayState
FROM (SELECT cuno
, SUM(POWER( 16.0, CONVERT( BIGINT, DATEDIFF( month, missedDuedate, '2020-12-1')))) sum_months
, dbo.BOR( STRING_AGG( CONVERT( BIGINT, POWER( 16.0, DATEDIFF( month, missedDuedate, '2020-12-1'))),',')) bor_months
FROM delayedPayment
GROUP BY cuno
) s
)
SELECT cuno
, FORMAT( CAST(sum_months AS BIGINT), 'X16') sum_months
, FORMAT( CAST(bor_months AS BIGINT), 'X16') bor_months
, delayState
FROM delayPattern
cuno sum_months bor_months delayState
123 00000*10112*000001 00000*10111*000001 dalyad 3+ month in row
456 0000000100010110 0000000100010110 NULL
将打印
, delayPattern AS -- optimal
(SELECT cuno
, bor_months
, IIF( FORMAT( CAST(bor_months AS BIGINT), 'X16') LIKE '%111%', 'dalyad 3+ month in row', NULL) delayState
FROM (SELECT cuno
, SUM(POWER( 16.0, missedmonth)) bor_months
FROM ( SELECT DISTINCT cuno
, missedmonth
FROM delayedPayment
CROSS APPLY (VALUES ( DATEDIFF( month, missedDuedate, '2020-12-1'))) _(missedmonth)
GROUP BY cuno, missedmonth
) ss
GROUP BY cuno
) s
)
SELECT cuno
, FORMAT( CAST(bor_months AS BIGINT), 'X16') bor_months
, delayState
FROM delayPattern
注意:我正在使用十六进制格式和 POWER(16.0, X) ,只是为了偷懒, POWER(2.0, X) 将是正确的,但是您需要 bin->string formatter。像这样:
cuno bor_months delayState
123 0000010111000001 dalyad 3+ month in row
456 0000000100010110 NULL