達夢6.0試用之SQL篇

yangtingkun發表於2010-07-17

前幾天ITPUB的熊建國主編和我聯絡,希望我能參加國產資料庫達夢的適用活動,並寫幾篇使用感受。本來最近手工的事情比較多,本打算推辭的,不過熊主編再三邀請,而且強調並非是槍手文,只要寫出真實使用感受即可。既然如此,我就本著支援國產資料庫的原則,寫幾篇試用感受。

由於本人唯一熟悉的資料庫就是Oracle,因此所有的對比都是與Oracle資料庫進行對比,在這個過程中,將盡可能避免將對Oracle資料庫的喜愛之情帶進來,爭取站在一個比較公正的位置上來進行評價。

這一篇簡單介紹一下達夢資料庫SQL相關的內容。

 

 

利用上一篇文章搭建的測試環境,首先登陸資料庫:

C:\dmdbms\bin>isql
isql V6.0.2.51-Build(2009.12.23)
SQL>login
server name:localhost
user name:test
password:
port:12345
dm_login time used:176.475(ms)
SQL>

首先測試DML語句,達夢資料庫支援SQL92標準,且相容Oracle8i的語法,下面測試一下:

SQL>INSERT INTO T
2   VALUES (1, 'TEST', SYSDATE);
INSERT INTO T
VALUES (1, 'TEST', SYSDATE)

1 rows affected
time used: 0.343(ms) clock tick:551330.
SQL>INSERT INTO T
2   VALUES (2, 'TEST AGAIN', TO_DATE('2010-3-31', 'YYYY-MM-DD'));
INSERT INTO T
VALUES (2, 'TEST AGAIN', TO_DATE('2010-3-31', 'YYYY-MM-DD'))

1 rows affected
time used: 0.516(ms) clock tick:848580.
SQL>UPDATE T
2   SET NAME = 'NEWNAME'
3   WHERE ID = 2;
UPDATE T
SET NAME = 'NEWNAME'
WHERE ID = 2;

1 rows affected
time used: 0.498(ms) clock tick:818000.
SQL>INSERT INTO T
2   SELECT ROWNUM + ID, NAME, CREATE_DATE
3   FROM T;
INSERT INTO T
SELECT ROWNUM + ID, NAME, CREATE_DATE
FROM T;

2 rows affected
time used: 67.481(ms) clock tick:112655890.
SQL>COMMIT;
COMMIT;

time used: 11.671(ms) clock tick:19334860.
SQL>SELECT * FROM T;
SELECT * FROM T;

id              name            create_date

1       1       TEST    2010-03-31

2       2       NEWNAME 2010-03-31

3       2       TEST    2010-03-31

4       4       NEWNAME 2010-03-31
4 rows got
time used: 0.385(ms) clock tick:631120.
SQL>DELETE T WHERE ID = 3;
DELETE T WHERE ID = 3;

0 rows affected
time used: 0.442(ms) clock tick:721900.
SQL>DELETE T WHERE ID = 4;
DELETE T WHERE ID = 4;

1 rows affected
time used: 0.475(ms) clock tick:780110.
SQL>SELECT * FROM T;
SELECT * FROM T;

id              name            create_date

1       1       TEST    2010-03-31

2       2       NEWNAME 2010-03-31

3       2       TEST    2010-03-31
3 rows got
time used: 0.321(ms) clock tick:520970.

最基本的SELECTINSERTUPDATEDELETE操作沒有問題,甚至連ORACLE語法中的ROWNUM都支援。

ISQL顯示結果中,有一個自動的行號顯示,但是列名顯示的時候沒有將這一位空出來,使得ID列跑到了行號的位置上,比較容易引起誤解。

SQL>MERGE INTO T
2   USING (SELECT ID + 1 ID, NAME, CREATE_DATE FROM T) T1
3   ON (T.ID = T1.ID)
4   WHEN MATCHED THEN UPDATE
5   SET T.NAME = T1.NAME, T.CREATE_DATE = T1.CREATE_DATE
6   WHEN NOT MATCHED THEN INSERT
7   VALUES (T1.ID, T1.NAME, T1.CREATE_DATE);
1: 'INTO'附近有語法錯誤
SQL>SELECT * FROM T
2   START WITH ID = 1
3   CONNECT BY PRIOR ID = ID + 1;
SELECT * FROM T
START WITH ID = 1
CONNECT BY PRIOR ID = ID + 1;

id              name            create_date

1       1       TEST    2010-03-31
1 rows got
time used: 54.497(ms) clock tick:90706490.

Oracle9i新增的MERGE語句果然是不支援的,而Oracle特有的樹形查詢居然是可以的,甚至連LEVEL偽列都是支援的:

SQL>SELECT T.*, LEVEL
2   FROM T
3   START WITH ID = 1
4   CONNECT BY ID = PRIOR ID + 1;
SELECT T.*, LEVEL
FROM T
START WITH ID = 1
CONNECT BY ID = PRIOR ID + 1;

id              name            create_date             LEVEL

1       1       TEST    2010-03-31      1

2       2       NEWNAME 2010-03-31      2

3       2       TEST    2010-03-31      2
3 rows got
time used: 35.683(ms) clock tick:59512460.

下面看看對其他Oracle特有的語法支援的如何:

SQL>SELECT *
2   FROM T, T1
3   WHERE T.ID = T1.ID;
SELECT *
FROM T, T1
WHERE T.ID = T1.ID;

id              name            create_date             ID

1       1       TEST    2010-03-31      1
1 rows got
time used: 0.521(ms) clock tick:855860.
SQL>SELECT *
2   FROM T, T1
3   WHERE T.ID = T1.ID (+);
SELECT *
FROM T, T1
WHERE T.ID = T1.ID (+);

id              name            create_date             ID

1       1       TEST    2010-03-31      1

2       2       NEWNAME 2010-03-31      NULL

3       2       TEST    2010-03-31      NULL
3 rows got
time used: 36.644(ms) clock tick:61195700.

不但普通的連線支援,連Oracle的外連線’+’都是支援的。

SQL>SELECT *
2   FROM
3   (
4       SELECT ROWNUM RN, A.*
5       FROM
6       (
7               SELECT T.*
8               FROM T, T1
9               WHERE T.ID = T1.ID(+)
10              ORDER BY 2, 1
11      ) A
12      WHERE ROWNUM <= 5
13  )
14  WHERE RN > 1;
SELECT *
FROM
(
        SELECT ROWNUM RN, A.*
        FROM
        (
                SELECT T.*
                FROM T, T1
                WHERE T.ID = T1.ID(+)
                ORDER BY 2, 1
        ) A
        WHERE ROWNUM <= 5
)
WHERE RN > 1;

RN              id              name            create_date

1       2       1       TEST    2010-03-31

2       3       2       TEST    2010-03-31
2 rows got
time used: 30.910(ms) clock tick:51615050.

Oracle的標準分頁語句是支援的,查詢子查詢語句是支援的,ORDER BY常量代替列名也是支援的。

SQL>SELECT ROW_NUMBER() OVER(ORDER BY NAME, ID DESC) RN,
2   ID,
3   NAME
4   FROM T;
1: '('附近有語法錯誤

顯然分析函式是不支援的,雖然分析函式在8i就出現了,不過確實屬於Oracle比較特有的技術,達夢不支援分析函式並不意外。

SQL>SELECT NAME, MAX(ID)
2   FROM T
3   GROUP BY NAME
4   HAVING COUNT(*) > 1;
SELECT NAME, MAX(ID)
FROM T
GROUP BY NAME
HAVING COUNT(*) > 1;

NAME

1       TEST    2
1 rows got
time used: 0.583(ms) clock tick:963240.

SQL>SELECT NAME, AVG(ID)
2   FROM T
3   GROUP BY ROLLUP(NAME);
SELECT NAME, AVG(ID)
FROM T
GROUP BY ROLLUP(NAME);

無效的儲存過程名 'ROLLUP' .error code = -1024

顯然聚集函式、GROUP BY語句和HAVING語句都是支援的。但是8i開始OracleGROUP BY提供了ROLLUPCUBE功能,而這顯然是達夢所不支援的,既然ROLLUPCUBE不支援,那麼對應的GROUPINGCUBEROLLUP專用的函式肯定也是不支援的。

SQL>SELECT T.ID, T1.ID ID1
2   FROM T, T1
3   WHERE T.ID = T1.ID (+)
4   ORDER BY 2 NULLS FIRST;
4: 'NULLS'附近有語法錯誤

支援ORDER BY語句,但是OracleNULLS LASTNULLS FIRST語法並不支援。

SQL>SELECT ID, (SELECT ID FROM T1 WHERE T1.ID = T.ID)
2   FROM T;
SELECT ID, (SELECT ID FROM T1 WHERE T1.ID = T.ID)
FROM T;

ID

1       1       1

2       2       NULL

3       2       NULL
3 rows got
time used: 0.556(ms) clock tick:916730.

將子查詢作為查詢列的方式居然也是支援的,這是沒有想到的。

SQL>SELECT DECODE(ID, 1, 'A', 2, 'B', 'C')
2   FROM T;
SELECT DECODE(ID, 1, 'A', 2, 'B', 'C')
FROM T;

 

1       A

2       B

3       B

4       C
4 rows got
time used: 32.734(ms) clock tick:54721350.
SQL>SELECT CASE ID WHEN 1 THEN 'A' WHEN 2 THEN 'B' ELSE 'C' END
2   FROM T;
SELECT CASE ID WHEN 1 THEN 'A' WHEN 2 THEN 'B' ELSE 'C' END
FROM T;

 

1       A

2       B

3       B

4       C
4 rows got
time used: 0.439(ms) clock tick:724740.

達夢還支援DECODE函式和CASE語法。

SQL>SELECT * FROM T;
SELECT * FROM T;

id              name            create_date

1       1       TEST    2010-03-31

2       2       NEWNAME 2010-03-31

3       2       TEST    2010-03-31
3 rows got
time used: 0.385(ms) clock tick:630330.
SQL>DELETE T WHERE ID = 2 AND ROWNUM = 1;
DELETE T WHERE ID = 2 AND ROWNUM = 1;

1 rows affected
time used: 0.611(ms) clock tick:1004030.
SQL>ALTER TABLE T ADD PRIMARY KEY (ID);
ALTER TABLE T ADD PRIMARY KEY (ID);

time used: 14.329(ms) clock tick:23647990.
SQL>ALTER TABLE T1 ADD PRIMARY KEY (ID);
ALTER TABLE T1 ADD PRIMARY KEY (ID);

time used: 12.599(ms) clock tick:20886900.
SQL>UPDATE
2   (SELECT T.ID, T.NAME
3   FROM T, T1
4   WHERE T.ID = T1.ID)
5   SET NAME = 'A';
2: '('附近有語法錯誤

SQL>UPDATE T A
2   SET (NAME, CREATE_DATE) =
3       (SELECT NAME, CREATE_DATE FROM T WHERE ID = A.ID);
3: 'SELECT'附近有語法錯誤

最後測試了一下UPDATE子查詢方式,顯然這也是達夢資料庫所不支援的,不過這種語法在Oracle8i中已經出現了。另一種透過子查詢同時更新多個列的寫法也是不支援的。

SQL>login
server name:localhost
user name:sysdba
password:
port:12345
dm_login time used:43.856(ms)
SQL>select * from dual;
select * from dual;

SYSDUAL_COL

1       1
1 rows got
time used: 40.933(ms) clock tick:68373830.

達夢為了相容Oracle還特意構造了一個DUAL表。雖然欄位名稱以及記錄的值和Oracle並不相同,但是無論是欄位名還是記錄的值都是不重要的,重要的是表中只有一個欄位且只有一條記錄。

簡單總結一下,達夢資料庫在文件中說已經支援Oracle8i的語法,當時看到這種說明心裡不以為然,要知道Oracle有很多語法是SQL92標準所不支援的,比如查詢子查詢、外連線、樹形查詢等等。

但是測試的結果確實出乎意料,進行了眾多的測試,既然只有寥寥幾種寫法是達夢不支援的,其中還要去掉MERGE語句,因為這是9i才引入的新特性。

除了支援SQL92語法,以及一些常見的語法外,達夢還支援下列Oracle特有的語法:包括樹形查詢、外連線、ROWNUM偽列、LEVEL偽列、DUAL表、標準分頁查詢、甚至包括子查詢作為查詢列。

其中達夢對於樹形查詢的支援比較出乎意料,不僅支援LEVEL偽列,還支援CONNECT_IS_LEAFCONNECT_IS_CYCLE偽列,還支援CONNECT BY NOCYCLE語句,以及CONNECT_BY_ROOT操作、SYS_CONNECT_BY_PATH函式。雖然達夢中所有樹形查詢的語法並沒有超過Oracle中的語法,但是這些偽列、函式和操作已經遠遠超過8iOracle的語法了,已經完全可以支援Oracle10g中樹形查詢的語法和功能了。

而在不支援的特性包括分析函式、ROLLUPCUBE相關的GROUP BY語句、ORDER BY語句中的NULLS FIRSTNULLS LAST語句、UPDATEDELETE子查詢語句。其中分析函式不支援再正常不過,這是Oracle提供的一個獨特的強大工具,能在記錄行級進行排序、分割槽等複雜的操作。而GROUP BYROLLUPCUBE的實現應該不算太複雜,不過畢竟這部分更多的是用在資料倉儲中,在OLTP環境下很少可以用到,不支援也不會有多大的影響。而ORDER BY中的NULLS FIRSTNULLS LAST語句不支援就不太應該了,這應該是經常會使用的功能,而且實現起來應該也很容易,懷疑是被遺漏的功能。至於UPDATEDELETE子查詢,這也是Oracle非常獨特的寫法,其中還涉及鍵值保留表的概念,因此不支援也是在情理之中。但是對於根據查詢結果同時更新多個列的情況還是比較常見的,這種寫法不但簡單,而且可以避免子查詢執行多次,這種UPDATE方式還是應該支援的。

從目前看,將8iSQL的語句遷移到達夢資料庫上而不做修改還是可行的,九成以上的功能都已經實現了。不過在Oracle9/10/11版本中,又增加了大量的新的語法,比如MERGEINSERT ALLWITHAS OFMODEL等等,如果達夢的新版本想要相容Oracle新版本中的語法,還是有不少難度的。

最後看看達夢中特有的寫法,說是特有隻是針對Oracle而言,這種語法早在SQLSERVER中就存在了:

SQL>SELECT TOP 2 *
2   FROM T
3   ORDER BY ID;
SELECT TOP 2 *
FROM T
ORDER BY ID;

id              name            create_date

1       1       TEST    2010-03-31

2       2       TEST    2010-03-31
2 rows got
time used: 31.185(ms) clock tick:52131540.
SQL>SELECT * FROM T
2   LIMIT 2 OFFSET 1;
SELECT * FROM T
LIMIT 2 OFFSET 1;

id              name            create_date

1       2       NEWNAME 2010-03-31

2       2       TEST    2010-03-31
2 rows got
time used: 0.469(ms) clock tick:772680.

前一個是獲取前N條記錄的方法,相當於Oracle標準分頁方式,不過語法要比標準分頁簡單得多。後面一個語句是在結果集中獲取指定位置開始的N條記錄。

Oracle中可以透過ROWNUM或分析函式實現同樣的功能,不過語句要複雜一些。

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4227/viewspace-668275/,如需轉載,請註明出處,否則將追究法律責任。

相關文章