正則表達式 - SQL 操作
[pol@fedora data]$ lsb_release -a LSB Version: :core-4.1-amd64:core-4.1-noarch Distributor ID: Fedora Description: Fedora release 34 (Thirty Four) Release: 34 Codename: ThirtyFour
我正在嘗試將範例數據庫文件從 MS SQL Server 轉換為 PostgreSQL。
所以,我有兩個我無法解決的小問題。
shipname NVARCHAR(40) NOT NULL,
那是
- (總是)兩個空格
- 標識符(即欄位名稱) - 總是
$$ a-z $$- 小寫字母
- 後跟未知數量的空格
- 後跟 NVARCHAR(xy) NOT NULL或可能後跟 NVARCHAR(xy) NULL
我想把它變成
shipname TEXT NOT NULL CHECK (LENGTH(shipname) <= xy),
或者
shipname TEXT NULL,
到目前為止我所擁有的:
sed 's/^ [a-z]+[ ]+NVARCHAR([0-9]+) NOT NULL/TEXT NOT NULL CHECK \(LENGTH\((\1) <= (\2)\)/g'
所以,
^
是字元串的開頭- 後跟兩個空格
- 後跟我的欄位名
$$ a-z $$+
- 後跟任意編號。空間
$$ $$+
- NVARCHAR(
$$ 0-9 $$+)
並替換為
TEXT
後跟 NOT NULL 然後 CHECK(LENGTH(xy) - 後向引用 1 - <= 後向引用 2…我已經嘗試了上述各種排列和組合,但似乎沒有什麼對我有用。
[pol@fedora data]$ sed 's/^ [a-z]+[ ]+NVARCHAR([0-9]+) NOT NULL/TEXT NOT NULL CHECK \(LENGTH\((\1) <= (\2)\)/g' sed: -e expression #1, char 87: invalid reference \2 on `s' command's RHS
獲取無效的反向引用…
理想情況下,我強調理想情況下,如果 NVARCHAR(xy) 後面的字元串是
NULL
和不是NOT NULL
,我不想要任何長度檢查 - 因為取 NULL 的 LENGTH 是沒有意義的……這是有條件的行為 -不確定在正則表達式中是否可能……ps認為這將是微不足道的。
有這樣的數據:
N'Strada Provinciale 1234', N'Reggio Emilia', NULL, N'10289', N'Italy');
我想將其更改
N'
為簡單的撇號'
(這N'
是 SQL Server 的東西),但我不想將其更改NULL
為空字元串,或者更糟ULL
- 所以我嘗試了:[pol@fedora data]$ sed 's/N\'\'/g TSQLV5.sql
但得到
sed: -e expression #1, char 7: unterminated `s' command
我知道我已經使用
sed
了很多,但我願意接受任何awk
可以執行所需任務的命令。
您已經得到了答案,但我想在您自己的方法中添加問題所在,以便您可以從中學習,而不僅僅是複制一些解決方案:
- 您使用擴展的正則表達式,但忘記
-E
給sed
.- 您想重用標識符,但未將其包含在
()
- 您似乎將 ERE
()
組與文字組混合在一起。你大概是說sed -E 's/^ ([a-z]+)[ ]+NVARCHAR\(([0-9]+)\) NOT NULL/TEXT NOT NULL CHECK \(LENGTH\((\1) <= (\2)\)/g'
- 替換中不顯示空格的第一部分。您還需要對其進行分組並將其用作替換中的參考:
sed -E 's/^( ([a-z]+)[ ]+)NVARCHAR\(([0-9]+)\) NOT NULL/\1TEXT NOT NULL CHECK \(LENGTH\((\2) <= (\3)\)/g'
[ ]+
是一樣的+
。不是錯誤,但會使閱讀更加混亂。- 該
g
選項是多餘的。使用類似錨^
或$
在模式中的多個替換是不可能的。- 您可以通過使
NOT
選項避免多個表達式:`sed -E ’s/^( ($$ a-z $$+) +)NVARCHAR(($$ 0-9 $$+)) (NOT )?NULL/\1TEXT \4NULL CHECK (LENGTH((\2) <= (\3))/'- 另一方面,如果您想省略支票,您可以通過單獨的替換來做到這一點:
s/^( [a-z]+ +)NVARCHAR\(([0-9]+)\) NULL/\1TEXT NULL/
- 您
s/N\'\'/g
錯過了搜尋模式和替換之間的分隔符:s/N\'/\'/g
所以你最終得到
sed -E 's/^( ([a-z]+) +)NVARCHAR\(([0-9]+)\) NOT NULL/\1TEXT NOT NULL CHECK \(LENGTH\((\2) <= (\3)\)/ s/^( [a-z]+ +)NVARCHAR\(([0-9]+)\) NULL/\1TEXT NULL/ s/N\'/\'/g'
既然你使用
fedora
你有GNU sed
,這應該工作:s=" shipname NVARCHAR(40) NOT NULL," echo "$s" | sed -E '/NOT/{s/^ ([[:lower:]]+)\s*NVARCHAR\(([[:digit:]]+)\) NOT NULL,$/\1 TEXT NOT NULL CHECK \(LENGTH\(\1\) <= \2\),/;q0} ; s/^ ([[:lower:]]+)/\1 TEXT NULL,/'
這模擬了一個假的 if。
if
:a
NOT
(/NOT/
) 在 db 結構中找到,然後執行第一個 sed 命令,然後退出 (q0
) 而不執行第二個語句。
else
:沒有
NOT
找到關鍵字,執行第二個實例。對於第二個要求:
sed "s/N'/'/g"
全域搜尋
N'
並將其替換為 only'
。'
我發現與"
for命令行分隔符交換很有用,sed
並使其更乾淨而無需大量轉義。將第一個
sed
放在文件中:#!/bin/sed -Ef # If a NOT is found execute this: # capture the column name and the value of this /NOT/ { s/^ ([[:lower:]]+)\s*NVARCHAR\(([[:digit:]]+)\) NOT NULL,$/\1 TEXT NOT NULL CHECK \(LENGTH\(\1\) <= \2\),/ # Quit without execute the other statement q0 } # Else: If we are here then the database # structure does not contains a length for the column; # so it should be NULL s/^ ([[:lower:]]+)/\1 TEXT NULL,/
該
{
命令用於將更多sed
命令組合在一起。該
q
是quit
命令,它用於使sed
退出。如果第一次測試成功,我在這裡使用它來強制sed
退出,然後再遇到最後一行。