使用 SQL 到 select 记录,来自多个位字段的单个 "true" 位字段
Using SQL to select records with a single "true" bit field from several bit fields
如果我有这样的table:
CREATE TABLE `Suppression` (
`SuppressionId` int(11) NOT NULL AUTO_INCREMENT,
`Address` varchar(255) DEFAULT NULL,
`BooleanOne` bit(1) NOT NULL DEFAULT '0',
`BooleanTwo` bit(1) NOT NULL DEFAULT '0',
`BooleanThree` bit(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`SuppressionId`),
)
有没有一种基于集合的方式,我可以 select 所有恰好具有三个位字段之一的记录 = 1 而无需写出字段名称 ?
例如给定:
1 10 Pretend Street 1 1 1
2 11 Pretend Street 0 0 0
3 12 Pretend Street 1 1 0
4 13 Pretend Street 0 1 0
5 14 Pretend Street 1 0 1
6 14 Pretend Street 1 0 0
我想return记录4和6。
你可以"add them up":
where cast(booleanone as unsigned) + cast(booleantwo as unsigned) + cast(booleanthree as unsigned) = 1
或者,使用元组:
where ( (booleanone, booleantwo, booleanthree) ) in ( (0b1, 0b0, 0b0), (0b0, 0b1, 0b0), (0b0, 0b0, 0b1) )
我不确定你说的 "set-based" 是什么意思。
如果您的布尔值数量会随时间变化并且您不想更新代码,我建议您将它们设为行而不是列。
例如:
CREATE TABLE `Suppression` (
`SuppressionId` int(11) NOT NULL AUTO_INCREMENT,
`Address` varchar(255) DEFAULT NULL,
`BooleanId` int(11) NOT NULL,
`BooleanValue` bit(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`SuppressionId`,`BooleanId`),
)
因此,通过 1 个查询和 'group by',您可以检查布尔值的所有值,无论它们有多少。当然,这会使您的 table 变大。
编辑:刚想出另一个主意:为什么不添加一个 checksum
列,其值将是所有位的总和?所以你会在每次写入你的 table 时更新它,并在你的 select
中检查这个
如果你
- 必须使用这种非规范化的方式来表示这些标志,而您
- 必须 能够在生产环境中向您的 table 添加新的标志列,并且您
- 无法 在添加列时手动重写查询,
然后你必须弄清楚如何编写程序来编写你的查询。
您可以使用此查询检索布尔值列的结果集,然后您可以在程序中使用该结果集来编写涉及所有这些列的查询。
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'Suppression'
AND COLUMN_NAME LIKE 'Boolean%'
AND DATA_TYPE = 'bit'
AND NUMERIC_PRECISION=1
不幸的是,当您添加列时,您在此处提出的方法的效果会呈指数级下降。每当软件工程师说 "exponential" 时,就该 运行 离开尖叫了。说真的。
一种多 更具可扩展性的方法是在您的Suppression
行和标志之间建立一对多关系。添加此 table.
CREATE TABLE SuppressionFlags (
SuppressionId int(11) NOT NULL,
FlagName varchar(31) NOT NULL,
Value bit(1) NOT NULL DEFAULT '0',
PRIMARY KEY (SuppressionID, FlagName)
)
然后,当您想要插入带有一些标志变量的行时,执行以下查询序列。
INSERT INTO Suppression (Address) VALUES ('some address');
SET @SuppressionId := LAST_INSERT_ID();
INSERT INTO SuppressionFlags (SuppressionId, FlagName, Value)
VALUES (@SuppressionId, 'BooleanOne', 1);
INSERT INTO SuppressionFlags (SuppressionId, FlagName, Value)
VALUES (@SuppressionId, 'BooleanTwo', 0);
INSERT INTO SuppressionFlags (SuppressionId, FlagName, Value)
VALUES (@SuppressionId, 'BooleanThree', 0);
这为您提供了一个 Suppression
行,其中在 SuppressionFlags
table 中设置了三个标志。注意使用 @SuppressionId
在第二个 table.
中设置 Id
值
然后要查找仅设置了一个标志的所有行,请执行此操作。
SELECT Suppression.SuppressionId, Suppression.Address
FROM Suppression
JOIN SuppressionFlags ON Suppression.SuppressionId = SuppressionFlags.SuppressionId
GROUP BY Suppression.SuppressionId, Suppression.Address
HAVING SUM(SuppressionFlags.Value) = 1
如果你想要更精细的组合,它会变得有点棘手。例如,如果您希望所有行都设置了 BooleanOne
和 BooleanTwo
或 BooleanThree
,则需要执行类似这样的操作。
SELECT S.SuppressionId, S.Address
FROM Suppression S
JOIN SuppressionFlags A ON S.SuppressionId=A.SuppressionId AND A.FlagName='BooleanOne'
JOIN SuppressionFlags B ON S.SuppressionId=B.SuppressionId AND B.FlagName='BooleanTwo'
JOIN SuppressionFlags C ON S.SuppressionId=C.SuppressionId AND C.FlagName='BooleanThree'
WHERE A.Value = 1 AND (B.Value = 1 OR C.Value = 1)
这种通用的数据库模式称为属性/值模式。因为 SQL 不容易让您使用变量作为列名(它实际上没有反射),这种命名属性的方式是实现可扩展性的最佳途径。
多了一点SQL。但是您可以在生产中根据需要添加任意数量的新标志,而无需重写查询或获得标志匹配的组合爆炸。 SQL 就是为处理这种查询而构建的。
如果我有这样的table:
CREATE TABLE `Suppression` (
`SuppressionId` int(11) NOT NULL AUTO_INCREMENT,
`Address` varchar(255) DEFAULT NULL,
`BooleanOne` bit(1) NOT NULL DEFAULT '0',
`BooleanTwo` bit(1) NOT NULL DEFAULT '0',
`BooleanThree` bit(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`SuppressionId`),
)
有没有一种基于集合的方式,我可以 select 所有恰好具有三个位字段之一的记录 = 1 而无需写出字段名称 ?
例如给定:
1 10 Pretend Street 1 1 1
2 11 Pretend Street 0 0 0
3 12 Pretend Street 1 1 0
4 13 Pretend Street 0 1 0
5 14 Pretend Street 1 0 1
6 14 Pretend Street 1 0 0
我想return记录4和6。
你可以"add them up":
where cast(booleanone as unsigned) + cast(booleantwo as unsigned) + cast(booleanthree as unsigned) = 1
或者,使用元组:
where ( (booleanone, booleantwo, booleanthree) ) in ( (0b1, 0b0, 0b0), (0b0, 0b1, 0b0), (0b0, 0b0, 0b1) )
我不确定你说的 "set-based" 是什么意思。
如果您的布尔值数量会随时间变化并且您不想更新代码,我建议您将它们设为行而不是列。 例如:
CREATE TABLE `Suppression` (
`SuppressionId` int(11) NOT NULL AUTO_INCREMENT,
`Address` varchar(255) DEFAULT NULL,
`BooleanId` int(11) NOT NULL,
`BooleanValue` bit(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`SuppressionId`,`BooleanId`),
)
因此,通过 1 个查询和 'group by',您可以检查布尔值的所有值,无论它们有多少。当然,这会使您的 table 变大。
编辑:刚想出另一个主意:为什么不添加一个 checksum
列,其值将是所有位的总和?所以你会在每次写入你的 table 时更新它,并在你的 select
如果你
- 必须使用这种非规范化的方式来表示这些标志,而您
- 必须 能够在生产环境中向您的 table 添加新的标志列,并且您
- 无法 在添加列时手动重写查询,
然后你必须弄清楚如何编写程序来编写你的查询。
您可以使用此查询检索布尔值列的结果集,然后您可以在程序中使用该结果集来编写涉及所有这些列的查询。
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'Suppression'
AND COLUMN_NAME LIKE 'Boolean%'
AND DATA_TYPE = 'bit'
AND NUMERIC_PRECISION=1
不幸的是,当您添加列时,您在此处提出的方法的效果会呈指数级下降。每当软件工程师说 "exponential" 时,就该 运行 离开尖叫了。说真的。
一种多 更具可扩展性的方法是在您的Suppression
行和标志之间建立一对多关系。添加此 table.
CREATE TABLE SuppressionFlags (
SuppressionId int(11) NOT NULL,
FlagName varchar(31) NOT NULL,
Value bit(1) NOT NULL DEFAULT '0',
PRIMARY KEY (SuppressionID, FlagName)
)
然后,当您想要插入带有一些标志变量的行时,执行以下查询序列。
INSERT INTO Suppression (Address) VALUES ('some address');
SET @SuppressionId := LAST_INSERT_ID();
INSERT INTO SuppressionFlags (SuppressionId, FlagName, Value)
VALUES (@SuppressionId, 'BooleanOne', 1);
INSERT INTO SuppressionFlags (SuppressionId, FlagName, Value)
VALUES (@SuppressionId, 'BooleanTwo', 0);
INSERT INTO SuppressionFlags (SuppressionId, FlagName, Value)
VALUES (@SuppressionId, 'BooleanThree', 0);
这为您提供了一个 Suppression
行,其中在 SuppressionFlags
table 中设置了三个标志。注意使用 @SuppressionId
在第二个 table.
Id
值
然后要查找仅设置了一个标志的所有行,请执行此操作。
SELECT Suppression.SuppressionId, Suppression.Address
FROM Suppression
JOIN SuppressionFlags ON Suppression.SuppressionId = SuppressionFlags.SuppressionId
GROUP BY Suppression.SuppressionId, Suppression.Address
HAVING SUM(SuppressionFlags.Value) = 1
如果你想要更精细的组合,它会变得有点棘手。例如,如果您希望所有行都设置了 BooleanOne
和 BooleanTwo
或 BooleanThree
,则需要执行类似这样的操作。
SELECT S.SuppressionId, S.Address
FROM Suppression S
JOIN SuppressionFlags A ON S.SuppressionId=A.SuppressionId AND A.FlagName='BooleanOne'
JOIN SuppressionFlags B ON S.SuppressionId=B.SuppressionId AND B.FlagName='BooleanTwo'
JOIN SuppressionFlags C ON S.SuppressionId=C.SuppressionId AND C.FlagName='BooleanThree'
WHERE A.Value = 1 AND (B.Value = 1 OR C.Value = 1)
这种通用的数据库模式称为属性/值模式。因为 SQL 不容易让您使用变量作为列名(它实际上没有反射),这种命名属性的方式是实现可扩展性的最佳途径。
多了一点SQL。但是您可以在生产中根据需要添加任意数量的新标志,而无需重写查询或获得标志匹配的组合爆炸。 SQL 就是为处理这种查询而构建的。