如何在SQL中编写搜索查询

时间:2017-03-10 13:54:30

标签: sql oracle oracle11g

我想写一个搜索查询。我有以下情况。

搜索结果有3个条件(ID,NAME,CITY)。 例如:

  • 如果用户输入ID = 123,那么ID = 123的所有行都应该 无论名称和城市如何,都可以获取
  • 如果用户输入ID = 123 和name ='SAM',然后ID = 123且name ='SAM'的所有行 无论城市如何都应该取得。
  • 如果用户输入ID = 123,name ='SAM'和city ='NY',然后所有行都是精确的 应该提取匹配。

4 个答案:

答案 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;

在仅给出IDNAME条件的示例情况下,它将生成如下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'
                )