将 SQL Server 中的元组与三元布尔逻辑进行比较

comparing tuples in SQL Server with ternary boolean logic

提问人:Olmo 提问时间:4/22/2011 最后编辑:Olmo 更新时间:4/22/2011 访问量:623

问:

我有一个关于三元布尔逻辑的问题,它会影响如何在 LINQ 提供程序中实现多态实体之间的比较。

在 SQL 中,如果使用外键将 Region 表联接到 Country :

SELECT * From Region r1, Region r2
WHERE r1.Country == r2.Country 

(注意:无论您使用 JOIN 还是 WHERE synthax,结果都是相同的)

它将返回条件为 true 的值,而不是条件为 false 或未知的值(因为某些键为 null)。因此,如果我们否定这个条件:

SELECT * From Region r1, Region r2
WHERE r1.Country != r2.Country 

我们得到条件为 true 的值(现在是不同的键),我们将跳过具有相同键的值,或者具有一些 null 值的值,因为条件再次返回 unknown。即使我们这样写:

SELECT * From Region r1, Region r2
WHERE not(r1.Country == r2.Country) 

未知数将被传播,因此对于这个简单条件,空值永远不会出现。目前为止,一切都好。

现在让我们想象一下,一个地区可以有一个国家(对于欧洲小国)或一个州(对于美国、俄罗斯、中国......如果该区域有一个州,它将具有 Country null,反之亦然。

我们如何连接对 [Country,State],使其具有与以前相同的属性?

SELECT * From Region r1, Region r2
WHERE r1.Country == r2.Country OR r1.State == r2.State

如果此表达式连接,则返回 true,否则返回 unknown。我们希望它仅在所有字段都为 null 的情况下返回 unknown,否则如果我们否定:

SELECT * From Region r1, Region r2
WHERE not(r1.Country == r2.Country OR r1.State == r2.State)

它不返回任何行!。如果我们尝试一个更复杂的表达式:

SELECT * From Region r1, Region r2
WHERE (r1.Country == r2.Country AND r1.Country IS NOT NULL AND r2.Country IS NOT NULL)  
   OR (r1.State   == r2.State   AND r1.State   IS NOT NULL AND r2.State   IS NOT NULL) 

然后,当货币对匹配时,它将返回 true,否则返回 false,并且永远不会 nothing。然后,如果我们否定,它将返回所有行都为 null 的值,其行为与 firs 示例中的行为不同。

那么比较这一对的表达式会表现得像 SQL 相等吗?

  • 匹配时为 True
  • 不匹配时为 False
  • 当某个操作数为 null 时未知。
sql-server linq null 元组 boolean-logic

评论


答:

2赞 Quassnoi 4/22/2011 #1

创建计算列:

Location AS COALESCE(Country, State)

,索引它并像往常一样进行比较:

SELECT  *
FROM    Region r1
JOIN    Region r2
ON      r2.Location = r1.Location

Location如果两者都是 和 是 .NULLCountryStateNULL

更新:

如果 和 可以相互比较,请创建一个附加列,显示使用的列:CountryState

LocationType AS CASE WHEN Country IS NOT NULL THEN 1 WHEN State IS NOT NULL THEN 2 END,
LocationId AS COALESCE(Country, State)

,索引两者并进行比较:

SELECT  *
FROM    Region r1
JOIN    Region r2
ON      r2.LocationType = r1.LocationType
        AND r2.LocationID = r1.LocationID

评论

0赞 Olmo 4/22/2011
如果我这样做,那么我会将奥地利(Id=1 的国家)与亚利桑那州(Id=1 的州)的地区混合。我怎样才能让它们与众不同?
0赞 Olmo 4/22/2011
这肯定会起作用,非常感谢。我不会按照你的方式去做,因为这是一个通用的解决方案,我可以比较任何一组实体中的任何一组实体,并且我不想用计算字段和索引来使模型混乱,以进行每次可能的比较。无论如何,你都应该得到一票。
0赞 Quassnoi 4/22/2011
@Olmo:您可以在查询中将计算字段替换为其定义:,尽管它不可访问。请注意,使用该解决方案,比较的数量将呈二次增长。ON CASE WHEN r1.… END = CASE WHEN r2.… END AND COALESCE(r1.…) = COALESCE(r2.…)OR
0赞 Olmo 4/22/2011
其实你在说服我。我认为比较的数量不会二次增长,但可比较是一个优势。此外,我认为计算值取决于每个可能的比较,但它们实际上是稳定的,因为我有一个全局的“表到 id”映射。我会走你的路。非常感谢。
1赞 ypercubeᵀᴹ 4/22/2011 #2

你能试试这个吗?

SELECT *
FROM Region r1
  JOIN Region r2
    ON ( r1.Country = r2.Country 
         AND r1.State IS NULL 
         AND r2.State IS NULL
       )
    OR ( r1.State = r2.State 
         AND r1.Country IS NULL 
         AND r2.Country IS NULL
       )

评论

0赞 ypercubeᵀᴹ 4/22/2011
Thnx。我想补充一点,您的表结构需要一些规范化以避免 NULL。这样一来,您的查询就不需要这种复杂的条件了,因为对于大表来说,这种条件可能会很慢。当然,你可能永远不会看到,一个世界上能有多少个国家?不过,最好在一开始就考虑这一点,而不是在架构中添加子区域、城镇和村庄时考虑这一点,然后突然意识到此查询是一个瓶颈。
0赞 Olmo 4/22/2011
我一直在花时间概括您的解决方案,当您使用简单的键 [Country,State] 加入元组 [Country,State] 时。当您将一个三重奏 [Country,State,SpaceColony]、另一个三重奏或元组等进行比较时......我们已经解决了这个问题,但我也在考虑另一种解决方案,因为它更易于理解,必要时可以作为索引。你怎么看?
0赞 ypercubeᵀᴹ 4/22/2011
我认为您应该将其作为单独的问题与您的表结构(字段、主键和外键)一起发布,因为我们现在只能对结构进行猜测。Quassnoi 的建议(在一个字段中合并 and 并添加一个似乎是最好的,但显示您的其他相关表格将有助于理解问题并建议可能的重组/规范化。CountryStateLocationType
0赞 Olmo 4/22/2011
我不是在构建一个应用程序,而是一个框架,我必须想出最好的通用解决方案。例如,可以将一个人的宠物 [狗、猫、鸟] 与农场中的动物 [狗、马、猪、鸡] 进行比较。