【问题标题】:how to write a search query in SQL如何在 SQL 中编写搜索查询
【发布时间】:2017-03-10 13:54:29
【问题描述】:

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

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

  • 如果用户输入 ID = 123,那么 ID = 123 的所有行都应该 无论姓名和城市,都可以获取
  • 如果用户输入 ID = 123 和 name = 'SAM',然后是 ID = 123 和 name = 'SAM' 的所有行 应该在不考虑城市的情况下获取
  • 如果用户输入 ID = 123,name = 'SAM' 和 city = 'NY',然后是所有行 应该获取匹配项。

【问题讨论】:

  • 添加一些示例表数据和预期结果 - 以及格式化文本。同时向我们展示您当前的查询尝试。
  • @GordonLinoff 老实说,我认为这并不矛盾。如果只输入一个 ID,则返回具有该 ID 的所有行,然后其他 2 行只是缩小搜索结果的范围。
  • 可能duplicate

标签: sql oracle oracle11g


【解决方案1】:

一个典型的方法是这样的:

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)

【讨论】:

  • 这样更准确。但正如第一条评论所说,发帖人应该提供更多信息,包括他们自己的尝试。
【解决方案2】:

Gordon Linoff 提供的答案绝对是好的、可行的和直截了当的,但它并没有解决像你这样的情况下的性能问题。

也就是说,您正在搜索的表可能非常大并且具有各种索引(例如,在您的情况下,NAME 上的索引和CITY 上的另一个索引)。在这种情况下,一个简单的静态 SQL 方法可能会使用一个或另一个索引,并且在未提供它所使用的索引的标准的情况下表现不佳。 (我知道 Oracle 11g 引入了自适应游标共享——虽然我还没有看到它真的很有帮助。我想相信它可能会使我的答案过时,它应该使我的答案过时。但我只是还没有看到它真的很好用。我欢迎 cmets 讨论它)。

无论如何,如果您没有 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;

【讨论】:

    【解决方案3】:

    虽然这是一个相对简单的示例和一个相对简单的解决方案,但我非常犹豫是否支持尝试构建一个通用 SQL 语句来处理所有可能性的方法。我见过一些非常复杂的例子,虽然它在功能上工作,但优化器不希望得到一个好的基数估计,然后是一个好的执行计划。 SQL 本质上不是一种过程语言。而 PL/SQL 或 Java 或 C# 或其他任何东西都是。因此,他们擅长条件:) 那么,为什么不做这样的事情,(逻辑上)

    如果仅指定 ID,则运行此 SQL

    select * from table where id = v_id;
    ;
    

    如果指定了 ID 和名称,则运行此 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 之间做出合理的折衷。 我希望这是有道理的。

    【讨论】:

      【解决方案4】:

      我认为这会起作用:

      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'
                      )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-23
        • 1970-01-01
        • 2016-05-09
        • 1970-01-01
        相关资源
        最近更新 更多