Clipper 教程:開源 Clipper(s) 使用指南/資料庫操作
讓我們回到維基百科條目 資料庫應用程式。
也存在不同型別的資料庫應用程式。如果你將朋友的電話號碼和地址儲存在文字處理器中,你將擁有所謂的自由格式資料庫(然而,在計算機科學中,類似的表達是矛盾的)——myBase®、askSam®、Personal Knowbase®、MyInfo®、Info Select® 和 GeneralKB® 是一系列專門的自由格式資料庫應用程式,實際上是指 PIM(個人資訊管理器)。現在,文字處理器允許我們搜尋資訊,但其他操作,例如按字母或數字順序對它們進行排序,通常無法由文字處理器自動完成。
嘗試將其儲存到電子表格中怎麼樣?我們可以使用一列用於姓名,一列用於姓氏,一列用於電話號碼,一列用於城市。這個儲存在電子表格中的快速資料庫可以進行搜尋和排序:例如,我們可以按城市和人員姓名按字母順序進行排序。這是一個平面資料庫,http://www2.research.att.com/~gsf/man/man1/cql.html:平面資料庫是由分隔符分隔的欄位的新行終止記錄的序列,並且電子表格顯示了其在資料輸入和報告方面的侷限性(如果你想使用表中的資料在信封上列印地址,電子表格不是一個好工具)。一個例子是 MyDatabase(http://www.pcmag.com/article2/0,2817,760833,00.asp)。
電子表格更適合做會計:如果記賬員的資料儲存在文字處理程式中,他的工作會變得多麼困難?這裡的目的是以某種方式組織我們的資料:所有成本在一個地方,所有收入在另一個地方。
在 1970 年之前,複雜的資料庫使用層次資料庫進行管理(只需要很少的資訊——例如,參見 http://www.extropia.com/tutorials/sql/hierarchical_databases.html 和 http://people.cs.pitt.edu/~chang/156/14hier.html)。層次資料庫的一個例子是 IBM IMS(資訊管理系統,該系統於 20 世紀 60 年代中期開發,用於航空航天行業的應用程式)。它們的實現基於樹,一種層次資料結構。層次資料庫和網路資料庫一起構成了如今所說的傳統資料庫系統。網路資料庫是由數據系統語言會議(CODASYL)作為對程式語言 COBOL 的擴充套件而產生的。網路資料模型基於稱為圖的資料結構。
如今的標準是關係資料庫(RDBMS),它是“一個數據庫,其記錄表之間存在基於公共欄位的關係”。我們將詳細討論它們,但我們將簡要提及第四種方法:面向物件資料庫。這些資料庫儲存物件(與在面向物件程式設計表示式中使用的意義相同)。它們的使用並不多,主要是因為物件比關係資料庫在其表中儲存的簡單欄位更復雜。有關此主題的更多資訊,請訪問 http://www.odbms.org/odmg-standard/。
關於 dBase 的維基百科條目寫道:“dBase 是一種應用程式開發語言和整合導航資料庫管理系統,Ashton-Tate 將其標記為“關係型”,但它不符合 Edgar F. Codd 博士定義的關係模型標準”。Codd 的標準(所謂的 12 條規則,實際上是 13 條,因為編號為“0”的規則實際上存在)非常嚴格,以至於在實踐中,真正的關係資料庫系統甚至不存在,但關鍵是 dBase 以另一種方式訪問資料庫,因此它被認為是導航資料庫(其工作方式模擬關係資料庫)。
http://www.databasedev.co.uk/design_basics.html
由於 dBase 及其的巨大成功,DBF 檔案格式成為行業標準。許多其他資料庫程式都使用它們來儲存資料,例如 Lotus Approach。我們還有許多小程式可以檢視和轉換為其他格式的檔案。這裡有一堆 URL:https://dbfview.com/、http://www.alexnolan.net/software/dbf.htm、https://dbfviewer.com/en/、https://www.dbf2002.com/、http://www.whitetown.com/dbf2sql/(“DBF 到 SQL 轉換器允許您將 dbf 檔案轉換為 SQL 指令碼。個人許可證 29.95 美元”,但請比較 https://www.vlsoftware.net/exportizer/)。它使用如此廣泛,以至於各種語言都提供了用於與其互動的介面,例如
- C++
- https://sourceforge.net/projects/xdb/、http://linux.techass.com/projects/xdb/xbasedocs/xbase_c1.html 和 http://linux.techass.com/projects/xdb/ Xbase(以前稱為 xdb,也以前稱為 xBase)是一組規範、程式、實用程式和 C++ 類庫,用於處理 Xbase 型別的資料檔案和索引。
- Python
- https://sourceforge.net/projects/xbase-py/ 用於管理 dbf 檔案的 Python 介面
- Java
- https://sourceforge.net/projects/xbasej/ xBaseJ - 用於 Java 的 xBase 引擎
- PHP 介面連結
好的,現在我們將瞭解如何按照預期的方式使用 DBF 檔案。
&& it was done this way at the Dot Prompt
&& we can type this interactively in hbrun
CREATE TMPNAMES
USE TMPNAMES
APPEND BLANK
REPLACE FIELD_NAME WITH "NAME"
REPLACE FIELD_TYPE WITH "C"
REPLACE FIELD_LEN WITH 15
APPEND BLANK
REPLACE FIELD_NAME WITH "ADDRESS"
REPLACE FIELD_TYPE WITH "C"
REPLACE FIELD_LEN WITH 30
CLOSE
CREATE NAMES FROM TMPNAMES && https://www.itlnet.net/programming/program/Reference/c53g01c/ngc785e.html
ERASE TMPNAMES.DBF && we get rid of the temporary file
上面的程式碼建立了一個 DBF 檔案,names.dbf,供以下程式碼使用。它將向 DBF 檔案新增一條記錄。它等效於我舊的 PC 指南中的“第一個示例程式”,該程式缺少現代 xBase 中必需的一行。
CLEAR
? "First Sample Program"
SELECT 1
USE NAMES
APPEND BLANK
REPLACE NAME WITH "MIKE BROWN"
REPLACE ADDRESS WITH "ROME STREET, 56"
CLOSE && this line is missing in my PC GUIDE but is needed in a compiled Harbour program
QUIT
CLOSE 命令等效於 dbCloseArea() 函式,該函式關閉工作區:寫入掛起的更新,釋放掛起的鎖。
下面的簡短程式碼執行上一節中兩段程式碼相同的工作(它只生成不同的檔名,namesdb.dbf 而不是 names.dbf)。
local aStruct := { { "NAME", "C", 15, 0 }, ;
{ "ADDRESS", "C", 30, 0 }}
REQUEST DBFCDX
dbCreate( "namesdb", aStruct, "DBFCDX", .t., "NAMESDB" )
&& http://www.fivetechsoft.com/harbour-docs/api.html
USE NAMESDB
NAMESDB->(DbAppend())
NAMESDB->NAME := "MIKE BROWN"
NAMESDB->ADDRESS := "ROME STREET, 56"
此示例使用別名運算子,->。http://www.ousob.com/ng/clguide/ngcf412.php
別名->欄位名 表示法用於訪問已載入但未啟用的資料庫的欄位。別名可以使用工作區編號(例如2->std_id)、工作區別名(例如B->std_id)或資料庫名稱(例如STUDENTS->std_id)指定。
此程式碼的結果是一個名為namesdb.dbf的檔案。有關 DBF 檔案的資訊可以在DBF 檔案結構中找到,http://www.dbf2002.com/dbf-file-format.html,在這裡我們可以找到這個欄位型別列表。
- C – 字元型
- Y – 貨幣型
- N – 數值型
- F – 浮點型
- D – 日期型
- T – 日期時間型
- B – 雙精度型
- I – 整型
- L – 邏輯型
- M – 備註型
- G – 通用型
- C – 字元型(二進位制)
- M – 備註型(二進位制)
- P – 影像型
- + – 自動增量(dBase 7 級)
- O – 雙精度型(dBase 7 級)
- @ – 時間戳(dBase 7 級)
我的 PC 指南展示瞭如何使用數據庫工具DBU建立 .dbf 檔案。此工具的克隆版本包括FiveDBU(帶有原始碼),位於https://code.google.com/archive/p/fivewin-contributions/downloads,DBF Viewer Plus,位於http://www.alexnolan.net/software/dbf.htm,CLUT,位於http://www.scovetta.com/archives/simtelnet/msdos/clipper。Harbour 包含其自身的HbDBU(原始碼位於\hb32\addons\hbdbu)和HbIDE的元件IdeDBU(另外兩個元件是IdeEDITOR和IdeREPORTS)。
從https://code.google.com/archive/p/fivewin-contributions/downloads我們可以獲取 fivedbu_20130930.zip(增強了 ADO 欄位編輯功能的新版 FiveDBU)。它支援 ADO、3 個 RDD(DBFNTX、CBFCDX 和 RDDADS)和 6 種語言 - 選擇“Bases de datos -> Preferencias -> Lenguaje: Inglés”以將其設定為英文。
讓我們看看我們的小檔案目前包含什麼內容。
USE NAMES
LIST DATE(), TIME(), NAME, ADDRESS
上一節中的工作旨在精確地複製我的 PC 指南中提供的資料庫。但是,這樣做也有一些缺點:只有一個 NAME 欄位,此資料庫無法按姓氏對資料進行排序。此外,粗心的使用者可能會將某些人的資料以姓氏開頭插入,而將其他一些資料以名字開頭插入。在設計資料庫時,應注意這些可能性。第一正規化 (http://www.1keydata.com/database-normalization/first-normal-form-1nf.php,http://www.sqa.org.uk/e-learning/SoftDevRDS02CD/page_14.htm) 要求您定義其資訊不能細分為更小部分的欄位。因此,我們應該使用 FIRST_NAME 和 LAST_NAME 欄位,而不是 NAME 欄位。遵守第一正規化,我們的小型資料庫將朝著成為規範化資料庫的方向發展。
資料庫設計是工作的重要組成部分,並且並非總是顯而易見應該如何進行。請參閱https://www.ntu.edu.sg/home/ehchua/programming/sql/relational_database_design.html。
用於輔助資料庫設計的圖形工具是實體關係圖:https://www.lucidchart.com/pages/er-diagrams,https://www.guru99.com/er-diagram-tutorial-dbms.html。
Harbour 包含一個名為 test.dbf 的檔案。在它的目錄中啟動 hbrun 並輸入
use test browse()
此時,我們看到它是一個包含 500 條記錄的表。使用游標鍵移動,完成後,按 Esc 鍵退出此互動式表瀏覽器和編輯器。要獲取名為 Ted 的人的記錄編號,請發出
locate for first="Ted" ? recno()
我們現在可以嘗試使用 hbrun 互動式地解決 ECDL 教材中的一道練習題。ECDL(歐洲計算機駕駛執照,https://www.findcourses.co.uk/inspiration/articles/why-does-ecdl-matter-17580)是一個資格認證,證明某人可以使用計算機,並具備不同程度的熟練程度。多年前當我獲得此資格時,資料庫模組的任務非常非常簡單。它是處理資料庫的第 5 個模組。我買了一本小書,重新閱讀後發現該模組中的第 2 項練習很有啟發性:建立一個表來管理電影庫。我在 ReactOS 0.4.13 下的 Harbour 3.0 中完成了此操作,幾乎以互動方式使用 hbrun - 這給它帶來了愉悅的 dBase 風格。但是,有必要使用一些輔助檔案,因為某些命令在單行中輸入非常困難 - 或不可能。這沒什麼不好,我們將看到如何從提示符執行儲存在檔案中的程式碼片段。
要點 1。開啟電腦並開啟資料庫管理器 - 雖然看起來很荒謬,但我的練習冊中每道題的第一步都是開啟電腦。
C:\videolib>c:\hb30\bin\hbrun
第一個要發出的命令將視窗設定為 25×80 個字元,以便它在螢幕上看起來不錯,並且沒有任何內容超出視野。
.setmode(25,80)
此時,我們應該或多或少看到以下內容(您的螢幕可能不是義大利語)
第一行顯示 hbrun 執行的最後一個命令,setmode(25,80),底行顯示了傳奇的點提示符。
要點 2。建立一個表來管理電影庫,包含以下欄位
- 電影程式碼,
- 電影標題,
- 型別,
- 製作年份,
- 主演,
- 歐元價格。
我們可以發出以下所有命令在點提示符處建立具有此結構的資料庫
.aDbf := {}
.AADD(aDbf, { "MOVIEID", "C", 6, 0 })
.AADD(aDbf, { "TITLE", "C", 30, 0 })
.AADD(aDbf, { "GENRE", "C", 30, 0 })
.AADD(aDbf, { "YEAR", "N", 4, 0 })
.AADD(aDbf, { "LEADACTOR", "C", 30, 0 })
.AADD(aDbf, { "PRICE_EUR", "N", 5, 2 })
.DBCREATE("videolib", aDbf)
或者我們可以將它們儲存在檔案中,例如 creadb.prg,並使用以下命令從命令列呼叫:.do creadb
現在我們可以檢查工作目錄中的內容
.dir
此命令將顯示當前目錄中所有資料庫的名稱。我們應該看到類似以下內容
Database Files # Records Last Update Size videolib.dbf 0 09/15/21 227
並開始使用我們的資料庫
.use videolib
前兩行現在應該顯示為
PP: use videolib
RDD: DBFNTX | Area: 1 | Dbf: VIDEOLIB | Index: | # 1/ 0 o
要點 3。建立表單以輸入資料 - 我們稍後將執行此操作。我們將看到它是如何完成的:@...GET 命令將輸入欄位放置在螢幕上,並允許使用者輸入資料。一個典型的過程如下所示
PROCEDURE EnterMovieData
LOCAL MOVIEID, TITLE, GENRE, YEAR, LEADACTOR, PRICE_EUR
@ 5, 10 SAY "Movie Code:" GET MOVIEID
@ 7, 10 SAY "Movie Title:" GET TITLE
@ 9, 10 SAY "Genre:" GET GENRE
@ 11, 10 SAY "Year of Production:" GET YEAR
@ 13, 10 SAY "Leading Actor:" GET LEADACTOR
@ 15, 10 SAY "Price in Euro:" GET PRICE_EUR
// Save data
INSERT INTO videolib VALUES (MOVIEID, TITLE, GENRE, YEAR, LEADACTOR, PRICE_EUR)
RETURN
要點 4。使用不同的資料填充表,至少包含 10 條記錄。
我們將從下面的 CSV 檔案“movies.txt”中獲取我們的示例資料,使用以下命令
.append from movies delimited
1,"Jurassic Park","azione",1993,"Jeff Goldblum",35.99 2,"Jumanji","avventura",1995,"Robin Williams",8.49 3,"Navigator","fantascienza",1986,"Joey Cramer",11.39 4,"Mortal Kombat","azione",1995,"Christopher Lambert",10 5,"Karate Kid 4","azione",1994,"Hilary Swank",4.9 6,"Ritorno al futuro","fantascienza",1985,"Michael J. Fox",6.95 7,"2001: Odissea nello Spazio","fantascienza",1968,"Keir Dullea",7.9 8,"Il pianeta proibito","fantascienza",1956,"Leslie Nielsen",9.99 9,"Interstellar","fantascienza",2014,"Matthew McConaughey",6.95 10,"Prometheus","fantascienza",2012,"Michael Fassbender",7
我們可以使用以下命令檢查當前資料庫中的資料
.browse()
現在我們可以對示例資料進行一些小的更改。例如,Genre 欄位中的資料為義大利語,但我們希望將其更改為英語。執行替換的命令非常簡單
.replace genre with "science fiction" for genre="fantascienza" .replace genre with "action" for genre="azione" .replace genre with "adventure" for genre="avventura"
以同樣的方式,我們可以將義大利語電影標題翻譯成英語等效項(“Il pianeta proibito”是“The Forbidden Planet”,“2001: Odissea nello spazio”是“2001: A Space Odyssey” - 但這很容易猜到 -,“Ritorno al futuro”是“Back to the Future”)。
movieid 欄位是一個簡單的數字,這很好,但為了使事情變得更有趣,讓我們嘗試使用以下命令更改它
.replace movieid with left(title,4)+right(str(year),2) all
.list movieid, title
此時,我們已經可以嘗試提取 2000 年之前製作的電影列表
.cls ; ? ; ? ; ?; list title, leadactor, year for year < 2000
要檢視如何在點提示符處輸入多個命令,只需用分號分隔它們即可。在這裡,我們還可以看到 dBase 命令是如何構建的
list - a verb title, leadactor, year - an expression list for year < 2000 - a condition
但是,這三個問號是為了騰出一些空間來檢視所有記錄(hbrun 的前三行被其他資訊佔用),這看起來很醜陋。下次我們將使用 setpos()。
現在我們仔細想想,記錄 7 中的 movie code 欄位 Movieid 僅包含數字,而記錄 8 中包含一個空格,這同樣很醜陋。我們可以透過在點提示符處輸入以下命令以互動方式更正它
.browse()
並將這些欄位替換為“Odys68”和“Forb56”。
既然我們正在進行更正,我突然想起我把2001: A Space Odyssey換成了一部更好的電影,因此我們不妨將其刪除
.delete record 7
.list title for deleted()
.pack
現在我們可以再次瀏覽()並手動新增一條新記錄:“Miss86”,“Mission”,“history,drama”,“1986”,“Jeremy Irons”,“6.95”。
要點 5。按電影標題對錶進行排序。
.index on title to titlesort
再次,.browse() 將允許我們確保檔案外觀上的內容確實發生了變化。
要點 6. 查詢資料庫中 2000 年之前製作的電影
我們已經做過這個了,但這次我們在 Title 欄位上啟用了索引。
.cls ; setpos(3,1) ; list title, leadactor, year for year < 2000
我們還能做什麼?我們還可以計算電影的平均年齡(以年為單位)
.average year(date()) - year to yearavg ; ? yearavg
(也許我應該看看最近的電影……),或者看看我們在電影收藏上花了多少錢。
.sum price_eur to totalprice ; ? totalprice
要點 7. 建立一個名為“電影列表”的報表,其中包含按型別和標題排序的電影記錄,幷包含程式碼、型別、標題、歐元價格欄位。
DOS dBase 有自己的報表生成器,Clipper 提供了一個實用程式 RL.EXE 來建立報表和標籤。這些程式建立了一個用於生成報表的格式檔案,在那些古老的時代,報表使用點陣印表機列印在連續表單紙上。但是我們今天如何建立報表呢?如何在傳送到印表機之前在螢幕上預覽它?最明顯的選項可能是建立 PDF 或 HTML 檔案(然後需要一個樣式表才能觀看,否則它會太乏味)。由於我從一開始就使用的是普通的 Harbour 3.0,我們將遵循最簡單的方法:生成一個 HTML 檔案。我們將嘗試一個表格報表(此程式碼需要更正,因為它生成的 HTML 雖然可以解釋,但由於缺少許多結束標籤而無法驗證)
** report.prg
use videolib
index on genre+title to iReport
set printer on
set printer to 'rMovie.html'
?[<html><body>]
?[<h1>],"Movie Report",[</h1>]
?[<table>]
?[<tr><th>Movie Code<th>Genre<th>Title<th>Price in Euro</tr>]
do while .not. eof()
?[<tr>]
?[<td>],movieid
?[<td>],genre
?[<td>],title
?[<td>],price_eur
?[</tr>]
skip
enddo
.do report
這沒什麼好看的,但 CSS 會大大改善它的外觀。即使像這樣的凌亂的 CSS(這也需要清理)
h1 {
display: block;
font-size: 2em;
margin-top: 0.67em;
margin-bottom: 0.67em;
margin-left: 0;
margin-right: 0;
font-weight: bold;
margin: auto;
width: 50%;
border: 3px solid green;
padding: 10px;
text-align: center;
font-family: Arial, Helvetica, sans-serif;
background-color: lightgreen;
}
table, th, td {
border: 1px solid black;
margin: auto;
width: 75%;
border: 3px solid green;
padding: 10px;
}
table.center {
margin-left: auto;
margin-right: auto;
}
th, td {
padding: 10px;
width: 25%;
text-align: center;
}
th {
padding: 10px;
width: 25%;
font-family: Arial, Helvetica, sans-serif;
border: 3px solid blue;
background-color: lightblue;
}
可以讓我們得到一個色彩鮮豔的列印報表,看起來不那麼單調——儘管不專業。
要點 8. 列印“電影列表”報表。
要點 9. 關閉資料庫和程式。在 hbrun 中,這兩件事分別由這兩個命令完成
.use
.quit
在我們的工作目錄中還剩下什麼?.dir *.* 報告如下
iReport ntx 2048 09/15/21 movies txt 605 09/15/21 report prg 414 09/15/21 rMovie htm 1290 09/15/21 styles css 743 09/15/21 titlesor ntx 2048 09/15/21 videolib dbf 1287 09/15/21
註釋 1. 使用 HTML 進行輸出很有趣,但這不是一個好主意,因為報表通常會超過一頁紙,而 HTML+CSS 表格無法正確處理這種多頁輸出。使用 libHaru (http://libharu.org/)(或 extras\hbvpdf)的繫結建立報表的 PDF 以進行列印將是一個更好的選擇。
註釋 2. 這個例子非常基礎。我們可以在不到 20 分鐘的時間內使用點提示符完成幾乎所有操作。ECDL 在過去的幾個五年裡發生了很大變化。現在我正在檢視ECDL 高階資料庫教學大綱 2.0。它看起來不錯,有一些有趣的練習。
讓我們暫時回憶一下過去,那時常用的計算機沒有滑鼠和 GUI。然而,如果我們想為一項任務使用多個表,我們必須使用某種方法來告訴計算機它應該考慮哪些表。例如,讓我們以一個圖書館為例。簡化地說,我們需要使用至少三個表來管理它(一個用於書籍,一個用於客戶,一個用於借閱)。
我們可能會發出這些命令
SELECT 1
USE Books
SELECT 2
USE Customers
SELECT 3
USE Loans
SELECT Books
我們可以將結果視覺化為下圖中的三個視窗。
這裡 SELECT 命令的作用就像單擊視窗以啟用它,而工作區本身看起來像具有編號(工作區編號)和名稱(工作區別名)的視窗。
這是來自 \hb30\tests 的 testdbf.prg 原始碼。應該詳細討論它。這是一段註釋不良的 GPL 程式碼片段。
/*
* $Id: testdbf.prg 1792 1999-11-10 10:17:19Z bcantero $
*/
function main()
local nI, aStruct := { { "CHARACTER", "C", 25, 0 }, ;
{ "NUMERIC", "N", 8, 0 }, ;
{ "DOUBLE", "N", 8, 2 }, ;
{ "DATE", "D", 8, 0 }, ;
{ "LOGICAL", "L", 1, 0 }, ;
{ "MEMO1", "M", 10, 0 }, ;
{ "MEMO2", "M", 10, 0 } }
REQUEST DBFCDX
dbCreate( "testdbf", aStruct, "DBFCDX", .t., "MYALIAS" )
? "[" + MYALIAS->MEMO1 + "]"
? "[" + MYALIAS->MEMO2 + "]"
? "-"
MYALIAS->( dbAppend() )
MYALIAS->MEMO1 := "Hello world!"
MYALIAS->MEMO2 := "Harbour power"
? "[" + MYALIAS->MEMO1 + "]"
? "[" + MYALIAS->MEMO2 + "]"
MYALIAS->( dbAppend() )
MYALIAS->MEMO1 := "This is a test for field MEMO1."
MYALIAS->MEMO2 := "This is a test for field MEMO2."
? "[" + MYALIAS->MEMO1 + "]"
? "[" + MYALIAS->MEMO2 + "]"
MYALIAS->NUMERIC := 90
MYALIAS->DOUBLE := 120.138
? "[" + Str( MYALIAS->DOUBLE ) + "]"
? "[" + Str( MYALIAS->NUMERIC ) + "]"
? ""
? "Press any key..."
InKey( 0 )
? ""
? "Append 50 records with memos..."
for nI := 1 to 50
MYALIAS->( dbAppend() )
MYALIAS->MEMO1 := "This is a very long string. " + ;
"This may seem silly however strings like this are still " + ;
"used. Not by good programmers though, but I've seen " + ;
"stuff like this used for Copyright messages and other " + ;
"long text. What is the point to all of this you'd say. " + ;
"Well I am coming to the point right now, the constant " + ;
"string is limited to 256 characters and this string is " + ;
"a lot bigger. Do you get my drift ? If there is somebody " + ;
"who has read this line upto the very end: Esto es un " + ;
"sombrero grande rid¡culo." + Chr( 13 ) + Chr( 10 ) + ;
"/" + Chr( 13 ) + Chr( 10 ) + "[;-)" + Chr( 13 ) + Chr( 10 )+ ;
"\"
next
MYALIAS->( dbCommit() )
? "Records before ZAP:", MYALIAS->( LastRec() )
? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
Directory( "testdbf.fpt" )[1][2]
MYALIAS->( __dbZap() )
MYALIAS->( dbCommit() )
? "Records after ZAP:", MYALIAS->( LastRec() )
? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
Directory( "testdbf.fpt" )[1][2]
? "Value of fields MEMO1, MEMO2, DOUBLE and NUMERIC:"
? "[" + MYALIAS->MEMO1 + "]"
? "[" + MYALIAS->MEMO2 + "]"
? "[" + Str( MYALIAS->DOUBLE ) + "]"
? "[" + Str( MYALIAS->NUMERIC ) + "]"
? "Press any key..."
InKey( 0 )
dbCloseAll()
dbCreate( "testdbf", aStruct,, .t., "MYALIAS" )
for nI := 1 to 10
MYALIAS->( dbAppend() )
MYALIAS->NUMERIC := nI
? "Adding a record", nI
if nI == 3 .or. nI == 7
MYALIAS->( dbDelete() )
? "Deleting record", nI
endif
next
MYALIAS->( dbCommit() )
? ""
? "With SET DELETED OFF"
? "Press any key..."
InKey( 0 )
MYALIAS->( dbGoTop() )
do while !MYALIAS->( Eof() )
? MYALIAS->NUMERIC
MYALIAS->( dbSkip() )
enddo
SET DELETED ON
? ""
? "With SET DELETED ON"
? "Press any key..."
InKey( 0 )
MYALIAS->( dbGoTop() )
do while !MYALIAS->( Eof() )
? MYALIAS->NUMERIC
MYALIAS->( dbSkip() )
enddo
? ""
? "With SET DELETED ON"
? "and SET FILTER TO MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8"
? "Press any key..."
InKey( 0 )
MYALIAS->( dbSetFilter( { || MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8 }, ;
"MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8" ) )
MYALIAS->( dbGoTop() )
do while !MYALIAS->( Eof() )
? MYALIAS->NUMERIC
MYALIAS->( dbSkip() )
enddo
SET DELETED OFF
? ""
? "With SET DELETED OFF"
? "and SET FILTER TO MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8"
? "Press any key..."
InKey( 0 )
MYALIAS->( dbSetFilter( { || MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8 }, ;
"MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8" ) )
MYALIAS->( dbGoTop() )
do while !MYALIAS->( Eof() )
? MYALIAS->NUMERIC
MYALIAS->( dbSkip() )
enddo
? "dbFilter() => " + dbFilter()
? ""
? "Testing __dbPack()"
? "Records before PACK:", MYALIAS->( LastRec() )
? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
Directory( "testdbf.dbt" )[1][2]
SET FILTER TO
MYALIAS->( __dbPack() )
MYALIAS->( dbCommit() )
? "Records after PACK:", MYALIAS->( LastRec() )
? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
Directory( "testdbf.dbt" )[1][2]
? "Press any key..."
InKey( 0 )
? "Value of fields:"
MYALIAS->( dbGoTop() )
do while !MYALIAS->( Eof() )
? MYALIAS->NUMERIC
MYALIAS->( dbSkip() )
enddo
? ""
? "Open test.dbf and LOCATE FOR TESTDBF->SALARY > 145000"
? "Press any key..."
InKey( 0 )
dbUseArea( ,, "test", "TESTDBF" )
locate for TESTDBF->SALARY > 145000
do while TESTDBF->( Found() )
? TESTDBF->FIRST, TESTDBF->LAST, TESTDBF->SALARY
continue
enddo
? ""
? "LOCATE FOR TESTDBF->MARRIED .AND. TESTDBF->FIRST > 'S'"
? "Press any key..."
InKey( 0 )
dbUseArea( ,, "test", "TESTDBF" )
locate for TESTDBF->MARRIED .AND. TESTDBF->FIRST > 'S'
do while TESTDBF->( Found() )
? TESTDBF->FIRST, TESTDBF->LAST, TESTDBF->MARRIED
continue
enddo
return nil
一個簡單的資料庫輸入掩碼(來自維基百科 Clipper 條目)
USE Customer SHARED NEW
clear
@ 1, 0 SAY "CustNum" GET Customer->CustNum PICT "999999" VALID Customer->CustNum > 0
@ 3, 0 SAY "Contact" GET Customer->Contact VALID !empty(Customer->Contact)
@ 4, 0 SAY "Address" GET Customer->Address
READ
http://harbourlanguage.blogspot.de/2010/06/understanding-harbour-rdd.html
https://searchsqlserver.techtarget.com/definition/ActiveX-Data-Objects http://cch4clipper.blogspot.com/2009/10/using-adordd-with-harbourxharbour.html
這是來自 x2c-base.zip\Tutor.zip 的 CHECKD.PRG。
Simple checkbook entry and edit program
Use the Checkbook file, indexed on Check number
USE CHECKS
DO WHILE .t.
DONE = 'D'
CLEAR
@ 1,0 SAY "New Check (N), Old Check (O), Done (D), List(L)" ;
GET DONE PICT "!"
READ
DO CASE
CASE DONE='L'
LINE = 2
GOTO TOP
DO WHILE .NOT. EOF()
IF LINE=2
CLEAR
@ 0, 1 say "Date Number Name"
@ 1, 1 say " $$$ Clr Catgy Description"
ENDIF
* DATE NUMBER NAME
* AMT CLR CAT DESC
@ LINE , 1 SAY CHECKDATE && MM/DD/YY
@ LINE ,12 SAY CHECKNO && NNNNNNNN
@ LINE ,22 SAY RECNO() picture "(9999)"
@ LINE ,30 SAY CHECKNAME && CHAR 30
@ LINE+1, 3 SAY CHECKAMT picture "99999.99"
@ LINE+1,14 SAY CHECKCLR && X
@ LINE+1,18 SAY CHECKCAT && XX
@ LINE+1,25 SAY CHECKDESC && Char 30
LINE = LINE + 2
IF LINE>22
Wait "Enter a key to continue"
LINE = 2
ENDIF
SKIP
enddo
IF LINE>0
ACCEPT "Enter a key to continue" TO DONE
ENDIF
LOOP
CASE DONE='D'
USE
EXIT
CASE DONE='O'
REQNO = 0
CLEAR
@ 1, 0 SAY "Record Number: " GET REQNO Picture "###"
READ
IF REQNO>RECCOUNT()
? "Check beyond end of file"
WAIT " Press any key"
LOOP
ENDIF
Goto reqno
LDATE = checkdate
LNO = checkno
LDESC = checkdesc
LNAME = checkname
LAMT = checkamt
LCLR = checkclr
LCAT = checkcat
CASE DONE='N'
APPEND BLANK
LDATE = date()
LNO = ' '
LDESC = space(30)
LNAME = space(30)
LAMT = 0.0
LCLR = "N"
LCAT = ' '
OTHERWISE
? CHR(7)
LOOP
ENDCASE
Now enter Check info or edit it
done = 'N'
DO WHILE done='N'
Make date into editable string
EDATE = DTOC(LDATE)
CLEAR
@ 1, 0 SAY "Check no.:" GET LNO
@ 1, 30 SAY "Date:" GET EDATE Pict "99/99/99"
@ 1, 60 SAY "Record number: "+str(RECNO(), 3)
@ 3, 0 SAY "Check to:" GET LDESC
@ 4, 0 SAY "Check Description:" GET LNAME
@ 6, 0 SAY "Amount of Check:" GET LAMT pict "99999.99"
@ 8, 0 SAY "Check Cleared?" GET LCLR
@ 8, 22 SAY "Check Catagory:" GET LCAT
READ
@ 10, 0 Say "All ok (Yes/No/Cancel) ?" get DONE pict "!"
READ
LDATE = CTOD(EDATE)
ENDDO
IF DONE='Y'
REPLACE checkdate WITH LDATE, checkno WITH LNO, ;
checkdesc WITH LDESC, checkname WITH LNAME, ;
checkamt WITH LAMT, checkclr WITH LCLR, ;
checkcat WITH LCAT
ENDIF
ENDDO
? LASTREC()
DELETE RECORD 4
PACK
? LASTREC()
在這段程式碼中,DELETE 命令將第四條記錄標記為要刪除。但檔案沒有被更改,即使是透過 CLOSE 命令也沒有。PACK 命令實際上刪除了標記為要刪除的記錄(並且還做了一些額外的工作)。RECALL 命令刪除了已刪除的標記。如果當前記錄被標記為要刪除,則函式 DELETED() 返回 .T.,否則返回 .F.。
PACK 命令執行實際的資料刪除操作,PACK 要求當前資料庫被獨佔使用。如果在呼叫 PACK 命令時不滿足此條件,CA-Clipper 會生成執行時錯誤。PACK 執行的其他工作是更新其修改的表上的索引(如果有)。
DELETE ALL 和 PACK 命令由一個名為 ZAP 的單個命令執行。
&& This example demonstrates a typical ZAP operation in a network
&& environment:
USE Sales EXCLUSIVE NEW
IF !NETERR()
SET INDEX TO Sales, Branch, Salesman
ZAP
CLOSE Sales
ELSE
? "Zap operation failed"
BREAK
ENDIF
USE Clients NEW
INDEX ON Name TO Clients UNIQUE
假設一個包含以下資料的表
FSTNAME LSTNAME John Doe John Doe John Doe Jane Doe
我們可以使用這段程式碼建立一個小的索引檔案。
SELECT 1
USE ind
? FILE("ind.ntx")
INDEX ON FstName TO ind
? FILE("ind.ntx") // we verify that a NTX file has been created

