我的 SQL Server 数据库中有两个表。一张表
BusinessOperations
包含有关该业务对象的各种信息,另一张表 OperationType
纯粹是一个按位标志表,如下所示:
| ID | Type | BitFlag |
| 1 | Basic-A | -2 |
| 2 | Basic | -1 |
| 3 | Type A | 0001 |
| 4 | Type B | 0002 |
| 5 | Type C | 0004 |
| 6 | Type D | 0008 |
| 7 | Type E | 0016 |
| 8 | Type F | 0032 |
BitFlag 列是一个 varchar 列,作为示例,位标志被插入为
'0001'
。在 BusinessOperations
表中,有一个列,使用这些表的应用程序会根据在应用程序 UI 中选择的内容来更新它。例如,我有一种类型选择了 Basic
、Type A
和 Type B
类型。 BusinessOperations
中的列值为 3。
基于此,我正在尝试编写一个查询,该查询将向我显示如下内容:
| ID | Name | Description | OperationType |
| 1 | Test | Test | Basic, Type A, Type B |
这是
BusinessOperations
表的实际布局(Basic-A
和 Basic
是位列:
| ID | Name | Description | Basic-A | Basic | OperationType |
| 1 | Test | Test | 0 | 1 | 3 |
没有任何东西将这两个表相互关联,因此我无法执行联接。我对按位运算非常缺乏经验,并且不知道如何准确地构建用于分析此数据的选择查询。我觉得它需要一个
STUFF
或 CASE
,但我不知道如何让它只显示类型,而不仅仅是结果的 BitFlag。
SELECT ID, Name, Description, OperationType
FROM OperationType
ORDER BY ID
由于您将标记存储在
OperationType
中作为 VARCHAR
,因此您需要做的第一件事是将 CONVERT
或 CAST
字符串转换为数字,以便我们可以进行正确的按位比较。我对 SQL Server 有点不熟悉,但您可能需要在转换之前删除前导零。因此,我们所需的 SQL 中的 OperationType
列将类似于
CONVERT(INT, BitFlag)
然后,将其与我们的
OperationType
专栏进行比较,看起来像这样
CONVERT(INT, BitFlag) & OperationType
完整的查询看起来像这样(再次原谅我缺乏 SQL Server 专业知识):
SELECT bo.ID, bo.Name, bo.Description, ot.Type
FROM BusinessOperations AS bo
JOIN OperationType AS ot
ON CONVERT(INT, ot.BitFlag) & OperationType <> 0
上面的查询将有效地为您提供操作类型的列表。如果您确实需要将它们放在一行中,请参阅其他答案以了解如何在 SQL Server 中模拟类似
GROUP_CONCAT
的内容。 免责声明:加入位掩码并不能保证性能。
这个答案没有解决的另一个问题是遗留的 Basic 和 Basic-A 字段的问题。就我个人而言,我会做以下两件事之一:
OperationType
表中删除,并让应用程序根据 Basic
和 Basic-A
列酌情将两者粘上。OperationType
表中,并让应用程序根据需要填充旧列以及 OperationType
列。正如 Aaron Bertrand 在评论中所说,这对于 Bitmasking 来说根本不是问题。拥有一个将
BusinessOperations.ID
与 OperationType.ID
关联起来的多对表可以更好地解决您的所有问题。
在 BusinessOperations 表中,Basic-A 和 Basic 字段是位字段,这只是表示值只能是 1 或 0 的另一种方式。将其视为布尔值 True/False。因此,在您的查询中,您可以检查每个查询以确定是否包含“Basic-A”和“Basic”。
OperationType 可能是一个 id,您可以在 OperationsType 表中查找它以获取 Type 和 BitFlag。在不完全理解您的数据的情况下,您似乎可以对该部分进行联接。希望这是正确的总体方向。如果没有,请告诉我。
似乎对我有用的方法是......
DECLARE @Unknown AS int = 0;
DECLARE @Orange AS int = 1;
DECLARE @Big AS int = 2;
DECLARE @Green AS int = 4;
DECLARE @Small AS int = 8;
DECLARE @MYTABLE TABLE (
id INT,
name VARCHAR(50),
description INT
);
INSERT INTO @MYTABLE (id, name, description) VALUES (1, 'item one', @Big + @Orange);
INSERT INTO @MYTABLE (id, name, description) VALUES (2, 'item two', @Small + @Orange);
INSERT INTO @MYTABLE (id, name, description) VALUES (3, 'item three', @Small + @Green);
INSERT INTO @MYTABLE (id, name, description) VALUES (4, 'item four', @Unknown + @Green);
SELECT TOP 1
(SELECT COUNT(*) FROM @MYTABLE WHERE (SELECT CASE description & @Small WHEN @Small THEN 1 WHEN 0 THEN 0 END) = 1) AS 'Small Items',
(SELECT COUNT(*) FROM @MYTABLE WHERE (SELECT CASE description & @Big WHEN @Big THEN 1 WHEN 0 THEN 0 END) = 1) AS 'Big Items'
DELETE @MYTABLE