我想編寫一個搜索查詢。我有以下情況。如何在SQL中編寫搜索查詢
有3個條件(ID,NAME,CITY)用於搜索結果。 例如:
- 如果用戶輸入ID = 123,那麼所有與ID的行= 123應該 而不管名稱和城市
- 的如果用戶輸入ID = 123 和名稱=「SAM取出',那麼不管城市如何,都應該獲取ID = 123且名稱='SAM' 的所有行
- 如果用戶輸入ID = 123,name ='SAM'且城市='NY',則全部應該獲取具有確切的 匹配的行。
我想編寫一個搜索查詢。我有以下情況。如何在SQL中編寫搜索查詢
有3個條件(ID,NAME,CITY)用於搜索結果。 例如:
由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...)
子句,對於空/非空輸入參數的每個可能組合,您必須有不同的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;
典型的方法是這樣的:
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)
這更準確。但正如第一條評論所說,海報應該提供更多的信息,包括他們自己的嘗試。 – unleashed
我認爲這將工作:
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的方法聲明來處理所有可能性。我已經看到了一些非常複雜的例子,雖然它在功能上起作用,但優化器並不希望獲得良好的基數估計,並且隨後還有一個很好的執行計劃。 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之間進行合理的折衷。 我希望這是有道理的。
添加一些示例表格數據和預期結果 - 以及格式化文本。同時向我們展示您當前的查詢嘗試。 – jarlh
@GordonLinoff老實說,我不認爲它有矛盾。如果只輸入一個ID,則返回具有該ID的所有行,然後另外兩個只是縮小搜索結果。 – David
可能[重複](http://stackoverflow.com/questions/17681428/issue-with-oracle-bind-variables-not-using-index-properly/) – tbone