我希望在可为空的列上搜索数据库表。有时我要搜索的值本身就是 NULL。由于 Null 等于无,甚至 NULL,说
where MYCOLUMN=SEARCHVALUE
将失败。现在我不得不求助于
where ((MYCOLUMN=SEARCHVALUE) OR (MYCOLUMN is NULL and SEARCHVALUE is NULL))
有没有更简单的说法?
(如果这很重要,我正在使用 Oracle)
你可以做 IsNull 或 NVL 的东西,但这只会让引擎做更多的工作。您将调用函数来进行列转换,然后必须比较结果。
使用你所拥有的
where ((MYCOLUMN=SEARCHVALUE) OR (MYCOLUMN is NULL and SEARCHVALUE is NULL))
@Andy Lester 断言查询的原始形式比使用 NVL 更有效。我决定测试这个断言:
SQL> DECLARE
2 CURSOR B IS
3 SELECT batch_id, equipment_id
4 FROM batch;
5 v_t1 NUMBER;
6 v_t2 NUMBER;
7 v_c1 NUMBER;
8 v_c2 NUMBER;
9 v_b INTEGER;
10 BEGIN
11 -- Form 1 of the where clause
12 v_t1 := dbms_utility.get_time;
13 v_c1 := dbms_utility.get_cpu_time;
14 FOR R IN B LOOP
15 SELECT COUNT(*)
16 INTO v_b
17 FROM batch
18 WHERE equipment_id = R.equipment_id OR (equipment_id IS NULL AND R.equipment_id IS NULL);
19 END LOOP;
20 v_t2 := dbms_utility.get_time;
21 v_c2 := dbms_utility.get_cpu_time;
22 dbms_output.put_line('For clause: WHERE equipment_id = R.equipment_id OR (equipment_id IS NULL AND R.equipment_id IS NULL)');
23 dbms_output.put_line('CPU seconds used: '||(v_c2 - v_c1)/100);
24 dbms_output.put_line('Elapsed time: '||(v_t2 - v_t1)/100);
25
26 -- Form 2 of the where clause
27 v_t1 := dbms_utility.get_time;
28 v_c1 := dbms_utility.get_cpu_time;
29 FOR R IN B LOOP
30 SELECT COUNT(*)
31 INTO v_b
32 FROM batch
33 WHERE NVL(equipment_id,'xxxx') = NVL(R.equipment_id,'xxxx');
34 END LOOP;
35 v_t2 := dbms_utility.get_time;
36 v_c2 := dbms_utility.get_cpu_time;
37 dbms_output.put_line('For clause: WHERE NVL(equipment_id,''xxxx'') = NVL(R.equipment_id,''xxxx'')');
38 dbms_output.put_line('CPU seconds used: '||(v_c2 - v_c1)/100);
39 dbms_output.put_line('Elapsed time: '||(v_t2 - v_t1)/100);
40 END;
41 /
For clause: WHERE equipment_id = R.equipment_id OR (equipment_id IS NULL AND R.equipment_id IS NULL)
CPU seconds used: 84.69
Elapsed time: 84.8
For clause: WHERE NVL(equipment_id,'xxxx') = NVL(R.equipment_id,'xxxx')
CPU seconds used: 124
Elapsed time: 124.01
PL/SQL procedure successfully completed
SQL> select count(*) from batch;
COUNT(*)
----------
20903
SQL>
我有点惊讶地发现安迪是多么正确。做 NVL 解决方案的成本要高出近 50%。因此,即使一段代码可能看起来不像另一段代码那样整洁或优雅,但它的效率可能会显着提高。我多次运行此程序,每次结果几乎相同。向安迪致敬...
在Expert Oracle Database Architecture中,我看到:
WHERE DECODE(MYCOLUMN, SEARCHVALUE, 1) = 1
不知道是不是更简单,但我偶尔用过
WHERE ISNULL(MyColumn, -1) = ISNULL(SearchValue, -1)
将“-1”替换为对列类型有效但也不太可能在数据中实际找到的值。
注意:我使用 MS SQL,而不是 Oracle,所以不确定“ISNULL”是否有效。
使用 NVL 在两边用一些虚拟值替换 null,如下所示:
WHERE NVL(MYCOLUMN,0) = NVL(SEARCHVALUE,0)
另一种选择,从执行查询的角度来看可能是最佳的,并且仅在您进行某种查询生成时才有用,即根据搜索值生成您需要的确切查询。
伪代码如下。
if (SEARCHVALUE IS NULL) {
condition = 'MYCOLUMN IS NULL'
} else {
condition = 'MYCOLUMN=SEARCHVALUE'
}
runQuery(query,condition)
尝试
WHERE NVL(mycolumn,'NULL') = NVL(searchvalue,'NULL')
如果带外值是可能的:
where coalesce(mycolumn, 'out-of-band')
= coalesce(searchvalue, 'out-of-band')
这也可以在 Oracle 中完成。
WHERE MYCOLUMN || 'X' = SEARCHVALUE || 'X'
在某些情况下,它通过 OR 击败了 IS NULL 测试。
我也很惊讶 DECODE 可以让你检查 NULL 和 NULL。
WITH
TEST AS
(
SELECT NULL A FROM DUAL
)
SELECT DECODE (A, NULL, 'NULL IS EQUAL', 'NULL IS NOT EQUAL')
FROM TEST
我认为你所拥有的还可以。你也许可以使用:
where NVL(MYCOLUMN, '') = NVL(SEARCHVALUE, '')
使用驱动报告的 Oracle 功能,我们经常遇到这种情况。我们希望允许用户输入一个值来限制结果或将其留空以返回所有记录。这是我用过的,对我们来说效果很好。
WHERE rte_pending.ltr_rte_id = prte_id
OR ((rte_pending.ltr_rte_id IS NULL OR rte_pending.ltr_rte_id IS NOT NULL)
AND prte_id IS NULL)