SQL、Linux 指令碼與 Ruby 之比較 ZT

asword發表於2009-06-19
[@more@]

針對 Linux 的 資料庫版本在 1998 年釋出,自那以後,陸續推出了可以在 Linux 上使用的其他 應用產品和中介軟體。隨著 Oracle 堅不可摧 Linux 支援計劃的實行,許多組織將他們的 Oracle 安裝移植到了 Linux 平臺。這些變化導致越來越多的 DBA 需要調查資源(如指令碼),以便管理在 Linux 上執行的
 
  在前面的文章中,我說過,系統管理員與 DBA 之間在職責方面存在一個傳統的差異,但實際情況中,這個差異並不明顯。公司會僱傭一些可以解決資料庫級以及作業系統級問題的員工。至少,安裝 Oracle 資料庫軟體的職責要求 DBA 與底層作業系統進行互動。
 
  即使出現了許多新的指令碼語言,管理員仍然需要使用 shell 指令碼作為可在所有系統上使用的技術。現有指令碼已經開發並在許多
上執行了數年 — 這些指令碼需要維護和升級。Shell 指令碼是一個針對各類 IT 專業人員的非常有用的工具。
 
  在本文中,您將瞭解 Oracle SQL、Linux 作業系統命令和指令碼的一些可轉換的常用概念。特別是,本文將說明如何使用“面向集的”方法看待與需要分組、排序和篩選的資料集相關的問題。這些操作對於瞭解 SQL 的人員來說很熟悉,但在使用 Linux 命令和/或 Ruby 程式語言時,通常也需要這些操作。
 
  我不會嘗試以任何嚴謹的方式討論數學集合論,而是在整個範圍內查詢人類語言中存在的所有模糊邊界(以及典型業務問題)。Oracle SQL 提供了比指令碼更純粹的資料集操作方式:每一列都包含已定義型別的域,並且邏輯資料獨立於底層物理表示進行處理。正常情況下,從查詢返回的資料與 shell 命令的輸出不同。以下內容針對教學目的,即,為了解 SQL 的人員提供他們所熟知的類比,以便在需要時利用他們解決問題的能力在資料庫外部工作。本文將提供一個解決問題的不同角度,並鼓勵您鑽研新的、不熟悉的領域。
 

資料集

  SQL 不是過程語言。相反,它用於檢視、彙總或運算元據集。然而,執行某些 shell 命令會導致輸出被寫入。這些結果也可以解釋為一組資料。它可能不是特別結構化的資料集,但大多數情況下都是。許多不同的 shell 命令會將一組資料作為輸出,但以下示例將使用 ls 命令,大多數使用者應該熟悉這個命令。後續命令將修改給定目錄中的檔案列表。首先是一個簡單的長列表命令:

ls -l	  
最終輸出取決於您的目錄。假設結果如下:

total 60K

-rw-r--r-- 1 root dba 1.7K May 13 09:02 xe_s000_2072.trc

-rw-r--r-- 1 cas dba 929 Apr 28 22:21 xe_smon_3664.trc

-rw-r--r-- 1 cas dba 794 Apr 26 17:19 xe_smon_3676.trc

-rw-r--r-- 1 oracle dba 792 Apr 23 21:52 xe_smon_2120.trc

-rw-r--r-- 1 oracle dba 794 Apr 19 17:37 xe_smon_3364.trc

-rw-r--r-- 1 root dba 5.5K Jun 20 2006 xe_s000_2412.trc

-rw-r--r-- 1 cas dba 790 May 25 2006 xe_smon_540.trc

 

  第一列包含一組目錄許可權。第二列顯示連結數量。第三列指明所有者。第四列表示相關組。第五列指出檔案大小(以位元組為單位)。第六列是檔案建立日期(會導致某些問題;如下所示)。第七列指出檔名稱。

可以使用一個表來容納這些資料:

FILE_PERMISSIONSNUM_LINKSFILE_OWNERFILE_GROUPSIZECREATED_ATFILE_NAME
-rw-r--r--1rootdba1675May 13 09:02xe_s000_2072.trc
-rw-r--r--1rootdba5532Jun 20 2006xe_s000_2412.trc
-rw-r--r--1oracledba792Apr 23 21:52xe_smon_2120.trc
-rw-r--r--1oracledba794Apr 19 17:37xe_smon_3364.trc
-rw-r--r--1casDba929Apr 28 22:21xe_smon_3664.trc
-rw-r--r--1casdba794Apr 26 17:19xe_smon_3676.trc
-rw-r--r--1casdba790May 25 2006xe_smon_540.trc

  後面的示例將構建這個結果的“虛構表”(ls -l 命令的輸出已在上面列出),以說明如何限制、排序和彙總資料。

注意事項

至少對於檢視資料的人來說,這個資料集的結構很明顯。然而,請注意第一行與結構不符:total 60K。這一開始就顯示了方法的侷限性;某些輸出不是結構化資料的一部分。

要整理這組資料,您可以將輸出重定向到一個檔案,隨後可以透過後續命令操作這個檔案。

ls -l > test.txt

然後,您可以使用喜歡的文字編輯器編輯該檔案,並刪除第一行。您可以調整資料,直到滿足您的結構化要求。結構化程度取決於您的目標物件。可讀報表不同於要載入到 Oracle 表中的檔案。

如果這個手動中間步驟不是一個選項(或破壞了您的自動化體驗),請考慮使用 grep -v 選項(本文稍後有述)傳送輸出。該命令用於排除匹配給定模式的行。

您可能還注意到,日期格式不一致。除了以幾個不同的格式顯示之外,也不能進行臨時排序,並且還包含了空格。這再一次展示了資料庫型別化資料的有用性。Oracle 提供了大量函式,用於操作和排序日期。在資料庫中,將日期作為結構化資料通常更易於操作。

如果必須在命令列按日期排序,您可能還需要 -t 或類似選項,以便在傳送給後續命令之前進行此排序。

限制和排序資料

  許多 Linux 命令都包含允許您以有用方式排序資料的選項。例如,如果使用簡單的 ls 命令,您可以指定 -t 選項按時間排序,或指定 -X 選項按副檔名排序。假設您希望只選擇檔案的所有者和名稱,並按所有者排序結果,但 ls 沒有包含允許此類輸出的選項。如果您將資料集置於表中,可以執行以下操作:

  SELECT file_owner, file_name FROM imaginary_table ORDER BY owner;
但是,您可以使用管道運算子將命令的輸出傳送(或重定向)到其他命令,以獲得所需結果。首先將資料限制為上面討論的那兩列:
[root@-server test]# ls -l | awk '{print $3"t"$9}'

root xe_s000_2072.trc
root xe_s000_2412.trc
oracle xe_smon_2120.trc
oracle xe_smon_3364.trc
cas xe_smon_3664.trc
cas xe_smon_3676.trc
cas xe_smon_540.trc
  在本例中,使用了 awk 實用程式篩選資料,以便僅列印所有者和檔名。您可能已經注意到,所有者是虛構表的第三列,因此 $3 表示該列。t 表示將使用一個標籤將資料與下一列分隔開。但是,為什麼要使用 $9? 

  仔細檢視資料後發現,日期資料分為三列。由於使用了空格分隔這三部分,資料確實“看上去像是”三個單獨的列。

  現在,需要將這些結果傳送到其他命令,以進行正確排序。排序實用程式將按字母順序或指定的其他順序列印各行。因此,您將獲得以下所需結果:

[root@-server test]# ls -l | awk '{print $3"t"$9}' | sort

cas xe_smon_3664.trc
cas xe_smon_3676.trc
cas xe_smon_540.trc
oracle xe_smon_2120.trc
oracle xe_smon_3364.trc
root xe_s000_2072.trc
root xe_s000_2412.trc

  現在,假設您只想檢視 oracle 所有的檔案。SQL 語句將使用諸如 WHERE owner='oracle' 的 WHERE 子句。可以使用實用程式的 grep 選項限制資料,方法如下:
[root@linux-server test]# ls -l | awk '{print $3"t"$9}' | sort | grep oracle

oracle xe_smon_2120.trc
oracle xe_smon_3364.trc
  同樣,可以使用 -v 標記將資料限制到非 oracle 所有(或等同於 WHERE owner <> 'oracle')的行:
[root@linux-server test]# ls -l | grep -v oracle

cas xe_smon_3664.trc
cas xe_smon_3676.trc
cas xe_smon_540.trc
root xe_s000_2072.trc
root xe_s000_2412.trc
特殊資料和分組資料

  首先,假設只顯示所有者資料(等效於 SELECT owner FROM imaginary_table ORDER BY 1):

[root@linux-server test]# ls -l | awk '{print $3}' | sort

cas
cas
cas
oracle
oracle
root
root

  您可能希望檢視目錄中檔案的所有者的簡單列表。在 SQL 中,這將表達為: 
SELECT DISTINCT owner FROM imaginary_table ORDER BY 1
  您可以使用 uniq 命令獲得類似結果: 
[root@linux-server test]# ls -l | awk '{print $3}' | sort | uniq

cas
oracle
root
  uniq 運算子具有一個 count 函式,允許您獲得與給定所有者關聯的每個檔案的計數。在 SQL 中,您將編寫以下查詢: 
SELECT count(*), owner FROM imaginary_table GROUP BY owner
  透過為 uniq 新增 -c 選項,您可以獲得以下結果:
[root@linux-server test]# ls -l | awk '{print $3}' | sort | uniq -c
1
3 cas
2 oracle
2 root
  輸出開頭處的 1 是什麼?如果您回頭檢視 ls -l 命令的最初輸出,可以看到有一行的內容是 total 60K。該輸出再一次被命令解釋為不包含任何資料的列 3。如果這不可接受,可以如前所示使用 grep 排除該行:
[root@linux-server test]# ls -l |grep -v total |awk '{print $3}' | sort | uniq -c
3 cas
2 oracle
2 root
  上面的所有示例都使用 ls 命令提供輸出。但是,產生輸出的任何命令都可以透過類似的方式使用。另一個方法是,考慮是否可以將命令的輸出重定向到一個檔案。如果可以,得到的檔案將包含隨後可操作的資料集。 

從 Shell 命令到 Ruby

  為了避免您認為熟悉這種型別的語法會佔用您學習新事物和熱門技術的時間,請考慮這些常用編碼在 Ruby 程式語言中出現的方式。作為 Rails Web 應用程式開發框架的一部分,Ruby 獲得了廣泛的接受。無論如何,它是一種非常有用的通用語言,可用於解決系統管理員和 DBA 所關心的各種問題。

  Oracle 和 SQL 有一個共同的特徵,即,它們都是相對“密集”或“簡潔”的語言。此類語言能夠以簡潔的方式表達極其強大的語句。這就使您能夠建立更加簡潔、更易於維護的程式。

  以下示例可以在互動式 Ruby Shell (irb) 中執行,irb 包含在典型 Ruby 版本中。除了 Ruby 程式語言本身所需的前提條件以外,這些示例不需要任何其他安裝條件。sort、uniq 和 grep 的以前用法反映在 ruby Array 類的方法名稱中。

  在命令列鍵入“irb”,轉到 irb 提示符:

[root@linux-server test]# irb
irb(main):001:0>

  建立陣列物件中包含的值列表: 
myList = ['ruby','sql','ruby','bash','python','perl','java','sql']
=> ["ruby", "sql", "ruby", "bash", "python", "perl", "java", "sql"]
  在執行 irb 時,第二行將顯示計算出的表示式的值。該值列表類似於前面示例中使用的 ls 命令的結果。該值列表將充當將要進行排序和限制的最初列表。 

  現在,可以對該列表進行排序:

irb(main):002:0> myList.sort
=> ["bash", "java", "perl", "python", "ruby", "ruby", "sql", "sql"]

  可以返回一個特殊結果列表: 
irb(main):003:0> myList.uniq
=> ["ruby", "sql", "bash", "python", "perl", "java"]
  甚至還可以使用帶有正規表示式匹配功能的 grep 方法: 
irb(main):004:0> myList.grep(/r/)
=> ["ruby", "ruby", "perl"]
  您還可以透過在 uniq 方法返回的陣列上呼叫 size 方法,來模擬 uniq -c 選項: 
irb(main):005:0> myList.uniq.size
=> 6

SQL 對於在 Oracle 資料庫中運算元據集很有用。Shell 指令碼(包含 Linux 命令)在檔案系統級別工作良好。如果需要在資料庫內部和外部運算元據集,可以執行哪些操作?

  Ruby 可以充當“粘合劑”語言,促進獨立技術或應用程式層之間的通訊。此功能的一個示例是,使用 Ruby 提供資料庫與作業系統之間的互動。考慮 web 應用程式中一個相當普遍的情況:即,資料庫中的一個記錄包含對檔案系統上某個檔案的引用。例如,一個使用者上載了圖片,隨後使用該使用者的 id 將該圖片儲存到檔案系統。如果您需要確定哪個使用者(瞭解具體名稱)上載了檔案,需要使用一個銜接這兩個領域的解決方案。

  除了 Ruby 之外,這些示例還需要一些額外安裝。在執行以下示例之前,還應該安裝 Oracle Client(連同配置好的、到包含 HR 模式的 Oracle 資料庫 [如 Oracle 資料庫快捷版] 的連線)和 OCI8 gem (ruby-oci9)(參見頁面頂部的“”部分)。

建立示例資料

  首先,導航到將在其中建立檔案的目錄,以模擬該情景。您可以在 irb 中逐行輸入以下程式,或者在包含先前建立的檔案的目錄中,將整個清單儲存到一個檔案中,然後在命令列執行它(ruby )。清單中的註釋行前面有一個 # 符號:

# First, enter some preliminary commands to create files that will
# simulate data that was uploaded by users. Load the OCI8 package
# and make a connection to your database:

require 'OCI8'
conn =OCI8.new('hr', 'hr', 'xe')

# Next create a SQL query that will return all of the users
# that have an "a" in their last name.
# This simulates arbitrary user uploaded files:

sql="select employee_id||'_'||upper(last_name)|| '.gif' "
sql+="from employees where upper(last_name) like '%A%'"

# Now we will create the actual (empty) files.
# They will be named using the employee id and last name.
# as specified in the first (and only) field in the query
conn.exec(sql) { |r| File.new(r[0],'w') }

如果您在 irb 中執行了這些示例,可以在提示符下鍵入“exit”,然後檢視已建立的檔案。 

建立檔案系統/資料庫協調報告

  接下來,使用 Ruby 建立一個報告,以列出每個使用者,並在與檔案關聯的使用者的名稱前面包含一個 X。

# Load OCI8
require 'OCI8'

# Make a connection to the Oracle Database containing the HR sample schema
conn =OCI8.new('hr', 'hr', 'xe')

# Create a SQL query
sql="select employee_id||'_'||upper(last_name)|| '.gif',"
sql+=" last_name, first_name from employees order by 2,3"

# Execute the query. Each result is an array of strings representing
# each field in the query. If a file exists on the file system that matches
# the pattern we specified earlier, display an X
conn.exec(sql) do |r|
if File.exists?(r[0])
print 'X '
else
print ' '
end

# Display the last name followed by the first name
puts "#{r[1]}, #{r[2]}"
end

  該報告的結果將寫入標準輸出,應與以下內容類似: 
X Abel, Ellen
X Ande, Sundar
X Atkinson, Mozhe
X Austin, David
X Baer, Hermann
X Baida, Shelli
X Banda, Amit
X Bates, Elizabeth
Bell, Sarah
Bernstein, David
Bissot, Laura
Bloom, Harrison
Bull, Alexis
X Cabrio, Anthony
...
Weiss, Matthew
X Whalen, Jennifer
Zlotkey, Eleni
  注意,(按照預期)應該只列出姓氏中包含“a”並且名稱前包含“X”(表示存在關聯檔案)的使用者。 

  Oracle 提供了許多完全在資料庫中處理該情況的方法。例如,可以使用 BLOB 在資料庫本身內將影像作為二進位制資料。然而,如果該設計不適用,這也有助於使用者瞭解其他一些替代方法。

總結和操作比較

  在下面的表中,每種情況的資料來源不同,但處理方法類似。SQL 資料集是針對資料庫表的查詢結果。Linux 資料集是傳送到所列出命令的某個命令的結果。上面所述的 Ruby 示例的資料集是一個 Ruby 字串陣列。表示式均具有代表性;根據所討論的資料集,特定值將有所不同。

SQLLinux 命令Ruby說明
WHERE col1 = 'value' grep value myArr.grep('value') 限制結果,返回匹配項
WHERE col1 != 'value' grep -v value MyArr.– myArr.grep('value') 限制結果,返回不匹配的項
ORDER BY 1 sort myArr.sort 按序排列結果
SELECT DISTINCT col1 uniq field1 myArr.uniq 從結果中刪除重複項
SELECT COUNT(*) , col1 FROM y GROUP BY x sort | uniq -c myArr.uniq.size 找出在一組結果中存在多少給定結果
SELECT col2, col1 awk '{print $2 "t" $1}' myArr.each{|rec| puts "#{rec[1,2]} #{rec[0,1]}"} 將結果限制為給定結果資料行的特定部分

在某一點,利用 Oracle SQL 的能力執行復雜的資料聚合更加容易。此外,解釋構造良好的 SQL 語句比解釋一系列傳送的 Linux 命令更加簡單。Oracle 提供了許多工具,可用於在以某種方式構建此類資料之後將其匯入。Oracle 外部表或 SqlLoader(sqlldr) 可用於將此類資料快速匯入表中。

  指令碼可用於快速構建某些所需功能的原型。符合給定業務需求的解決方案可以獲得更廣泛的使用,並且需要增強。在處理需要複雜篩選和分析的資料集時,您可以將這些指令碼轉換為 Oracle 資料庫支援的程式,以便可靠地維護和儲存資料。可以立即獲得的最大效益可能就是使用 Oracle SQL 了 — 設計的目的是為了這類操作。

結論

  總之,組織應該儘量使用可重複的流程和設計良好的系統。“藝術家”(通常是程式設計師)可以建立最適合需求的軟體。組織要求或業務需求不可避免地會更改,扮演“執行者”角色的人員應該緊跟潮流,以快速適應不斷變化的環境。執行者(通常是系統管理員或 DBA)可以透過較少的時間和資源來開發解決方案。他們需要快速找出合格的技術以解決問題,並具有可用於滿足技術和業務需求的所有基礎演算法。他們通常沒有奢侈的廣泛流程(程式設計師通常有),但有機會提供獨特的創造性解決方案,這些對於正在執行的操作是不可或缺的。

  如果您熟悉 SQL,可以使用本文中引入的概念來檢視指令碼和命令構造。您已經掌握了多種問題解決策略,可以在其他設定中使用它們。如果您瞭解指令碼並且希望深入探究 SQL,將發現過程方法已經為您提供了某些基本資源,當您深入更加一致、面向集合的思考方法時可以使用這些資源。無論在哪種情況下,您都會發現,在您獲得新技能以滿足緊迫的日常業務和技術需求時,大部分現有知識都可以派上用場。

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

相關文章