使用 SQL Server 和 SSMS,如何从 SQL Server
BitArray
列读取用 C# 创建的 VARBINARY(MAX)
?
有几个人发布了 Sql Server 按位运算符的解释或数据库规范化文章的链接。 这并不能回答这个问题。我没有使用位标志;我也没有兴趣在表中创建无数的位列。我正在存储一个 C# BitArray,它可以在 SqlServer VARBINARY(MAX) 字段中包含数百到数千个值。 BitArray 类似于通用的
List<bool>
,只不过它是经过优化的。我可以轻松地在 C# 中重构并读取这个位数组。我有兴趣了解是否有一种方法可以从 Sql Server 存储过程中的这个位数组读取并确定索引“x”处的项目是真还是假。感谢任何知道这是否可行并可以提供如何做到这一点的示例的人。
使用 C#,我正在创建一个像这样的
BitArray
:
var bitArray = new BitArray(2);
我正在设置这样的位:
bitArray[0] = true;
bitArray[1] = false;
...等等。
我将
bitArray
发送到 SQL Server 存储过程,并将其保存到 VARBINARY(MAX)
列中。我在 C# 中创建的 SqlParameter
看起来像这样:
var byteArray = new byte[2];
bitArray.CopyTo(byteArray,0);
new SqlParameter("@BitArray", SqlDbType.VarBinary) { Size=-1, Value = byteArray }
在我的存储过程中,我将
@BitArray
保存到 VARBINARY(MAX)
列。保存后,我可以从 C# 应用程序的表中检索该值,并从重构的位数组中读取各个 true/false 项。这一切都很好。
我使用
打开
SqlDataReader
SELECT MyByteArray FROM MyTable WHERE Id = @MyId
我在数据读取器中获取字节数组并将其转换为位数组:
Byte[] myByteArray = (byte[])reader.GetValue(0);
BitArray myReconstitutedBitArray = new BitArray(myByteArray)
然后我可以从
myReconstitutedBitArray
中读取内容,发现一切正常:
等等。一切正常。
问题是,在 SQL Server 中,我需要能够从
MyTable.MyByteArray
读取并查看各个真/假值,但我不能。一切都返回错误。我使用的是 SQL Server 2022,因此我使用 GET_BIT
方法,但它对所有内容都返回 false。无论我在索引位置放什么,它都会返回 false。我知道该字段具有正确的值,因为我可以在 C# 中读取它们。
如何在 SQL Server 中读取这些值?
每次这样的调用都会返回 false,即使我知道该位是 true 并且我可以在 C# 中看到真实值:
SELECT GET_BIT(MyByteArray, 0) FROM MyTable WHERE MyId = @MyId
SELECT GET_BIT(MyByteArray, 1) FROM MyTable WHERE MyId = @MyId
不幸的是,C#
BitArray
和 SQL Server 的 GET_BIT
只是工作方式有点不同。
因此,需要采取一些措施来以最小的摩擦在两者之间进行映射(C# 比 SQL 更灵活,因此更容易解决 C# 中的不匹配问题)
我只是添加一些辅助扩展方法到
BitArray
public static class BitArrayExtension
{
public static bool GET_BIT(this BitArray bitArray, int offset)
=> bitArray.Get(bitArray.Length - 8 - (offset & 0xF8) + (offset & 0x7));
public static void SET_BIT(this BitArray bitArray, int offset, bool value)
=> bitArray.Set(bitArray.Length - 8 - (offset & 0xF8) + (offset & 0x7), value);
}
var myBitArray = new BitArray(24);
myBitArray.SET_BIT(6, true)
myBitArray.GET_BIT(6) // == SELECT GET_BIT(MyBitArray, 6) FROM ...
/**/
中的 SQL Server 和 []
var flags = new BitArray(24)
{
// 0x01 == 0b0000_0001 ~ GET_BIT(MyByteArray, 16)
/* 16 */ [00] = true, // <--
/* 17 */ [01] = !true,
/* 18 */ [02] = !true,
/* 19 */ [03] = !true,
/* 20 */ [04] = !true,
/* 21 */ [05] = !true,
/* 22 */ [06] = !true,
/* 23 */ [07] = !true,
// 0x04 == 0b0000_0100 ~ GET_BIT(MyByteArray, 10)
/* 08 */ [08] = !true,
/* 09 */ [09] = !true,
/* 10 */ [10] = true, // <--
/* 11 */ [11] = !true,
/* 12 */ [12] = !true,
/* 13 */ [13] = !true,
/* 14 */ [14] = !true,
/* 15 */ [15] = !true,
// 0x40 == 0b0100_0000 ~ GET_BIT(MyByteArray, 6)
/* 00 */ [16] = !true,
/* 01 */ [17] = !true,
/* 02 */ [18] = !true,
/* 03 */ [19] = !true,
/* 04 */ [20] = !true,
/* 05 */ [21] = !true,
/* 06 */ [22] = true, // <--
/* 07 */ [23] = !true
};
var byteArray = new byte[flags.Length / 8];
flags.CopyTo(byteArray, 0); // will write out => 0x010440 (this is what you will see in SQL Server)
注意映射之间的对称性(例如
/* 06 */ [22]
和 /* 22 */ [06]
),因此我们只需要一个函数
双向转换
int SqlServerOffset(int bitArrayOffset) => (myBitArray.Length / 8 - bitArrayOffset / 8 - 1) * 8 + bitArrayOffset % 8
int BitArrayOffset(int sqlServerOffset) => (myBitArray.Length / 8 - sqlServerOffset / 8 - 1) * 8 + sqlServerOffset % 8
// same as above but using bit operations
int SqlServerOffset(int bitArrayOffset) => myBitArray.Length - 8 - (bitArrayOffset & 0xF8) + (bitArrayOffset & 0x7)
(LEN(MyBitArray) - @BitArrayOffset/8 - 1) * 8 + (@BitArrayOffset % 8) SqlServerOffset
LEN(MyBitArray)<<3 - 8 - (@BitArrayOffset & 0xF8) + (@BitArrayOffset & 0x7) SqlServerOffset