关系运算符
在所有运算符中,关系运算符是最复杂的运算符,这就是我们将它们留到最后的原因。
关系运算符也称为比较运算符,它们用于比较事物。
比较结果是布尔值 false 或 true。
有趣的是,如果你在 VFP 帮助中检查它,你只能看到一个简短的列表操作和更多的行,就好像它是关于那些运算符的。
嗯,复杂性来自这样一个事实:它们可以在任何类型上运行,无论是数字,日期,日期时间,逻辑还是字符串,甚至是对象。此外,行为可能看起来很尴尬,除非你知道结果会产生什么影响,否则你得不到你的期望。
让我们从关系运算符列表开始:
操作者 | 描述 | MOST 基本样本 |
---|---|---|
> | 比…更棒 | ?1> 2 && .F。 |
< | 少于 | ?1 <2 && .T。 |
> = | 大于或等于 | ?1> = 2 &&。F。 |
<= | 小于或等于 | ?1 <= 2 && .T。 |
= | 等于 | ?1 = 1 && .T。 |
== | 完全等于(对字符串有意义) | ? ‘1’=‘1’&& .T。 |
!=,#,<> | 不等于(所有 3 个运算符的行为方式相同,选择你最喜欢的) | ?1 != 1 && .F。 |
尽管可以将这些用于所有数据类型,但操作数之间应该存在类型兼容性。例如,如果你尝试将 Date 与 Integer 进行比较,则会出现错误。
可以比较日期和日期时间,尽管它们是不同的类型,VFP 会隐式为你进行转换。
? Date() > DateTime() && .F.
? Date() <= DateTime() && .T.
? Date() < DateTime() && .T. if it is not midnight
当操作数是数字时,所有这些操作符都很简单直接,它们就像在数学表达式中那样工作。
有了逻辑操作数,F。被认为小于 .T。
对象我们比较的是对象在内存中的引用。因此,最常用的比较是确定两个对象变量是否指向同一个对象。即:
local o1, o2
o1 = createobject('Label')
o2 = createobject('Label')
? m.o1 = m.o2 && is o1 and o2 the same object?
? m.o1 > m.o2 && this would work too but likely you would never use
* remember we are comparing their references in memory
*
* They are different objects, but do they have any difference in their properties?
? CompObj(m.o1, m.o2) && .T. They are identical properties wise
字符数据类型的比较,即字符串的比较是 VFP 中最令人困惑的。它不像其他语言和/或数据库那样工作,并且对 VFP 是独一无二的(也可能是其他一些 xBase 语言)。
许多年前,我甚至看到社区中一些非常先进的成员,他们还不知道这些运算符如何在 VFP 中运作。因此,可以理解的是,轻微的细微差别可能会让新手容易混淆。
比较基本上是关于相等与否。如果它们不相等,那么我们可能会考虑运算符>,<,> =,<=,对吗?对于字符串,当两个字符串被认为相等时,它会令人困惑。
重要提示: VFP 字符串区分大小写。 ‘A’和’a’是两个不同的字符串。对于许多数据库而言,情况并非如此,其中默认情况下使用不区分大小写的排序规则。例如,在使用不区分大小写(CI)排序规则创建的表上的 postgreSQL 或 MS SQL Server 中:
select * from myTable where Country = 'TURKEY'
select * from myTable where Country = 'Turkey'
会产生相同的结果。在 VFP 中虽然只能获得套管匹配的那些。然而,VFP 具有一些整理支持并且使得不区分大小写的比较。 (不要相信,见下文)
-
如果两个字符串不相等,那么这么好,**只要你没有更改任何默认值,**那么它们将根据它们的 ASCII 值进行比较。
? 'Basoz' < 'Cetin' && is true. ? 'basoz' < 'Cetin' && is false. ? 'Cetin' < 'David' && is true. ? 'Çetin' < 'David' && is false.
整理的默认值是机器,这就是你得到的。当你将排序规则更改为其他内容时,你将根据该排序规则的排序顺序进行比较。对于默认计算机以外的排序规则设置,你还意味着比较不区分大小写(不要相信这是相同的):
set collate to 'GENERAL'
? 'Basoz' < 'Cetin'
? 'basoz' < 'Cetin'
? 'Cetin' < 'David'
? 'Çetin' < 'David'
现在所有这些表达式都是正确的。
个人建议:VFP 中的排序规则从来都不够可靠。我建议你不要使用排序规则并坚持使用默认的’MACHINE’。如果你要使用排序规则,那么请记住,当你遇到关于字符数据的非常意外的事情时,请先检查它。我已经看到并证明它在许多情况下都失败了,但后来我在 VFP9 版本之前停止尝试使用它,它现在可能是一致的,我真的不知道。
考虑到我们用字符串覆盖不等式情况,棘手的是同等情况。在 VFP 中基本上有两个设置影响比较:
- SET EXACT(默认为 OFF 并影响常规比较 - 除了 SQL 之外)
- SET ANSI(默认为 OFF 并仅在 SQL 中进行效果比较 .SET EXACT 对 SQL 查询中的比较没有影响。
使用 SET EXACT OFF,读取比较为右边的字符串是否为左边的字符串?它们与正确的字符串长度进行比较。
? "Bobby" = "B" && Bobby starts with B, so TRUE
? "Bobby" = "Bob" && Bobby starts with Bob, so TRUE
? "Bobby" = "Bob " && Bobby starts with Bob but there is a trailing space there, FALSE
? "Bobby" = "bob" && would be true with collation set to GENERAL
注意,通过常规比较,
Bobby
=B
为 TRUE,但B
=Bobby
为 FALSE。换句话说,操作数的位置很重要。
使用 SET EXACT ON 时,字符串必须完全匹配,但忽略它们的尾随空格(我们忽略了 set collate,这也会使用大小写不敏感):
? "BOBBY" = "BOB" && FALSE
? "BOBBY" = "BOBBY" && TRUE
? "BOBBY" = "BOBBY " && TRUE
? "BOBBY " = "BOBBY" && TRUE
现在,使用 SQL 命令 SET EXACT 没有任何效果,它的行为就像 SET EXACT OFF 一样。
Select * from Customers where Country = 'U'
从美国,英国选择以’U’开头的任何国家的客户。
但是,在 SQL 中,根据定义,更改操作数的顺序应该会产生相同的结果。从而:
Select * from Customers where 'U' = Country
也会以相同的方式工作(请注意与非 SQL 命令的区别)。
当你想暗示完全匹配时,一个选项是打开 ANSI:
SET ANSI ON
Select * from Customers where Country = 'USA'
返回来自美国的所有客户。请注意,将忽略国家/地区字段 OR 中右侧表达式的尾随空格。无论你有多少落后都没关系。你可以像进行比较一样进行比较:RTRIM(Country)
= RTRIM(‘USA’)。
尽管在 VFP 中的操作符中未提及,但 SQL 运算符是 LIKE。当你使用 LIKE 时,无论 SET ANSI 设置如何,你都会获得完全匹配的比较(使用 LIKE 强制和隐式 ANSI ON 情况 - 毕竟它是 ANSI 运算符)。但是,请注意行为略有不同。除非带有预告片的总大小等于或小于字段大小,否则它不会忽略尾随空格。例如,如果 Country 字段是
C(10)
,则 Country =‘USA’或 Country =‘USA__‘将起作用,但 Country =‘USA___________‘将失败( 下划线表示一个空格,最后一个表示多于 7 个尾随空格)。
最后我们是最后一个运算符,==。这意味着完全相同,并使用字符串。一个优点是,使用==你总是意味着你想要完全匹配,无论 SET EXACT 或 SET ANSI 设置如何。但是请注意,当它是 SQL 命令或非 SQL 常规命令时,它的行为是不同的。
使用 SQL:
Select * from Customers where Country == 'USA'
无论 ANSI 和 EXACT 设置如何,我们都希望所有来自美国的客户。任何一侧的尾随空格都会被忽略。
使用非 SQL:
? m.lcString1 == m.lcString2
只有在它们的套管和长度完全相同时才会成立 (尾随空格不会被忽略)。它不受 SET ANSI,EXACT 或 COLLATE 设置的影响。