我目前正在开发一个工具来帮助我的用户将他们的SQL代码移植到SQL-Server 2005.为此,我将SQL解析为语法树,分析它需要注意的构造,修改它并转换它回到T-SQL。
在我想支持的事情上,其他RDBMS的“bools is values too”语义。例如,MS-Access允许我编写select A.x and A.y as r from A
,这在T-SQL中是不可能的,因为:
因此,我的转换例程将上述语句转换为:
select case
when (A.x<>0) and (A.y<>0)
then -1
when not((A.x<>0) and (A.y<>0))
then 0
else
null
end as r
from A;
哪个有效,但很烦人,因为我必须复制逻辑表达式(可能非常复杂或包含子查询等),以便区分true
,false
和{{1 - 后者应映射为null。所以我想知道T-SQL专业人员是否知道更好的方法来实现这个目标?
更新 我想指出,试图将操作数保持在整数域中的解决方案必须考虑到,一些操作数可能是第一位中的逻辑表达式。这意味着需要一个将bool转换为值的有效解决方案。例如:
unknown
答案 0 :(得分:1)
我认为没有一个好的答案,实际上它是SQL的限制。你可以为你需要的每个布尔表达式创建一个UDF
CREATE FUNCTION AndIntInt
(
@x as int,@y as int
)
RETURNS int
AS
BEGIN
if (@x<>0) and (@y<>0)
return -1
if not((@x<>0) and (@y<>0))
return 0
return null
END
通过
使用select AndIntInt(A.x,A.y) as r from A
答案 1 :(得分:1)
布尔处理
Access似乎使用给出2个布尔值的逻辑
我不确定这是否是其他DBMS(Oracle,DB2,PostgreSQL)处理bool + null的方式,但这个答案是基于Access的确定(MySQL和SQLite同意)。结果表如下。
X Y A.X AND B.Y
0 0 0
0 -1 0
0 (null) 0
-1 0 0
-1 -1 -1
-1 (null) (null)
(null) 0 0
(null) -1 (null)
(null) (null) (null)
SQL Server帮助程序1:来自任何“单值”的布尔函数
在SQL Server中,此功能将填补缺少any value as boolean
功能的空白。它返回一个三元结果,1/0 / null - 1和0是SQL Server等效的true / false(实际上不是布尔值)。
drop function dbo.BoolFromAny
GO
create function dbo.BoolFromAny(@v varchar(max)) returns bit as
begin
return (case
when @v is null then null
when isnumeric(@v) = 1 and @v like '[0-9]%' and (@v * 1.0 = 0) then 0
else 1 end)
end
GO
注意:以Access作为起点,只有数值0的计算值为FALSE 这使用了一些SQL Server技巧
isnumeric
返回1,但在@v * 1.0
处失败,因此需要对LIKE
[0-9]%``进行显式测试以“修复”isnumeric。@v * 1.0
需要克服一些算术问题。如果将字符串“1”传递给没有* 1.0的函数,它将弹出现在我们可以测试这个功能了。
select dbo.BoolFromAny('abc')
select dbo.BoolFromAny(1)
select dbo.BoolFromAny(0) -- the only false
select dbo.BoolFromAny(0.1)
select dbo.BoolFromAny(-1)
select dbo.BoolFromAny('')
select dbo.BoolFromAny('.')
select dbo.BoolFromAny(null) -- the only null
现在,您可以在针对任何单个列的查询中安全地使用它,例如
SELECT dbo.BoolFromAny(X) = 1
SQL Server帮助程序2:返回BOOL AND BOOL结果的函数
现在,下一部分是在SQL Server中创建相同的真值表。此查询显示两位列如何交互,以及简单的CASE语句与Access生成相同的表以及更复杂的表。
select a.a, b.a,
case
when a.a = 0 or b.a = 0 then 0
when a.a = b.a then 1
end
from
(select 1 A union all select 0 union all select null) a,
(select 1 A union all select 0 union all select null) b
order by a.a, b.a
这很容易表达为函数
create function dbo.BoolFromBits(@a bit, @b bit) returns bit as
begin
return case
when @a = 0 or @b = 0 then 0
when @a = @b then 1
end
end
SQL Server转换其他表达式(不是单个值)
示例是SQL Server中的“true boolean”,它不能是列的结果。
select A > B -- A=B resolves to one of true/false/null
from C
需要表达为
select case when A is null or B is null then null when A > B then 1 else 0 end
from C
但是如果A不是标量值而是像(select sum(x)...)
那样的子查询,那么你可以看到A将出现两次并在CASE语句中被重复两次(重复)。
最终测试 现在我们将所有转换规则用于此长表达式
SELECT X AND Y=Z AND C FROM ..
( assume X is numeric 5, and C is varchar "H" )
( note C contributes either TRUE or NULL in Access )
这转换为SQL Server(链接两个函数并使用CASE)
SELECT dbo.BoolFromBits(
dbo.BoolFromBits(dbo.BoolFromAny(X), CASE WHEN Y=Z then 1 else 0 end),
dbo.BoolFromAny(C))
FROM ...
访问Bool or bool
为了完整性,这是Access bool OR bool
的真值表。基本上,它与AND相反,所以
因此,SQL SERVER案例语句将是
case
when a.a = 1 or b.a = 1 then 1
when a.a = b.a then 0
end
(遗漏ELSE子句是故意的,因为省略时结果为NULL)
答案 2 :(得分:-1)
编辑:根据添加到问题中的其他信息以及对其中一个建议的答案所做的评论,我正在重新制定这个答案:
如果您移植到SQL Server,那么我希望您也在转换数据以匹配SQL Server类型。如果您有一个布尔字段,则True,False和Unknown将映射为1,0和NULL作为NULLable BIT字段。
考虑到这一点,您只需要担心转换纯布尔值。表达式如:
exists (select * from B where B.y=A.y)
和
A.x in (1,2,3)
已经处于可行的状态。意思,声明如下:
IF (EXISTS(SELECT 1 FROM Table))
和
IF (value IN (list))
已经正确了。所以你只需要担心“真实”的“1”本身就不够了。因此,您可以通过测试它们是否实际上等于“1”来将“1”值转换为布尔表达式。例如:
IF (value = 1)
相当于您之前所拥有的:
IF (value)
将所有这些放在一起,您应该能够简单地将旧代码的纯布尔值的所有实例转换为“value = 1”形式的布尔表达式,因为1将生成True,0将生成False和NULL会给你错误。
但是,真正的复杂性是通过WHERE条件选择值与测试不同。布尔表达式在WHERE条件中正确计算,但没有直接表示SELECT(特别是因为NULL / Unknown不是真正的布尔值)。因此,您可以在WHERE条件中使用“Value = 1”转换,但如果您希望将其作为结果进行选择,则仍需要CASE语句。
刚才提到过,由于NULL / Unknown不是真正的布尔值,因此为了WHERE条件的目的,尝试将“NULL AND NULL”转换为NULL是没有意义的。实际上,NULL确实为FALSE,因为它无法确定为TRUE。同样,在SELECT语句中,这可能与您的目的不同,这也是CASE语句是您唯一选择的原因。