2016年4月15日 星期五

SEQUENCE、INDEX (DDL 六)

※SEQUENCE

其他的資料庫有自動流水號的功能,而Oracle在11g(含)之前都沒有,之前都一直用這個Sequence


※增刪改查

CREATE SEQUENCE xxx;
CREATE SEQUENCE ooo START WITH 5;
CREATE SEQUENCE zzz START WITH -5 MINVALUE -2000;
CREATE SEQUENCE yyy INCREMENT BY -2000;
CREATE SEQUENCE aaa MAXVALUE 2000 MINVALUE 1 INCREMENT BY 40 CACHE 48 CYCLE;
    
ALTER SEQUENCE aaa MAXVALUE 2500;
    
DROP SEQUENCE aaa;
    
SELECT xxx.NEXTVAL from dual;
SELECT ooo.NEXTVAL from dual;
SELECT zzz.CURRVAL from dual;
    
SELECT * from USER_SEQUENCES

※設定完用NEXTVAL和CURRVAL,可以得知下一個和目前的sequence是多少,但剛創建完一定要執行過NEXTVAL後,才能執行CURRVAL



※選項

官方文件:創建修改刪除
可以得知創建總共有16種選項,修改有15種(少了創建的START WITH)

以下的預設值沒特別寫就是NOxxx:
START WITH:這個選項ALTER沒有,是設定初始值的,當然不會有
    預設值正數是MINVALUE,負數是MAXVALUE
INCREMENT BY:一次增加多少,正數往上增加;負數往下,預設是1
MAXVALUE/NOMAXVALUE:設定最大值,預設是10的28次方減1
MINVALUE/NOMINVALUE:設定最小值,預設是0,可以設到10的27次方減1
CYCLE/NOCYCLE:要不要循環,不環循到臨界點的時候就會出錯誤訊息
CACHE/NOCACHE:預設是20,用完了再去資料庫要20,最主要是要降低和資料庫字典要值的次數,可以增加效能

以下六個我無法理解:
ORDER/NOORDER
KEEP/NOKEEP
SESSION/GLOBAL:預設是GLOBAL



※注意

設定CACHE容易出現的錯誤:
CREATE SEQUENCE aaa MAXVALUE 2000 MINVALUE -200 INCREMENT BY -40 CACHE 48 CYCLE;

如果沒設定好會出「ORA-04013: 要放到 CACHE 中的數目必須小於一個循環週期」的錯
官方有個公式(CEIL (MAXVALUE - MINVALUE)) / ABS (INCREMENT)
如果CACHE大於這個數字就會報這個錯(等於不會)
CEIL是天花板,就是往上(正數);ABS就是絕對值(絕對是正數的值)
以這個例子就是
(CEIL(2000-(-200))) / ABS(-40)
2200/40 = 55
所以只要CACHE設定超過55就會報這個錯



※流水號

流水號終於在12c這一版增加了


CREATE TABLE t1 (
    id NUMBER GENERATED AS IDENTITY,
    name VARCHAR2(10)
);
    
CREATE TABLE t2 (
    id NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 100 INCREMENT BY 10),
    name VARCHAR2(10)
);

※第一條SQL是不加選項,什麼都用預設的

※要加選項就用第二條SQL



※INDEX

官網連結
主要是加快查詢速度用的

語法:
CREATE [UNIQUE|BITMAP] INDEX INDEX ON 表(欄[,欄...]) [USABLE|UNUSABLE]
最後面什麼都不打,預設是USABLE,表示可以用的意思

使用sys登入後,下「SET AUTOTRACE ON;」(要關掉改OFF即可)

※要注意每次執行完SQL語句都要按一次上面的按鈕才會更新

※如果用sqlplus,還可以看到更詳細的資訊,如時間…等

※執行最後一條SQL後,紅框的FULL會變成ROWID,ROWID是效能最快的;FULL指的是一行一行去掃描

※由於INDEX在工作時,我從來沒用過,不知會出什麼問題,只能提供官網連結(上面有提供了),拉到最下面也有很多範例,還有一個我覺得寫的很好的文章

※如要刪除,就下「DROP INDEX index名稱」

VIEW、SYNONYM (DDL 五)

※VIEW

官網連結
使用View時,在12c預設是沒有權限的
要用有權限的帳號登入後,下「GRANT CREATE VIEW TO 帳號;」

view的功能就是將很複雜的SQL變的很簡單,讓大家使用,所以寫view的SQL都很強,它是連到其他的table,所以其他的table改變,它也會跟著變



※範例

CREATE OR REPLACE NOFORCE VIEW v_xxx
AS
SELECT deptno dno, empno eno, dname, ename 
FROM emp natural join dept;

※一定要打AS,不能打IS

※FORCE/NOFROCE:預設是NOFORCE,所以上面的範例NOFORCE可以不打
FORCE表示連到的表不存在,也要創建view,雖然創建好了,但是還是不能SELECT
可能是多人開發時,有些table還沒創建好,但你負責的部分是要寫view,如果沒這個選項你就沒辦法寫了

※AS下面就是隨個人發揮,寫簡單的也可以,只是沒意義,欄位可以打別名

※OR REPLACE也是選項,有打的話,發現同名稱會覆蓋

※刪除view,就下「DROP VIEW v_xxx;」,沒有PURGE的選項,因為本來就是連別的表



※查詢view

SELECT * FROM tab WHERE TABTYPE = 'VIEW';
SELECT * FROM user_views;

※TAB是所有的TABLE,只是看大概的訊息

※更詳細的就要看USER_VIEWS了



※view的增刪改

增刪改當然是針對原表,而view的增刪改是有限的,這樣看你怎麼樣下SQL了
以一張表為例
正常情況和一般增刪改一樣
可是如果表裡有not null、PK的欄位,但你下SQL並沒有把它SELECT出來,所以新增就沒辦法了,修改當然也不能改成null,刪除如果有FK、PK的關係有是有可能刪不了

多張表的情況也類似這樣,反正它只能儘量增刪改
要以下出來的條件為準,view只是一個連結,而這個連結可以讓你增刪改,不能的話就要看你怎麼下SQL了

可以使用delete,但不能使用truncate



※WITH READ ONLY

CREATE OR REPLACE NOFORCE VIEW view_test
AS
SELECT * FROM EMP WHERE deptno = 30 AND job = 'SALESMAN'
WITH READ ONLY;

※加上這個語句就只能查詢而已



※WITH CHECK OPTION [CONSTRAINT xxx]

CREATE OR REPLACE NOFORCE VIEW view_test
AS
SELECT * FROM EMP WHERE deptno = 30 AND job = 'SALESMAN'
WITH CHECK OPTION;

※加上這個語句就不能修改where的欄位,也不能新增;但可以刪除

※CONSTRAINT是給一個約束名稱,不給系統會幫你產生

※以下這兩個都不能成功
UPDATE view_test SET job = 'xxx' WHERE empno = 7499;
INSERT INTO view_test(empno, ename)values(9999, 'BRUCE');



※SYNONYM

SYNONYM翻譯成代名詞,官網連結,簡單來說就是一個table的別名,這個別名等同原來的table,所以也可以增刪改查,而且原來的表也會更動


使用SYNONYM必須用sys登入才有權限
CREATE [PUBLIC] SYNONYM 代名詞名稱 FOR 帳號.表;
CREATE SYNONYM bruce FOR ooo.EMP;

※因為是用sys創建的,所以select * from bruce,只有sys帳號才看得到,如果想讓大家都看到,就要使用CREATE PUBLIC

※完成後也能在USER_SYNONYMS這張表找到

※刪除就下「DROP SYNONYM 代名詞名稱;」

2016年4月14日 星期四

JSP 的指令元素(taglilb)和指令元素與動作元素的include (JSP 2.x 三)

※指令元素(taglilb)

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="xxx" tagdir="/WEB-INF/tags" %>

※uri和tagdir都是取得tag的地方

※uri是別人寫好而且公認的標籤,如JSTL;而tagdir是自己寫的,裡面寫放tag的路徑
等寫到JSTL就會有感覺了

※prefix是標籤開頭用的,如c:,xxx:



※指令元素與動作元素的include

@page 有個include,屬性只有file,指定要包含的路徑
但後面還會寫動作元素,裡面也有個include,兩個很像,這篇要介紹不同的地方

<jsp:include>是動作元素,屬性變成page,也是指定要包含的路徑,但它還有另外一個屬性flush,是在問說buffer滿的時候要不要清空,預設是false
先準備三個頁面,副檔名分別是txt、html、jsp

※text.txt

<%@ page pageEncoding="UTF-8"%>
<br />我是text<br />
<%=new Date()%><br />



※html.html

<%@ page pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <br />我是HTML<br />
        <%=new Date()%><br />
    </body>
</html>

※txt和html先不用include,看單一的結果:

※可以看出txt編碼沒有用,而html我將全部的編碼刪除,run起來都不會出亂碼



※jsp.jsp

<%@ page pageEncoding="UTF-8" import="java.util.*" isELIgnored="false"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        我是jsp<br />
        <%=new Date()%><br />
    </body>
</html>



※test.jsp

<%@ include file="/text.txt" %><br />
<%@ include file="/html.html" %><br />
<%@ include file="/jsp.jsp" %>
---------------------------------------------------<br />
<jsp:include page="/text.txt" /><br />
<jsp:include page="/html.html" /><br />
<jsp:include page="/jsp.jsp" />

※結果:

※虛線上面是指令元素;下面是動作元素

※指令元素的jsp和html不加編碼會出亂碼;動作元素不會

※指令元素會將全部的內容include才進行編譯;但並不完全正確,如果是這樣的話,那page除了import可以寫多行,其他都不行,如果include進來,以我上面的做法有多行一樣的,那就會500才對,可是結果並沒有,而且編碼設定真的有效果

※動作元素每一支都會先編譯才include進來(可以看變成servlet的目錄,指令元素永遠只有一個;動作元素會有多個,但只有jsp才會轉成servlet)
所以指令元素不管變數加在txt、html、jsp都會錯誤,而動作元素不會

2016年4月10日 星期日

JSP 的指令元素 (page) (JSP 2.x 二)

參考JSP2.3的spec的JSP.1.10,上一篇有提供連結,語法如下
<%@ directive { attr=”value” }* %>

directive 有三種:page、tablib、include



※page

屬性有15種
{ language=”scriptingLanguage”}
{ extends=”className”}
{ import=”importList”}
{ session=”true|false”  }
{ buffer=”none|sizekb” }
{ autoFlush=”true|false”  }
{ isThreadSafe=”true|false”  }
{ info=”info_text” }
{ errorPage=”error_url” }
{ isErrorPage=”true|false” }
{ contentType=”ctinfo”}
{ pageEncoding=”peinfo”}
{ isELIgnored=”true|false” }

2.1(含)後又增加這兩個
{ deferredSyntaxAllowedAsLiteral=”true|false”}
{ trimDirectiveWhitespaces=”true|false”}


※除了import以外,<%@ page %>可以寫很多行,但屬性名不能重覆,否則會出「Page directive must not have multiple occurrences of pageencoding」的錯

※以下用2.3版測試


※contentType、pageEncoding

<%@ page language="java" contentType="application/msword; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <%
            response.setHeader("Content-Disposition", "Attachment; filename=xxx.doc");
        %>
        <table border="1">
            <tr><td>ascii</td></tr>
            <tr><td>氣气気</td></tr>
        </table>
    </body>
</html>

※contentType="text/html; charset=UTF-8"
在tomcat資料夾下的webapps\examples\WEB-INF\web.xml可以複製

※MIME預設就是text/html

※MIME是text/plain就會變成純文字檔,所以一點顏色都沒有,亂打會出現下載視窗

※MIME是application/msword,我用IE11會出錯,但用chrome是ok的,一樣會出現下載視窗,檔名依瀏覽器不同會不一樣,內容編碼吃的是meta的編碼

※如果要控制下載的檔名,可用response那一行,參考這裡

※charset 參考Servlet第二篇



※errorPage、isErrorPage

※test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page errorPage="error.jsp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <% int a = 1 / 0;%>
    </body>
</html>

※指定errorPage後,如果此網頁有錯,就會跳轉,且網址不變



※error.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page isErrorPage="true"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <h1>我錯了</h1>
        <%
            response.setStatus(200);
        %>
        <%=exception.getMessage()%>
    </body>
</html>

※isErrorPage是true,才能使用隱函物件exception

※網頁錯誤後,就會跳轉到error.jsp,多按幾次重整會發現有時候成功,有時候500,所以增加response那一行,200代表ok,都沒問題


還有一種方法是用web.xml的設定方式,在Servlet六的錯誤頁面有寫,但要注意IE有可能會有問題(我用IE11預設不行),將下面的勾取消即可


它預設有勾,它勾起來我反而不易懂好不好
上面還有「如果IE不是預設的網頁瀏覽器,請告訴我」,不想用的可取消


※language

文件寫的好長,我懶的看,反正寫java就對了


※extends

※xxx.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page extends="pkg.JSPDad"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <%System.out.println("name=" + getName()); %>
        <%System.out.println("name2=" + getName2()); %>
        <h1>我跳</h1>
    </body>
</html>

※extends的類別,一定要繼承HttpServlet,不然會出「Unable to compile class for JSP」的錯

※沒加extends那一行時,getName()和getName2()當然不能用,「我跳」當然也有在畫面上
我一加extends那一行,就會跳到JSPDad的doGet方法,所以「我跳」沒看到很合理,但getName()和getName2()在控制台也沒有看到,不知道為什麼?


※JSPDad.java

public class JSPDad extends HttpServlet {
    private String name = "xxx";
    public static String name2 = "ooo";
    // setter/getter...
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("get");
        // resp.getWriter().println("<html><head></head><body><h1>Yeah!</h1></body></html>");
        // req.getRequestDispatcher("/first.jsp").include(req, resp);
        resp.sendRedirect(req.getContextPath() + "/first.jsp");
    }
}

※父類別一定要覆寫doGet方法,覆寫doPost沒用,沒覆寫就會405

※first.jsp自己隨便寫,是可以跳轉的

※如果跳轉又寫xxx.jsp會變成無限迴圈


※import

和java的import一樣,可以寫很多行或者寫一行,用「,」隔開,重覆import也是可以的


※session

可不可以使用session,預設是true
上一篇有說9種隱含物件,它就是其中一種,如果這個選項設false,那這一頁就不能使用session了


※buffer、autoFlush

buffer為設定buffer大小,預設是8kb
autoFlush為buffer滿時,是否自動強制輸出,預設是true

如果超出buffer大小
autoFlush設false,會出「IOException: Error: JSP Buffer overflow」
autoFlush設true,還是可以正常輸出,內容也不會變少

buffer設定成0kb或none
autoFlush設false,會出「jsp.error.page.badCombo」
autoFlush設true,還是可以正常輸出,內容也不會變少


※isThreadSafe

在Servlet2.4已過時了,tomcat5.5、Servlet就是2.4,而JSP是2.0,看tomcat連結
預設是true,表示此jsp是安全的,一次允許多個request
如果是false,則一次只允許一個request

※info

設定訊息,可以透過getServletInfo()取得
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" info="xxx"%>
<html>
    <head></head>
    <body><%=getServletInfo()%></body>
</html>


※isELIgnored

要不要忽略EL,就是長「${}」的東東,以後會講



※以下兩個屬性是2.1(含)才有的

※deferredSyntaxAllowedAsLiteral

是否允許在頁面上有「#{}」,預設不允許,所以一打會出現編譯錯誤(Spring的EL就長這樣)

※官方文件有寫說page的作者說,在web.xml寫如下的設定可以覆蓋
<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <deferred-syntax-allowed-as-literal>false</deferred-syntax-allowed-as-literal>
    </jsp-property-group>
</jsp-config>

※我試的結果根本沒覆蓋


※trimDirectiveWhitespaces

※jsp頁面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page trimDirectiveWhitespaces="false" isELIgnored="false"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
</head>
    
<body>
    <% String s = "s"; %>
    <b>hey man!</b>
    ${user}
    <%=s%>
</body>

※這個要在網頁按右鍵,檢視-->原始檔才看得出來,如下:

※上面的圖是設成false或沒設時,下面的圖是true

※官方文件有寫說page的作者說,在web.xml寫如下的設定可以覆蓋
<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <trim-directive-whitespaces>true</trim-directive-whitespaces>
    </jsp-property-group>
</jsp-config>

※我在web.xml全不寫當然是ok的,但奇怪的是我把這裡改成false,我試的結果還是run的好好的,根本就沒覆蓋啊!

完整性約束的型態 (DDL 三)

參考官網,用法和範例可參考這連結,總共分成六種:
1.NOT NULL:禁止放null

2.UNIQUE:同一欄位只能唯一,但允許放null

3.PRIMARY KEY(PK):NOT NULL + UNIQUE

4.FOREIGN KEY(FK):欄位指定FK後,表示此欄位的值必需是另外一張表的欄位值的其中之一

5.CHECK:符合指定的條件

6.REF:參考到另外一個物件,個人覺得這個比較不像約束,比較像自定型態,很多書寫完整性約束時也沒有提到這個,不過官網有就是了

※NOT NULL只能放在宣告欄位的最後面,其他的除了這種方式,還有一種是為約束命名的寫法,這個是官方說的,可是FK我試的結果只能用約束命名的寫法(而且只能使用CONSTRAINT獨立一行),官網也沒有提供不寫約束名稱的範例

※以下以這個範例來練習,先寫沒有約束名稱的寫法
DROP TABLE xxx PURGE;
CREATE TABLE xxx (
    id number(5) PRIMARY KEY,
    name varchar2(20) UNIQUE,
    color varchar2(10) NOT NULL,
    price number(5,2) DEFAULT 0 CHECK(price BETWEEN 0 AND 1000),
    make_date timestamp DEFAULT sysdate UNIQUE
);
    
    
CREATE TABLE ooo (
    oid number(5) PRIMARY KEY,
    oname varchar2(20) UNIQUE,
    xid number(5),
    CONSTRAINT fk_xid FOREIGN KEY(xid) REFERENCES xxx(id)
);

※DEFAULT一定要在約束前面,如果有DEFAULT,INSERT時可以不塞值


※NOT NULL

INSERT INTO xxx(id, color) VALUES (1, null);
INSERT INTO xxx(id) VALUES (1);

※預設不打就是null,所以這兩條SQL是一樣的

※因為color是null,所以會出「ORA-01400: 無法將 NULL 插入 ("帳號"."XXX"."NAME")」的錯



※UNIQUE

INSERT INTO xxx(id, name, color, price) VALUES (1, 'apple', 'red', 50);
INSERT INTO xxx(id, name, color, price) VALUES (2, 'apple', 'blue', 40);

※第一條SQL新增成功,但第二條的name值一樣,所以會出「ORA-00001: 違反必須為唯一的限制條件」的錯

※null可以重覆,官網有說明



※PRIMARY KEY

一筆記錄的唯一值,所以是NOT NULL + UNIQUE



※CHECK

INSERT INTO xxx(id, name, color, price) VALUES (1, 'apple', 'red', 1002);

※因為price的範圍是0~1000,1002已經超過了,所以會出「ORA-02290: 違反檢查條件 (C##SCOTT.SYS_C0010287)」的錯



※FOREIGN KEY

一定要有兩張表而且FK對應的欄位必需有UNIQUE(所以PK也可以),而且型態也要一樣

只要欄位有FK,那就表示它的值有多筆,對應到另外一個table的欄位值(除非另外一個table的欄位值只有一種)
因為這樣的原因,所以刪除時,預設也必須先刪除子項(FK)的欄位,才能刪除父項(PK)的欄位


INSERT INTO xxx(id, name, color) VALUES (1, 'aaa', 'red');
INSERT INTO xxx(id, name, color) VALUES (2, 'bbb', 'blue');
    
    
INSERT INTO ooo(oid, oname, xid) VALUES (2, 'qoo', 3);

※xxx新增兩條SQL,而ooo的xid是對應到xxx的id,而xxx的id值,全部的值有1和2,所以ooo的xid不塞1或2,就會發生「ORA-02291: 違反完整性限制條件 (C##SCOTT.FK_XID) - 找不到父項索引鍵」的錯

※null是可以的



※REF

-- 1.新增TYPE
DROP TYPE aaa;
CREATE TYPE aaa AS OBJECT (
    p1 varchar2(40),
    p2 varchar(2)
);
    
-- 2.新增Table,並使用REF型態
DROP TABLE bbb PURGE;
CREATE TABLE bbb (
    id number, 
    ref_name REF aaa
);
    
-- 3.新增另一張Table,並引用aaa TYPE,順便塞值
DROP TABLE table_a PURGE;
CREATE TABLE table_a OF aaa;
INSERT INTO table_a VALUES('1', 'a');
INSERT INTO table_a VALUES('2', 'b');
    
-- 4.新增資料
INSERT INTO bbb(id, ref_name)VALUES(1, 
    (SELECT REF(t) FROM table_a t WHERE P1 = '1')
);
INSERT INTO BBB(id, ref_name)VALUES(2, 
    (SELECT REF(t) FROM table_a t WHERE P1 = '2')
);

※主要是2的bbb表,它要使用一個TYPE,所以1才要新增TYPE
4要新增時,不知道怎麼塞值,所以3就引用的aaa TYPE
所以使用REF是不能直接在裡面塞值的,要像3這樣引用才可以

※修改可參考PL/SQL 三十三



※以上的6種約束可以混用,用空格隔開如「NOT NULL UNIQUE」,但不是全部都可以,詳情要看官網的說明,例如PRIMARY KEY UNIQUE就不行




※有約束名稱的寫法

CREATE TABLE xxx (
    id number(5),
    name varchar2(20),
    color varchar2(10) NOT NULL,
    price number(5,2) DEFAULT 0,
    make_date timestamp DEFAULT sysdate UNIQUE,
    
    CONSTRAINT pk_id PRIMARY KEY(id),
    CONSTRAINT uk_name UNIQUE(name),
    CONSTRAINT ck_price CHECK(price BETWEEN 0 AND 1000)
);
    
    
CREATE TABLE xxx (
    id number(5) CONSTRAINT pk_id PRIMARY KEY,
    name varchar2(20) CONSTRAINT uk_name UNIQUE,
    color varchar2(10) NOT NULL,
    price number(5,2) DEFAULT 0 CONSTRAINT ck_price CHECK(price BETWEEN 0 AND 1000),
    make_date timestamp DEFAULT sysdate UNIQUE
);

※PK、UNIQUE、CHECK都可以獨立成一行或宣告欄位的最後面

※獨立一行還可以設定多個欄位,下面會介紹



※PK、FK、UNIQUE多個欄位

DROP TABLE xxx PURGE;
CREATE TABLE xxx (
    id number(5),
    name varchar2(20),
    color varchar2(10) NOT NULL,
    price number(5,2) DEFAULT 0 UNIQUE,
    make_date timestamp DEFAULT sysdate UNIQUE,
    
    CONSTRAINT pk_id_name PRIMARY KEY(id, name),
    CONSTRAINT uk_name UNIQUE(name),
    CONSTRAINT ck_price CHECK(price BETWEEN 0 AND 1000)
);
    
DROP TABLE ooo PURGE;
CREATE TABLE ooo (
    oid number(5),
    oname varchar2(20),
    xid number(5),
    xname varchar2(20),
    
    CONSTRAINT pk_oid_xid PRIMARY KEY(oid, xid),
    CONSTRAINT fk_xid_xname FOREIGN KEY(xid, xname) REFERENCES xxx(id, name),
    CONSTRAINT uk_oname_xid UNIQUE(oname, xid)
);

※主要是看ooo表,而FK對應的表欄位兩個欄位也要有UNIQUE(所以PK也可以)



※新增/刪除約束

-- 1
ALTER TABLE xxx MODIFY(color varchar2(25));
    
-- 2
ALTER TABLE ooo DROP CONSTRAINT fk_xid;
ALTER TABLE xxx DROP CONSTRAINT pk_id;
ALTER TABLE xxx ADD CONSTRAINT pk_id PRIMARY KEY(id);
ALTER TABLE ooo ADD CONSTRAINT fk_xid FOREIGN KEY(xid) REFERENCES xxx(id);
    
-- 3
ALTER TABLE xxx DROP CONSTRAINT ck_price;
ALTER TABLE xxx ADD CONSTRAINT ck_price CHECK(price BETWEEN 500 AND 2000);
    
-- 4
ALTER TABLE xxx DISABLE /*ENABLE*/ CONSTRAINT ck_price;

※1是NOT NULL用的

※2我在官網沒有看到修改約束和修改約束名稱的功能,所以只好先刪除再新增了

※2和3是PK、FK、CHECK

※4是將約束啟用/停用的功能

※因為有這個功能,所以有些地方是先新增表,創建完了才用這種語法加一些限制條件



※FK還有重要的功能,下一篇再寫

2016年4月9日 星期六

ALTER、COMMENT (DDL 二)

這一篇還是DDL,所以ALTER、COMMENT執行完也會將前面沒有commit的進行commit

前一篇是針對表的增刪改,下面是針對表裡的欄位的增刪改
ALTER TABLE <table> ADD <field> <type> [default];-- 增加一個欄位
ALTER TABLE <table> ADD (<field> <type> [default], <field> <type> [default]);-- 增加多個欄位
    
ALTER TABLE <table> DROP COLUMN <field>;-- 刪除一個欄位
ALTER TABLE <table> DROP (<field>, <field>);-- 刪除多個欄位
    
ALTER TABLE <table> MODIFY <field> <type> [default] [not null];-- 修改欄位,不包括欄位重命名
ALTER TABLE <table> RENAME COLUMN <old_field> TO <new_field>;-- 欄位重命名用這個

官網連結

※删除约束可用「ALTER TABLE <table> Drop CONSTRAINT <約束名稱>;
※增加UK:ALTER TABLE <table> ADD CONSTRAINT <約束名稱> UNIQUE(欄位);
※增加PK:ALTER TABLE <table> ADD CONSTRAINT <約束名稱> PRIMARY KEY(欄位);
※增加複合主鍵:前面和「增加PK」一樣,最後是PRIMARY KEY(欄位,欄位...);
※增加FK:ALTER TABLE <table> ADD CONSTRAINT <約束名稱> FOREIGN KEY(欄位) REFERENCES 另一張表名稱(另一張表欄位);


※UNUSED

如果資料太多,會刪的很慢,所以可以先設定成不使用,等到沒什麼人用資料庫時再刪,例如凌晨時再刪

變成不使用的狀態後,SELECT看不到不使用的欄位


ALTER TABLE xxx SET UNUSED COLUMN price; -- 將一個欄位設為不使用
ALTER TABLE xxx SET UNUSED(name); -- 將一個欄位設為不使用
ALTER TABLE xxx SET UNUSED(name, id); -- 將多個欄位設為不使用
ALL_UNUSED_COL_TABS -- 這張表可以得知表裡有幾個不使用欄位
ALTER TABLE xxx DROP UNUSED COLUMNS; -- 刪除不使用的欄位

※官網我沒有看到恢復UNUSED變成使用狀態,所以小心使用

※如果想要恢復還可以用下面要講的VISIBLE



※VISIBLE/INVISIBLE

這個功能是Oracle 12c才有的,可以用V$VERSION這張表,看資料庫版本

在新增時,如果欄位名稱不夠,會編譯錯誤,而這個功能可以讓SELECT、DESC時,看不到這個欄位,新增也不需要用到這個欄位,它會直接給null


ALTER TABLE xxx MODIFY(name invisible);
    
CREATE TABLE xxx (
    id number(5),
    name varchar2(20) INVISIBLE
);

※第一條SQL是讓name欄位看不見,要回復只要改成VISIBLE即可

※也可以像第二條SQL那樣,在新增表時就使用

※雖然看不見欄位,但用下面要講的COMMENT,USER_COL_COMMENTS 這張表就能看到了



※COMMENT

就是為表和欄位增加註解,讓人家知道這個表和欄位是做什麼用的


COMMENT ON TABLE <table> IS 'xxx';
COMMENT ON COLUMN <table>.<field> IS 'ooo';
    
SYS.USER_TAB_COMMENTS -- 看所有表的註解
SYS.USER_COL_COMMENTS -- 看所有欄位的註解

CREATE、DROP、RENAME、TRUNCATE、回收表 (DDL 一)

※DDL

之前講的都是DML(資料處理語言),針對表的增刪改查,做完要commit,其他的session才會看的到
而DDL(資料定義語言),是創建、刪除、修改表,把它想成執行完會自動commit,所以待會講到CREATE、DROP、RENAME、TRUNCATE、FLASHBACK、PURGE(增刪改)時,假設有做update表的動作,但沒有commit,只要一執行DDL,那就會自動commit了,這點要注意


※新增

CREATE TABLE chess (
    id number(5),
    name varchar2(20),
    price number(5,2) DEFAULT 0,
    make_date1 date DEFAULT sysdate,
    make_date2 timestamp,
    pic clob
);

※chess要表名稱,自己想一個,「()」裡最左邊是欄位名稱,自己想名字,右邊為資料型態

官網連結,內鍵的有23種

※常用的就是下面那幾種了
.number:
可用int和float代替,number(5,2),5為全部位數,不包括小數點; 2為小數點位數
所以123.45是ok的;number(3, 0)等同number(3),沒有小數點的意思,上面官網連結的Table 2-2有NUMBER的說明,已經很清楚了

.varchar、varchar2、char:
char(5)就是長度是5,varchar2(5)就是最多長度是5,如果有個值是abc,那char取出來後面會包括兩個空格,varchar和varchar2不會,所以才叫var(變動)
varchar和varchar2目前都一樣,官方推薦用varchar2

.date、timestamp:
date年月日;timestamp年月日時分秒

.clob、blob:
c就是character,所以放的是文字;b就是byte,所以放的是圖片

.nXXX:
如nchar、nvarchar2、nclob,主要是存中文字,因為你輸入長度3,它可能內部會將你輸入的長度乘2或3,看編碼而定

※複製表

CREATE TABLE xxx AS SELECT * FROM emp;
CREATE TABLE xxx AS SELECT * FROM emp WHERE 1=0;

※也就是從emp這張表整個複製一份出來變成xxx

※第二條SQL的where永遠不成立,所以只會複製表結構,裡面沒有內容,算是一種運用吧!

※要注意CONSTRAINT 不複製,所以PK、FK都沒有,過幾篇會說明

※AS一定要打,而且不能打IS



※修改

RENAME xxx TO ooo;

※xxx是舊表名,ooo是新表名,就算有PK、FK也可以改



※刪除

DROP TABLE chess;
DROP TABLE chess PURGE;

※兩種方式都可以,差別在下面的資源回收表有說



※清空表

TRUNCATE TABLE xxx;
DELETE FROM xxx;

※TRUNCATE為DDL;DELETE為DML

※DDL比較快;但DML因為還沒commit,所以多一個反悔的機會



※查看表結構

DESC ooo;

※只能看到欄位名稱、欄位類型、是否接受null



※查看目前帳號的表

SELECT * FROM tab;

※資料字典分為兩類
.動態資料字典:隨著資料庫運行而更新的表,通常以「V$」開頭

.靜態資料字典又分成以下三種:
user_開頭:目前帳號的表
all_開頭:目前帳號可以存取的表
dba_開頭:所有的表,必需有權限



※回收表

就是將表刪除後,類似Windows的資源回收桶,還有還原的機會


DROP TABLE xxx;--1
SELECT * FROM tab;--2
SELECT * FROM recyclebin;--3
FLASHBACK TABLE xxx TO BEFORE DROP;--4

※第一步刪除完後,看第二步的表,會發現TNAME多一個「BIN$」開頭,「==$0」結尾的,表示已經到回收表裡了

※第三步的表就是回收表,從ORIGINAL_NAME也會發現剛剛刪除的xxx,如果有多個,那就表示之前刪除了很多,剛好名稱也是xxx

※如果名稱有多個,它還會幫我們多兩個記錄,
「SYS_IL」開頭,「$$」結尾和「SYS_LOB」開頭,「$$」結尾的
而且CREATETIME時間欄位和刪除的表一樣

※第四步使用這種語法能恢復表,它會恢復最接近目前時間且名稱和ORIGINAL_NAME一樣的(例如有三筆資料:1分鐘前刪的、2分鐘前刪的、3分鐘前刪的,會恢復1分鐘前刪的)

※恢復完成後,第二和第三步的表都會少一行

※如果不想進資源回收表,就使用DROP TABLE xxx PURGE;



※刪除回收表

回收表沒辦法用DELETE和TRUNCATE刪的,要用以下的語法


PURGE TABLE xxx;
PURGE recyclebin;

※第一條SQL是刪除一筆,如果有「SYS_IL」開頭,「$$」結尾和「SYS_LOB」開頭,「$$」結尾的,而且CREATETIME時間欄位和刪除的表一樣的,也會一起刪除
刪除的是離目前時間最遠的那一筆(例如有三筆資料:1分鐘前刪的、2分鐘前刪的、3分鐘前刪的,會刪除3分鐘前刪的)

※一筆一筆刪除太累了,所以第二條SQL是清空整個回收表用的