1

我有一个将标志值存储在整数列中的表。(我不是设计者)

我有一个查询,其中一个条件是

标志 & 256==0

上述条件适用于所有第 8 位不为 1 的无符号数

问题是,在 sql server 中,将 int 转换为二进制时,它花费了太多时间,并且查询的性能随着许多其他连接而下降。

有没有办法用简单的条件替换上述条件?

4

1 回答 1

2

从你所说的,我推测你想flags & 256用 sargable 运算符表达。

如果您的标志是使用最多的位之一(即使用第 8 位以上的少数位),这将更容易。为此,我们需要记住,写b在位上的数字的值介于 0 和 2 b -1 之间。

标志值第 8 位的含义

  • 如果第 8 位是最大使用的,则表示为数字的标志的值介于 0 和 511 之间。然后很容易看到它flags & 256 > 0等同于flags >= 256,因为任何未设置第 8 位的二进制数都是 7 位上的数字,因此具有介于 0 和 255 之间的值。

  • 理想情况下,要在没有位掩码的情况下表达这一点,我们想做(flags % 512) >= 256,但我怀疑这也不会是 sargable。因此,对于第 8 位以上的每个可能的标志组合,我们将需要枚举设置第 8 位的间隔。如果只使用 1 或 2 个,则容易,否则更难。

10 位间隔示例

假设您使用 10 位作为标志,那么您的位可以以 00、01、10 或 11 为前缀。因为这些是位 10 和 9,所以相应的数字(假设这些前缀以 8 个零为后缀)是 0、512 、 1024 和 1536。

因此,未设置第 8 位的“标志”值的所有区间为:

  • [0,255]
  • [512,767]
  • [1024,1279]
  • [1536,1791]

因此flag & 256 == 0等价于flags BETWEEN 0 AND 255 OR flags BETWEEN 256 AND 511 OR flags BETWEEN 512 AND 767 OR flags BETWEEN 768 AND 1023

推广区间

假设您可能有 F 标志(也就是使用的位数)并且您对标志 C 感兴趣(因此该位的位置)。

1对于所有 i < 2 F-C ,带有标志的间隔是[(2i+1)2 C , (2i+2)2 C -1]

0对于所有 i < 2 F-C , [(2i)2 C , (2i+1)2 C -1] ,标志设置为的区间是

如您所见,您需要考虑 2 个F-C区间。因此,如果有可能贿赂设计您 SQL 数据库的人,请让他尽可能地设置该标志:F=C,而您只有一个标志需要注意。

这是一个快速而肮脏的 bash 脚本,它将为您编写丑陋的间隔子句:

#!/usr/bin/env bash

bits=10 # F, total number of possible flags
check=8 # C, position of the flag

grain=$(( 2 ** check ))
interval_offsets="$(seq 0 $grain $(( 2 ** bits -1 )) )"


str=
for i in $interval_offsets 
do str="$str OR flags BETWEEN $(( i )) AND $(( i + grain - 1))"
done

echo "intervals for bit $check NOT set : ${str# OR}"


str=
for i in $interval_offsets 
do str="$str OR flags BETWEEN $(( i + grain )) AND $(( i + 2 * grain - 1))"
done

echo "intervals for bit $check set : ${str# OR}"
于 2014-12-15T15:59:50.370 回答