如何在 SQL 中编写搜索查询

Posted

技术标签:

【中文标题】如何在 SQL 中编写搜索查询【英文标题】:how to write a search query in 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 【参考方案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'
                )

【讨论】:

以上是关于如何在 SQL 中编写搜索查询的主要内容,如果未能解决你的问题,请参考以下文章

VBA、Access 和 SQL 的新手。我如何编写一个 SQL 查询来搜索我的 AccessDB 中的一个表并在找到它时给我一个 True/False? [复制]

如何在学说中编写联合查询?

如何从程序向 Sql Server 和 MySql 发送查询?

如何编写基于组合框选择的单个 sql 查询?

如何在 SQL 查询中动态跳过没有 If else 的 where 子句?

SQL查询中年龄计算的性能