我想写一个搜索查询。我有以下情况。
搜索结果有3个条件(ID,NAME,CITY)。 例如:
答案 0 :(得分:3)
典型的方法是这样的:
where (id = v_id or v_id is null) and
(name = v_name or v_name is null) and
(city = c_city or v_city is null)
答案 1 :(得分:0)
Gordon Linoff提供的答案肯定是好的,可行的和直截了当的,但它并没有解决像你这样的情况下的性能问题。
即,您要搜索的表可能非常大并且具有各种索引(例如,在您的情况下,NAME
上的索引和CITY
上的另一个索引)。在这种情况下,简单的静态SQL方法可能会使用一个或另一个索引,并且在未提供其使用的索引的条件的情况下执行效果不佳。 (我知道Oracle 11g引入了自适应游标共享 - 我还没有看到它真的有用。我想相信它可以让我的答案过时,它应该让我的答案过时。但我还没有看到它真的很好用。我欢迎你的评论。)
无论如何,如果你没有11g和/或不想大量依赖自适应光标共享,我认为目前编写此类搜索查询的最佳做法是使用REF CURSOR
。像这样:
-- Create a table to query from
CREATE TABLE matt1 ( id number, name varchar2(30), city varchar2(30) );
CREATE OR REPLACE PACKAGE matt_query_pkg AS
FUNCTION get_results ( p_id NUMBER, p_name VARCHAR2, p_city VARCHAR2 ) RETURN SYS_REFCURSOR;
END matt_query_pkg;
CREATE OR REPLACE PACKAGE BODY matt_query_pkg AS
FUNCTION get_results ( p_id NUMBER, p_name VARCHAR2, p_city VARCHAR2 )RETURN SYS_REFCURSOR IS
l_rc SYS_REFCURSOR;
l_sql VARCHAR2(32000);
BEGIN
l_sql := 'SELECT id, name, city FROM matt1 WHERE 1=1';
if p_id IS NULL THEN
l_sql := l_sql || ' AND (1=1 OR :b_id IS NULL)';
else
l_sql := l_sql || ' AND (id = :b_id)';
end if;
if p_name IS NULL THEN
l_sql := l_sql || ' AND (1=1 OR :b_name IS NULL)';
else
l_sql := l_sql || ' AND (name = :b_name)';
end if;
if p_city IS NULL THEN
l_sql := l_sql || ' AND (1=1 OR :b_city IS NULL)';
else
l_sql := l_sql || ' AND (id = :b_city)';
end if;
dbms_output.put_line('Executing:');
dbms_output.put_line(l_sql);
OPEN l_rc FOR l_sql USING p_id, p_name, p_city;
RETURN l_rc;
END get_results;
END matt_query_pkg;
在仅给出ID
和NAME
条件的示例情况下,它将生成如下SQL:
SELECT id, name, city
FROM matt1
WHERE 1=1
AND (id = :b_id)
AND (name = :b_name)
AND (1=1 OR :b_city IS NULL)
Oracle的优化器将分解出(1=1 OR :b_city IS NULL)
子句(因为它知道它总是true
),留下一个SQL,然后它可以根据给定的条件进行专门优化。
注意:放入(1=1 OR :b_city IS NULL)
子句的目的是保持绑定变量的数量不变,因此您始终可以使用以下命令运行搜索:
OPEN l_rc FOR l_sql USING p_id, p_name, p_city;
如果你没有输入那个(1=1 OR...)
子句,那么对于每个可能的null / not null输入参数组合,你必须有一个不同的OPEN..FOR
语句。
以下是一些测试它的代码,供参考:
-- Test it
insert into matt1 values (1, 'Fred', 'New York');
insert into matt1 values (2, 'Fred', 'Philadelphia');
insert into matt1 values (3, 'John', 'Philadelphia');
insert into matt1 values (4, 'Mark', 'Philadelphia');
insert into matt1 values (5, 'Mark', 'Chicago');
commit;
declare
l_rc SYS_REFCURSOR;
l_id NUMBER;
l_name VARCHAR2(30);
l_city VARCHAR2(30);
begin
l_rc := matt_query_pkg.get_results (NULL, 'Fred', NULL);
loop
fetch l_rc INTO l_id, l_name, l_city;
exit when l_rc%NOTFOUND;
dbms_output.put_line ('Found: ' || l_id || ', ' || l_name || ', ' || l_city);
end loop;
end;
答案 2 :(得分:0)
虽然这是一个相对简单的解决方案相对简单的例子,但我非常犹豫是否赞同尝试构建一个通用SQL语句来处理所有可能性的方法。我已经看到了一些非常复杂的例子,虽然它在功能上起作用,但优化器并没有希望获得良好的基数估计,以及随后的良好执行计划。 SQL本质上不是一种过程语言;而PL / SQL或Java或C#或者是什么。因此,条件很好:)所以,为什么不做这样的事情,(逻辑上)
如果仅指定了ID,则运行此SQL
select * from table where id = v_id;
;
如果指定了ID和Name,则运行此SQL
select * from table
where id = v_id
and name = v_name
;
如果指定了全部三个,则运行此SQL
select * from table
where id = v_id
and name = v_name
and city = v_city
结果是你有三个非常容易优化的SQL语句,而不是一个很难的。
现在论证总是,“但我有一些可查询的字段,有太多的组合”。这是您赚取工资(或咨询费!)的地方。您需要务实并将其分解为查询的“集合”,以便在每个可能的组合的SQL和一个非常复杂的不可优化的SQL之间进行合理的折衷。 我希望这是有道理的。
答案 3 :(得分:-1)
我认为这会奏效:
SELECT *
FROM myTable AS t1
WHERE t1.ID = 123
OR (
t1.ID = 123
AND t1.NAME = 'SAM'
)
OR (
t1.ID = 123
AND t1.NAME = 'SAM'
AND t1.CITY = 'NY'
)