Oracle:資料查詢語言-DQL-select
SELECT語句功能
投影操作:結果集是源表中的部分列。
選擇操作:結果集是源表中的部分行。
連線操作:將兩張表裡的行按某種條件組合成一條長長的行放入結果集。
最基本的用法:SELECT ……FROM……
SELECT子句
用於指定欄位名,多個欄位名用逗號隔開,*代表所有列。
SELECT後面可以跟列,字元,表示式,DISTINCT關鍵字(去掉重複行),函式,常量,組標識。完成對資料的處理。
FROM子句
用於指定表名。
例如:SELECT DISTINCT NAME FROM TEST;
WHERE子句
根據條件過濾出需要處理的資料,對行過濾。後面跟條件表示式(列名,常量,比較運算子,文字值,不能跟列別名),可以用AND,OR連線多個條件表示式。
語法順序:SELECT……FROM……WHERE……
例如:SELECT NAME FROM TEST WHERE ID = 10001;
ORDER BY子句
SELECT語句的最後一個子句,也是最後執行的子句,功能是改變記錄的輸出順序,排序。
ASC(預設/預設) 升序,DESC降序。
ORDER BY後面可以跟列名,表示式,列別名,列在結果集中的位置值。
ORDER BY 後面可以跟多列,用逗號隔開。
NULL值在ORDER BY 子句中排序的順序是:降序排在最上,升序排在最下。
語法順序:SELECT ——> FROM ——> WHERE ——> ORDER BY
執行順序:FROM ——> WHERE ——> SELECT ——> ORDER BY
例如:SELECT * FROM TEST ORDER BY NAME DESC;
GROUP BY子句
功能是根據指定的列對行進行分組
語法順序:SELECT……FROM……WHERE……GROUP BY……ORDER BY……
執行順序:FROM……WHERE……GROUP BY……SELECT……ORDER BY……
例如:SELECT NAME FROM SERVICE GROUP BY NAME;
注意:
在SELECT語句中,如果沒有GROUP BY子句,若在SELECT子句中有一個組函式,那麼其他都必須是組函式,否則會報錯。
在SELECT語句中,如果有GROUP BY子句,SELECT子句中可跟組函式,或GROUP BY後面的表示式、組標識,其他會報錯。
GROUP BY中包含多列的情況,用逗號隔開,分組的粒度更細,每組的記錄少了,但組多了。
行級資訊和組級資訊不可以同時顯示出來。
HAVING 子句
功能是對組過濾,後面接條件表示式。
語法順序:SELECT……FROM……WHERE……GROUP BY……HAVING……ORDER BY……
執行順序:FROM……WHERE……GROUP BY……HAVING……SELECT……ORDER BY……
WHERE和HAVING 的區別:
1、WHERE過濾的是行(記錄)
2、HAVING過濾的是分組(組標識,每組資料的聚合結果)
3、WHERE後面可以跟任意列名,單行函式,不能跟組函式
4、HAVING後面只能跟組函式、組標識、GROUP BY後面的表示式
5、WHERE子句執行在前,HAVING子句執行在後
6、WHERE子句和HAVING子句都不允許有列別名
設定結果集每頁顯示的記錄數
SET PAGESIZE 記錄數;
設定顯示結果集時每行的長度
SET LINESIZE 長度;
設定當前會話的日期格式
ALTER SESSION SET NLS_DATE_FORMAT='格式';
例如:
ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS';
把SELECT出來的結果導到一個文字檔案中
SPOOL 路徑\檔名.TXT;
SELECT * FROM 表名;
SPOOL OFF;
新建兩個表,並插入一些資料:
CREATE TABLE CUSTOMER_TEST(ID VARCHAR2(48),
CODE VARCHAR2(48),
NAME VARCHAR2(58),
TEL VARCHAR2(16),
ADDRESS VARCHAR2(80),
CREATETIME TIMESTAMP DEFAULT SYSDATE,
REMARK VARCHAR2(80));
INSERT INTO CUSTOMER_TEST
(ID, CODE, NAME, TEL, ADDRESS, CREATETIME, REMARK)
VALUES
('C000000000000000000001',
'C000000000000000000001',
'張三',
'15044447777',
'廣東深圳寶安XXXX',
TO_DATE('2015-01-01 09:30:29', 'yyyy-mm-dd hh24:mi:ss'),
'ABCDEFG');
INSERT INTO CUSTOMER_TEST
(ID, CODE, NAME, TEL, ADDRESS, CREATETIME, REMARK)
VALUES
('C000000000000000000002',
'C000000000000000000002',
'李四',
'15122336548',
'廣東深圳寶安XXXX',
TO_DATE('2015-01-01 11:30:29', 'yyyy-mm-dd hh24:mi:ss'),
'ABCDEFG');
INSERT INTO CUSTOMER_TEST
(ID, CODE, NAME, TEL, ADDRESS, CREATETIME, REMARK)
VALUES
('C000000000000000000003',
'C000000000000000000003',
'王五',
'13456892112',
'廣東深圳寶安XXXX',
TO_DATE('2016-02-01 10:30:29', 'yyyy-mm-dd hh24:mi:ss'),
'ABCDEFG');
INSERT INTO CUSTOMER_TEST
(ID, CODE, NAME, TEL, ADDRESS, CREATETIME, REMARK)
VALUES
('C000000000000000000004',
'C000000000000000000004',
'趙六',
'17766522233',
'廣東深圳寶安XXXX',
TO_DATE('2016-02-10 09:30:29', 'yyyy-mm-dd hh24:mi:ss'),
'ABCDEFG');
INSERT INTO CUSTOMER_TEST
(ID, CODE, NAME, TEL, ADDRESS, CREATETIME, REMARK)
VALUES
('C000000000000000000005',
'C000000000000000000005',
'劉七',
'13154546666',
'廣東深圳寶安XXXXX',
TO_DATE('2016-03-01 09:30:29', 'yyyy-mm-dd hh24:mi:ss'),
'ABCDEFG');
COMMIT;
CREATE TABLE ORDER_TEST(ID VARCHAR2(48),
CUSTOMERID VARCHAR2(48),
COMMODITYIDS VARCHAR2(800),
ORDERNUMBER VARCHAR2(48),
ORDERTIME TIMESTAMP DEFAULT SYSDATE,
REMARK VARCHAR2(80),
DELIVERYADDRESS VARCHAR2(80));
INSERT INTO ORDER_TEST
(ID, CUSTOMERID, COMMODITYIDS, ORDERNUMBER, ORDERTIME, REMARK, DELIVERYADDRESS)
VALUES
('O000000000000000000001',
'C000000000000000000001',
'C001,C002,C003,C004,C005',
'O000000000000000000001',
TO_DATE('2016-01-01 17:09:50', 'yyyy-mm-dd hh24:mi:ss'),
'不要錯了',
'廣東廣州白雲XXXXX');
INSERT INTO ORDER_TEST
(ID, CUSTOMERID, COMMODITYIDS, ORDERNUMBER, ORDERTIME, REMARK, DELIVERYADDRESS)
VALUES
('O000000000000000000002',
'C000000000000000000001',
'C001,C002,C003,C004,C005',
'O000000000000000000002',
TO_DATE('2016-03-10 17:09:50', 'yyyy-mm-dd hh24:mi:ss'),
'不要錯了',
'廣東廣州白雲XXXXX');
INSERT INTO ORDER_TEST
(ID, CUSTOMERID, COMMODITYIDS, ORDERNUMBER, ORDERTIME, REMARK, DELIVERYADDRESS)
VALUES
('O000000000000000000003',
'C000000000000000000002',
'C001,C002,C003,C004,C005',
'O000000000000000000003',
TO_DATE('2016-03-11 17:09:50', 'yyyy-mm-dd hh24:mi:ss'),
'不要錯了',
'廣東東莞虎門XXXXX');
INSERT INTO ORDER_TEST
(ID, CUSTOMERID, COMMODITYIDS, ORDERNUMBER, ORDERTIME, REMARK, DELIVERYADDRESS)
VALUES
('O000000000000000000004',
'C000000000000000000003',
'C001,C002,C003,C004,C005',
'O000000000000000000004',
TO_DATE('2016-03-11 17:09:50', 'yyyy-mm-dd hh24:mi:ss'),
'不要錯了',
'廣東東莞后街XXXXX');
INSERT INTO ORDER_TEST
(ID, CUSTOMERID, COMMODITYIDS, ORDERNUMBER, ORDERTIME, REMARK, DELIVERYADDRESS)
VALUES
('O000000000000000000005',
'C000000000000000000004',
'C001,C002,C003,C004,C005',
'O000000000000000000005',
TO_DATE('2016-02-11 17:09:50', 'yyyy-mm-dd hh24:mi:ss'),
'不要錯了',
'廣東東莞后街XXXXX');
COMMIT;
非關聯子查詢:在查詢過程中內表不需要引用主表中的列
先執行子查詢,子查詢的結果作為條件,再執行主查詢。子查詢只執行一次。
查詢在2016年2月有訂單的客戶:
SQL> SELECT C.NAME
2 FROM CUSTOMER_TEST C
3 WHERE C.ID IN
4 (SELECT O.CUSTOMERID
5 FROM ORDER_TEST O
6 WHERE O.ORDERTIME >=
7 TO_DATE('2016-02-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
8 AND O.ORDERTIME <=
9 TO_DATE('2016-02-29 23:59:59', 'yyyy-mm-dd hh24:mi:ss'));
NAME
-----------------
趙六
關聯子查詢:在子查詢中引用主表中的列
查詢在2016年2月有訂單的客戶:
SQL> SELECT C.NAME
2 FROM CUSTOMER_TEST C
3 WHERE EXISTS (SELECT 1
4 FROM ORDER_TEST O
5 WHERE O.ORDERTIME >=
6 TO_DATE('2016-02-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
7 AND O.ORDERTIME <=
8 TO_DATE('2016-02-29 23:59:59', 'yyyy-mm-dd hh24:mi:ss')
9 AND C.ID = O.CUSTOMERID);
NAME
------------------
趙六
多列子查詢:主查詢的where條件中可以用多個列進行過濾(多列條件表示式)
查詢收貨地址相同,且購買商品相同的客戶:
SQL> SELECT C.NAME
2 FROM CUSTOMER_TEST C
3 WHERE C.ID IN (SELECT O.CUSTOMERID
4 FROM ORDER_TEST O
5 WHERE (O.COMMODITYIDS, O.DELIVERYADDRESS) IN
6 (SELECT O.COMMODITYIDS, O.DELIVERYADDRESS
7 FROM ORDER_TEST O
8 GROUP BY O.COMMODITYIDS, O.DELIVERYADDRESS
9 HAVING COUNT(DISTINCT O.CUSTOMERID) > 1));
NAME
------------
王五
趙六
EXISTS:用於檢查子查詢是否至少會返回一行資料,實際上並不在乎子查詢返回任何具體資料,而是在乎是否有值返回,有則是True否則為False。
查詢在2016年2月有訂單的客戶:
SQL> SELECT C.NAME
2 FROM CUSTOMER_TEST C
3 WHERE EXISTS (SELECT 1
4 FROM ORDER_TEST O
5 WHERE O.ORDERTIME >=
6 TO_DATE('2016-02-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
7 AND O.ORDERTIME <=
8 TO_DATE('2016-02-29 23:59:59', 'yyyy-mm-dd hh24:mi:ss')
9 AND C.ID = O.CUSTOMERID);
NAME
---------------
趙六
NOT EXISTS:與exists相反。
查詢在2016年2月沒有訂單的客戶:
SQL> SELECT C.NAME
2 FROM CUSTOMER_TEST C
3 WHERE NOT EXISTS
4 (SELECT 1
5 FROM ORDER_TEST O
6 WHERE O.ORDERTIME >=
7 TO_DATE('2016-02-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
8 AND O.ORDERTIME <=
9 TO_DATE('2016-02-29 23:59:59', 'yyyy-mm-dd hh24:mi:ss')
10 AND C.ID = O.CUSTOMERID);
NAME
-----------------
王五
張三
李四
劉七
IN與EXISTS的比較
如果子查詢得出的結果集記錄較少,主查詢中的表較大且又有索引時應該用IN。
反之如果外層的主查詢記錄較少,子查詢中的表大,又有索引時使用EXISTS。
其實我們區分IN和EXISTS主要是造成了驅動順序的改變(這是效能變化的關鍵),如果是EXISTS,那麼以外層表為驅動表,先被訪問,如果是IN,那麼先執行子查詢,所以我們會以驅動表的快速返回為目標,那麼就會考慮到索引及結果集的關係了。
表連線查詢
交叉連線(CROSS JOIN)
假設TABLE1中有M條記錄,TABLE2中有N條記錄,那麼交叉連線的結果集為M*N條記錄。該連線產生的結果集稱為笛卡爾集。
SQL> SELECT COUNT(1) FROM CUSTOMER_TEST C, ORDER_TEST O;
COUNT(1)
----------
25
SQL> SELECT COUNT(1) FROM CUSTOMER_TEST C CROSS JOIN ORDER_TEST O;
COUNT(1)
----------
25
內連線(INNER JOIN)
任何一張表裡的記錄一定要在另一張表中找到匹配的記錄,否則不能出現在結果集中。
查詢在2016年2月有訂單的客戶:
SQL> SELECT C.NAME
2 FROM CUSTOMER_TEST C
3 INNER JOIN ORDER_TEST O
4 ON C.ID = O.CUSTOMERID
5 WHERE O.ORDERTIME >=
6 TO_DATE('2016-02-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
7 AND O.ORDERTIME <=
8 TO_DATE('2016-02-29 23:59:59', 'yyyy-mm-dd hh24:mi:ss');
NAME
-------------
趙六
SQL> SELECT C.NAME
2 FROM CUSTOMER_TEST C, ORDER_TEST O
3 WHERE C.ID = O.CUSTOMERID
4 AND O.ORDERTIME >=
5 TO_DATE('2016-02-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
6 AND O.ORDERTIME <=
7 TO_DATE('2016-02-29 23:59:59', 'yyyy-mm-dd hh24:mi:ss');
NAME
--------------
趙六
外連線(LEFT JOIN, RIGHT JOIN, FULL JOIN)
左連線LEFT OUTER JOIN
查詢每個使用者單號:
SQL> SELECT C.NAME, O.ORDERNUMBER
2 FROM CUSTOMER_TEST C
3 LEFT JOIN ORDER_TEST O
4 ON C.ID = O.CUSTOMERID
5 ORDER BY C.NAME;
NAME ORDERNUMBER
----------------------- ------------------------------------------
劉七
張三 O000000000000000000001
張三 O000000000000000000002
李四 O000000000000000000003
王五 O000000000000000000004
趙六 O000000000000000000005
RIGHT OUTER JOIN
查詢每個使用者單號:
SQL> SELECT O.ORDERNUMBER, C.NAME
2 FROM ORDER_TEST O
3 RIGHT JOIN CUSTOMER_TEST C
4 ON C.ID = O.CUSTOMERID
5 ORDER BY C.NAME;
ORDERNUMBER NAME
--------------------------------- -------------
劉七
O000000000000000000001 張三
O000000000000000000002 張三
O000000000000000000003 李四
O000000000000000000004 王五
O000000000000000000005 趙六
FULL OUTER JOIN
FROM後面跟子查詢
在表連線過程中,有時候需要先對其中一個表先進行資料過濾在連線,這就是在FROM後面跟子查詢。
這種情況會先執行子查詢,將子查詢的結果集作為一個表與另一個表進行連線。
查詢在2016年2月有訂單的客戶及其訂單號:
SQL> SELECT C.NAME, O.ORDERNUMBER
2 FROM (SELECT CUSTOMERID, ORDERNUMBER
3 FROM ORDER_TEST
4 WHERE ORDERTIME >=
5 TO_DATE('2016-02-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
6 AND ORDERTIME <=
7 TO_DATE('2016-02-29 23:59:59', 'yyyy-mm-dd hh24:mi:ss')) O
8 JOIN CUSTOMER_TEST C
9 ON C.ID = O.CUSTOMERID;
NAME ORDERNUMBER
-------------- ---------------------------
趙六 O000000000000000000005
結果集運算
UNION/UNION ALL:結果集為兩個查詢結果的並集。UNION會進行對資料進行去重操作;UNION ALL直接將兩個結果集合並在一起,不會去重。
INTERSECT:結果集為兩個查詢結果的交集。
MINUS:結果集屬於第一個查詢結果,但不屬於第二個查詢結果。即為第一個查詢結果集減去與第二個查詢結果集的交集。
集合運算中,要求兩個SELECT語句中列的個數、資料型別是一樣的(即同構的)。
分頁查詢
Oracle的分頁利用ROWNUM
ROWNUM是一個偽列,對查詢返回的行進行編號即行號,從1開始遞增。ORACLE的ROWNUM值是在獲得每行之後才賦予的。
SQL> SELECT ROWNUM, C.NAME FROM CUSTOMER_TEST C;
ROWNUM NAME
---------- -------------------
1 張三
2 李四
3 王五
4 趙六
5 劉七
ROWNUM的產生:
假如有一條查詢語句為SELECT XX,YY FROM TABLE WHERE ZZ > 20 AND ROWNUM < 10。
執行FROM TABLE,從中取出第一條記錄,系統給這條記錄進行ROWNUM編號為1;
執行WHERE子句,判斷記錄是否符合條件表示式,符合則留下,不符合就丟棄;
從TABLE中取出第二條記錄,這條記錄的ROWNUM編號,為前一條符合條件被留下的記錄的ROWNUM + 1;
重複前面的步驟,直到不符合ROWNUM < 10為止。
例如:當前頁碼pageIndex為3,頁容量pageSize為2,查詢當前頁的資料:
SQL> SELECT ROW_.*
2 FROM (SELECT ROWNUM RN, C.NAME FROM CUSTOMER_TEST C WHERE ROWNUM <= 3 * 2) ROW_
3 WHERE ROW_.RN > (3 - 1) * 2;
RN NAME
---------- -----------------
5 劉七
分頁查詢開始的startRowNum = (pageIndex - 1) * pageSize; 結束endRowNum = pageIndex * pageSize + 1。startRowNum與endRowNum都不包含在當前頁中。