Oracle數據庫查詢之單表查詢的關鍵子句及其用法
前言
在 Oracle 數據庫操作中,查詢數據是最頻繁、最核心的操作之一。單表查詢,即僅從一個表中檢索信息,是所有復雜查詢的基礎。本筆記將系統(tǒng)梳理單表查詢的關鍵子句及其用法,并特別介紹Oracle中偽列的使用。
思維導圖


一、SELECT 語句基本結構
一個完整的單表查詢語句通常包含以下按執(zhí)行順序排列 (邏輯上) 的子句:
SELECT <select_list> -- 5. 選擇要顯示的列或表達式 FROM <table_name> -- 1. 指定數據來源表 [WHERE <filter_conditions>] -- 2. 行過濾條件 [GROUP BY <group_by_expression>] -- 3. 分組依據 [HAVING <group_filter_conditions>] -- 4. 分組后的過濾條件 [ORDER BY <order_by_expression>]; -- 6. 結果排序
- FROM 子句:最先執(zhí)行,確定查詢的數據源表。
- WHERE 子句:其次執(zhí)行,根據指定條件篩選滿足要求的行。
- GROUP BY 子句:在
WHERE過濾后執(zhí)行,將符合條件的行按一個或多個列的值進行分組。 - HAVING 子句:在
GROUP BY分組后執(zhí)行,用于過濾分組后的結果集 (通常與聚合函數配合使用)。 - SELECT 子句:在上述操作完成后,選擇最終要顯示的列、表達式或聚合函數結果。
- ORDER BY 子句:最后執(zhí)行,對最終結果集進行排序。
二、SELECT 子句:選擇列與表達式
- 選擇所有列:
SELECT *
SELECT * FROM employees;
- 選擇特定列:
SELECT column1, column2, ...
SELECT employee_id, first_name, salary FROM employees;
- 使用列別名 (AS): 提高可讀性或避免重名。
SELECT employee_id AS "員工編號", first_name "名", salary "月薪" FROM employees; SELECT salary * 12 AS annual_salary FROM employees;
- 計算列/表達式: 可以在
SELECT中進行算術運算、字符串拼接、函數調用等。
SELECT last_name || ', ' || first_name AS full_name, salary / 30 AS daily_rate FROM employees; SELECT SYSDATE - hire_date AS days_employed FROM employees; SELECT UPPER(first_name) AS upper_first_name FROM employees;
- 去除重復行 (DISTINCT): 只顯示唯一的行組合。
SELECT DISTINCT department_id FROM employees; SELECT DISTINCT department_id, job_id FROM employees;
- 常量值: 可以在查詢結果中包含常量。
SELECT first_name, salary, 'Oracle Corp' AS company_name FROM employees;
三、FROM 子句:指定表
對于單表查詢,FROM 子句非常簡單,就是指定要查詢的那個表名。
FROM employees;
可以為表指定別名,在單表查詢中不常用,但在多表連接或子查詢中非常有用。
FROM employees e;
四、WHERE 子句:行過濾
WHERE 子句用于根據指定的條件篩選出滿足要求的行。
常用比較運算符:= (等于), > (大于), < (小于), >= (大于等于), <= (小于等于), <> 或 != (不等于)。
邏輯運算符:AND (與), OR (或), NOT (非)。
其他常用條件:
BETWEEN ... AND ...: 范圍判斷 (包含邊界值)。
SELECT first_name, salary FROM employees WHERE salary BETWEEN 5000 AND 10000;
IN (value1, value2, ...): 匹配列表中的任何一個值。
SELECT first_name, department_id FROM employees WHERE department_id IN (10, 20, 30);
LIKE: 模糊匹配字符串。%: 匹配任意數量 (包括零個) 的字符。_: 匹配任意單個字符。ESCAPE 'char': 定義轉義字符,用于匹配%或_本身。
SELECT first_name FROM employees WHERE first_name LIKE 'A%'; SELECT last_name FROM employees WHERE last_name LIKE '_o%'; SELECT note FROM notes WHERE note LIKE '100\%%' ESCAPE '\';
IS NULL/IS NOT NULL: 判斷是否為空值。
SELECT first_name, commission_pct FROM employees WHERE commission_pct IS NULL;
代碼案例:查詢薪水大于8000且部門ID為90的員工:
SELECT employee_id, first_name, salary, department_id FROM employees WHERE salary > 8000 AND department_id = 90;
查詢部門ID為10或20,或者職位ID以 ‘SA_’ 開頭的員工:
SELECT employee_id, department_id, job_id FROM employees WHERE department_id IN (10, 20) OR job_id LIKE 'SA\_%';
五、Oracle 偽列 (Pseudocolumns)
Oracle 提供了一些特殊的列,它們不實際存儲在表中,但可以像普通列一樣在SQL語句中引用。這些被稱為偽列。
常用的偽列:
ROWID:- 唯一標識數據庫中每一行的物理地址。
- 它是訪問表中行的最快方式。
ROWID的值看起來像一串十六進制字符。- 雖然唯一,但如果表發(fā)生重組或遷移,行的
ROWID可能會改變。因此,不建議將其作為持久的行標識符。
SELECT ROWID, employee_id, first_name FROM employees WHERE ROWNUM <= 5;
ROWNUM:- 對于查詢返回的每一行,
ROWNUM會按順序分配一個從1開始的數字。 ROWNUM是在數據被檢索出來之后,但在任何ORDER BY子句應用之前分配的。- 常用于限制查詢結果的行數 (分頁查詢的基礎)。
- 重要:不能直接在
WHERE子句中使用ROWNUM > n(n>1) 來獲取第n行之后的數據,因為ROWNUM是逐行分配的。如果第一行不滿足ROWNUM > 1,那么就沒有第二行可以被分配ROWNUM = 2。
- 對于查詢返回的每一行,
-- 獲取前5名員工 (基于默認順序或ORDER BY之前的順序)
SELECT employee_id, first_name, salary FROM employees WHERE ROWNUM <= 5;
-- 錯誤的方式嘗試獲取第6到第10名員工
-- SELECT * FROM employees WHERE ROWNUM > 5 AND ROWNUM <= 10; (通常不會返回任何結果)
-- 正確的分頁方式 (使用子查詢)
SELECT *
FROM (SELECT employee_id, first_name, salary, ROWNUM AS rn
FROM (SELECT employee_id, first_name, salary
FROM employees
ORDER BY salary DESC)) -- 內層先排序
WHERE rn BETWEEN 6 AND 10;
LEVEL:- 與層次查詢 (Hierarchical Queries) 一起使用 (
CONNECT BY子句)。 - 表示當前行在層次結構中的級別。根節(jié)點為
LEVEL 1。
- 與層次查詢 (Hierarchical Queries) 一起使用 (
-- 假設employees表有 manager_id 列,形成層級關系 SELECT LEVEL, employee_id, first_name, manager_id FROM employees START WITH manager_id IS NULL -- 定義根節(jié)點 CONNECT BY PRIOR employee_id = manager_id; -- 定義父子關系
NEXTVAL和CURRVAL(與序列 Sequence 相關):sequence_name.NEXTVAL: 獲取序列的下一個值。每次調用都會使序列遞增。sequence_name.CURRVAL: 獲取序列的當前值 (必須在當前會話中至少調用過一次NEXTVAL之后才能使用)。- 常用于在
INSERT語句中為主鍵列生成唯一值。
-- 假設存在一個名為 employee_seq 的序列 CREATE SEQUENCE employee_seq START WITH 200 INCREMENT BY 1; INSERT INTO employees (employee_id, first_name, last_name, email) VALUES (employee_seq.NEXTVAL, 'New', 'Employee', 'new.emp@example.com'); SELECT employee_seq.CURRVAL FROM dual; -- 查看當前會話中序列的當前值
六、GROUP BY 子句:數據分組
GROUP BY 子句將具有相同值的行組織成一個摘要組。通常與聚合函數 (如 COUNT(), SUM(), AVG(), MAX(), MIN()) 一起使用,對每個組進行計算。
聚合函數: (與之前版本相同)
COUNT(*),COUNT(column_name),COUNT(DISTINCT column_name)SUM(column_name),AVG(column_name)MAX(column_name),MIN(column_name)
使用規(guī)則:
SELECT列表中所有未包含在聚合函數中的列,都必須出現(xiàn)在GROUP BY子句中。WHERE子句先于GROUP BY執(zhí)行;HAVING子句后于GROUP BY執(zhí)行。
代碼案例:查詢每個部門的員工人數:
SELECT department_id, COUNT(*) AS num_employees FROM employees GROUP BY department_id;
七、HAVING 子句:分組過濾
HAVING 子句用于在數據分組后對分組結果進行進一步篩選。它通常包含聚合函數。
代碼案例:查詢平均薪水大于8000的部門:
SELECT department_id, AVG(salary) AS avg_salary FROM employees GROUP BY department_id HAVING AVG(salary) > 8000;
八、ORDER BY 子句:結果排序
ORDER BY 子句用于對最終查詢結果集進行排序。它是查詢語句中邏輯上最后執(zhí)行的部分。
排序方式: (與之前版本相同)
ASC(升序, 默認),DESC(降序)- 多列排序, 列別名排序, 列序號排序 (不推薦)
NULLS FIRST/NULLS LAST
代碼案例:按薪水降序排列員工信息:
SELECT employee_id, first_name, salary FROM employees ORDER BY salary DESC;
總結: 單表查詢是 Oracle SQL 的基石。熟練掌握各子句的功能、用法、執(zhí)行順序,以及偽列 (特別是 ROWNUM 和 ROWID) 的特性,是編寫高效、準確查詢的關鍵。
練習題
背景表:假設我們有一個 products 表,結構如下:
CREATE TABLE products ( product_id NUMBER PRIMARY KEY, product_name VARCHAR2(100) NOT NULL, category_id NUMBER, supplier_id NUMBER, unit_price NUMBER(10,2), units_in_stock NUMBER, discontinued CHAR(1) DEFAULT 'N' -- 'Y' or 'N' ); -- 插入一些樣例數據 (請自行補充更多數據以測試所有題目) INSERT INTO products VALUES (1, 'Chai', 10, 1, 18.00, 39, 'N'); INSERT INTO products VALUES (2, 'Chang', 10, 1, 19.00, 17, 'N'); INSERT INTO products VALUES (3, 'Aniseed Syrup', 20, 1, 10.00, 13, 'N'); INSERT INTO products VALUES (4, 'Chef Anton''s Cajun Seasoning', 20, 2, 22.00, 53, 'N'); INSERT INTO products VALUES (5, 'Chef Anton''s Gumbo Mix', 20, 2, 21.35, 0, 'Y'); INSERT INTO products VALUES (6, 'Grandma''s Boysenberry Spread', 30, 3, 25.00, 120, 'N'); INSERT INTO products VALUES (7, 'Northwoods Cranberry Sauce', 20, 3, 40.00, 6, 'N'); INSERT INTO products VALUES (8, 'Mishi Kobe Niku', 40, 4, 97.00, 29, 'Y'); INSERT INTO products VALUES (9, 'Ikura', 40, 4, 31.00, 31, 'N'); INSERT INTO products VALUES (10, 'Queso Cabrales', 40, 5, 21.00, 22, 'N'); COMMIT;
假設 category_id 10=‘Beverages’, 20=‘Condiments’, 30=‘Confections’, 40=‘Dairy Products’。
請為以下每個場景編寫相應的SQL查詢語句。
題目:
- 查詢
products表中所有產品的ROWID和product_name。 - 查詢
products表中前5條記錄的product_id,product_name,unit_price(基于它們在表中的物理存儲順序,不指定特定排序)。 - 查詢
products表中按unit_price降序排列后的第3到第5條產品記錄的product_name和unit_price。 - 查詢每個
category_id下有多少種產品,并為每個類別結果行分配一個行號 (基于category_id的默認分組順序)。 - 查詢所有
category_id為 20 (Condiments) 的產品名稱和庫存量 (units_in_stock),并給product_name列起別名為 “調味品名稱”,units_in_stock列起別名為 “當前庫存”。 - 查詢單價 (
unit_price) 大于等于20且小于50的所有產品信息 (使用BETWEEN或比較運算符均可)。 - 查詢產品名稱 (
product_name) 以 “Chef Anton” 開頭的所有產品ID和產品名稱。 - 統(tǒng)計每個
supplier_id供應的產品中,已停產 (discontinued= ‘Y’) 的產品數量。只顯示供應了已停產產品的供應商ID及其對應的已停產產品數量。 - 查詢所有產品信息,并按
category_id升序排序,在同一類別中再按units_in_stock降序排序,并將庫存量為NULL的產品排在最后。 - (與序列相關,假設已創(chuàng)建序列
product_pk_seq) 使用序列product_pk_seq.NEXTVAL作為product_id,插入一條新產品記錄:product_name=‘New Test Product’, category_id=10, unit_price=15.00, units_in_stock=100。然后查詢該序列的當前值。(只需寫INSERT和查詢序列的語句)
答案與解析:
- 查詢 ROWID 和 product_name:
SELECT ROWID, product_name FROM products;
- 解析:
ROWID是一個偽列,可以直接在SELECT列表中引用。
- 查詢前5條記錄 (基于物理順序):
SELECT product_id, product_name, unit_price FROM products WHERE ROWNUM <= 5;
- 解析:
ROWNUM在WHERE子句中用于限制返回的行數。此時的順序是Oracle獲取數據的自然順序,不保證特定排序。
- 分頁查詢 (排序后取特定范圍):
SELECT product_name, unit_price
FROM (SELECT product_name, unit_price, ROWNUM AS rn
FROM (SELECT product_name, unit_price
FROM products
ORDER BY unit_price DESC))
WHERE rn BETWEEN 3 AND 5;
- 解析: 這是Oracle分頁的標準寫法。最內層查詢先按價格降序排序,中間層查詢?yōu)榕判蚝蟮慕Y果分配
ROWNUM(并賦予別名rn),最外層查詢根據rn篩選出第3到第5條記錄。
- 分組并為組結果分配行號 (分析函數):(嚴格來說,為分組結果分配行號通常使用分析函數如
ROW_NUMBER() OVER(),ROWNUM在GROUP BY之后應用是對聚合后的結果行進行編號)
如果題目意圖是統(tǒng)計后給結果行編號:
SELECT category_id, COUNT(*) AS product_count, ROWNUM AS group_row_num FROM products GROUP BY category_id;
- 解析: 先按
category_id分組并用COUNT(*)統(tǒng)計。然后對這個聚合后的結果集中的每一行分配ROWNUM。
如果意圖是在每個組內部分配行號,則需要分析函數(超出單表查詢基礎范圍,但可作了解):
-- SELECT product_name, category_id, ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY product_name) AS rn_in_category -- FROM products;
- 使用列別名并過濾 (同前):
SELECT product_name AS "調味品名稱", units_in_stock AS "當前庫存" FROM products WHERE category_id = 20;
- 范圍查詢 (多種寫法):使用
BETWEEN AND:
SELECT * FROM products WHERE unit_price BETWEEN 20 AND 49.99;
使用比較運算符:
SELECT * FROM products WHERE unit_price >= 20 AND unit_price < 50;
- 解析:
BETWEEN包含邊界。如果題目是大于等于20且小于50,則用第二種更精確。
- 模糊查詢 (LIKE):
SELECT product_id, product_name FROM products WHERE product_name LIKE 'Chef Anton%';
- 解析:
LIKE 'Chef Anton%'匹配以 “Chef Anton” 開頭的所有字符串。
- 分組統(tǒng)計已停產產品:
SELECT supplier_id, COUNT(*) AS discontinued_product_count FROM products WHERE discontinued = 'Y' GROUP BY supplier_id HAVING COUNT(*) > 0; -- 或者直接不加HAVING,如果沒有已停產的供應商則不會顯示
- 解析: 先用
WHERE篩選出已停產產品,然后按supplier_id分組并用COUNT(*)統(tǒng)計。HAVING COUNT(*) > 0確保只顯示那些確實有已停產產品的供應商。
- 多列排序與NULLS LAST:
SELECT * FROM products ORDER BY category_id ASC, units_in_stock DESC NULLS LAST;
- 解析: 先按
category_id升序,再按units_in_stock降序,NULLS LAST確保units_in_stock為NULL的記錄排在每個類別的最后。
- 使用序列插入并查詢當前值:(假設序列
product_pk_seq已創(chuàng)建:CREATE SEQUENCE product_pk_seq START WITH 11 INCREMENT BY 1;)
INSERT INTO products (product_id, product_name, category_id, unit_price, units_in_stock) VALUES (product_pk_seq.NEXTVAL, 'New Test Product', 10, 15.00, 100); SELECT product_pk_seq.CURRVAL FROM dual;
- 解析:
product_pk_seq.NEXTVAL獲取序列的下一個值并用于插入。product_pk_seq.CURRVAL從dual表查詢當前會話中該序列的當前值 (必須在同一會話中先調用過NEXTVAL)。
總結
到此這篇關于Oracle數據庫查詢之單表查詢的關鍵子句及其用法的文章就介紹到這了,更多相關Oracle單表查詢內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Oracle中的Connect/session和process的區(qū)別及關系介紹
本文將詳細探討下Oracle中的Connect/session和process的區(qū)別及關系,感興趣的你可以參考下,希望可以幫助到你2013-03-03
DBA 在Linux下安裝Oracle Database11g數據庫圖文教程
正在學習Oracle DBA的知識,所以安裝oracle 11個的數據庫用以做測試,如Clone, RMAN, Stream等2014-08-08
Oracle給用戶授權truncatetable的實現(xiàn)方案
這篇文章主要介紹了Oracle給用戶授權truncatetable的實現(xiàn)方案,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-05-05
Oracle查詢中OVER (PARTITION BY ..)用法
這篇文章主要介紹了Oracle查詢中OVER (PARTITION BY ..)用法,內容和代碼大家參考一下。2017-11-11
oracle?11g導入\導出(expdp?impdp)之導入過程
導出需使用SEC.DMP格式,無分號;建立expdir目錄(E:/exp)并確保存在;導入在cmd下執(zhí)行,需sys用戶權限;若需修改TEST用戶密碼,須通過sysdba登錄sys用戶操作2025-09-09

