[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に変換しようとしています。
それで、私が解決できない2つの小さな問題があります。
shipname NVARCHAR(40) NOT NULL,
それは
(いつも) 2つのスペース
識別子(例:フィールド名) - 常に[az] - 小文字
その後に未知の数の空白が続きます。
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'
だから、
^
文字列の先頭です後ろに2つのスペース
以下は私のフィールド名です。 [az]+
その後、ランダムな数字が続きます。スペース[ ]+
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の長さを取ることは意味がないためです...これは条件付き動作です。正規表現で可能かどうかはわかりません...
ps。これは些細なことだと思います。
次のデータがあります。
N'Strada Provinciale 1234', N'Reggio Emilia', NULL, N'10289', N'Italy');
単純なアポストロフィ(SQL Serverの場合)N'
に変更したいのですが、空の文字列に変更したり、さらに悪く変更したくないので、次のようにします。'
N'
NULL
ULL
[pol@fedora data]$ sed 's/N\'\'/g TSQLV5.sql
しかし得る
sed: -e expression #1, char 7: unterminated `s' command
sed
私はそれをたくさん使ってきたことを知っていますが、awk
必要なことを行うすべてのコマンドで開いています。
答え1
あなたが使用した後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
:
db構造で()を見つけて、NOT
最初のsedコマンドを実行し、2番目のステートメントを実行せずに終了()します。/NOT/
q0
else
:
キーワードが見つからない場合は、NOT
2 番目のインスタンスが実行されます。
2番目の要件の場合:
sed "s/N'/'/g"
グローバルN'
に検索して'
。多くのエスケープ操作を行わなくても、よりきれいにするために'
コマンドライン区切り文字に"
置き換えることが役に立つと思います。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
答え2
すでに答えを得ていますが、問題に対する独自のアプローチを追加して、いくつかのソリューションをコピーするのではなく、問題から学ぶことを望んでいました。
- 拡張正規表現を使用しますが、
-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 / ^(([az] +)+) CHECK (長さ((\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'
答え3
sed
一部のタスクには非常に便利ですが、他のタスクには条件文やprintfなどawk
のすべての機能を備えた言語が必要です。perl
正規表現とRPN計算機の恐ろしい混合のように読まない言語が望ましいです:-).
#!/usr/bin/perl
use strict;
while(<>) {
# print verbatim any lines that don't define an identifier
unless (m/^\s+\S/) { print; next };
# print a blank line before certain identifiers
print "\n" if m/birthdate|address|phone/;
# various regex transformations for IDENTITY and VARCHAR fields
s/\s+NOT NULL IDENTITY/ GENERATED BY DEFAULT AS IDENTITY/;
s/([[:lower:]]+)\s+NVARCHAR\((\d+)\) NOT NULL/$1 TEXT NOT NULL CHECK (LENGTH($1) <= $2)/;
s/\s+NVARCHAR\((\d+)\)\s+NULL/ TEXT NULL/;
# remove length checks from NULL definitions
s/\s+CHECK.*/,/ if /(?<!NOT) NULL/;
# add a comma at the end of the mgrid line if it's not there
s/\s*$/,/ if /mgrid/ && ! /,\s*$/;
# hacky crap to nicely format "TYPE (NOT )?NULL" output.
my @F = split;
my $identifier = shift @F;
my $type = shift @F;
$type .= " " . shift @F if ($F[0] =~ /NOT/);
$type = sprintf "%-8s", $type;
$type .= " " . shift @F if ($F[0] =~ /NULL/);
printf " %-15s %-13s%s\n", $identifier, $type, join(" ",'',@F);
# print the test_field definition after mgrid
if ($identifier eq 'mgrid') {
print " test_field TEXT NULL CHECK (LENGTH(test_field) <= 25)\n";
};
}
これは、入力を(ほぼ)所望の出力に変換する非常に無差別的な方法です。いくつかの正規表現変換と「フィールド」を素敵に並べ替えるいくつかのコード。また、適切な場合は空行とtest_fieldを追加する追加の印刷ステートメントもあります。したがって、通常は役に立ちませんが、必要に応じて他のSQL変換に対応するように調整できます。
スクリプトは、「必須出力」に示されているものではなく、質問に記載されているものを実装します。たとえば、NULLフィールドなので、長さチェックと長さチェックは
region
ありません。postalcode
出力:
CREATE TABLE employee
(
empid INT GENERATED BY DEFAULT AS IDENTITY,
lastname TEXT NOT NULL CHECK (LENGTH(lastname) <= 20),
firstname TEXT NOT NULL CHECK (LENGTH(firstname) <= 10),
title TEXT NULL,
titleofcourtesy TEXT NULL,
birthdate DATE NOT NULL,
hiredate DATE NOT NULL,
address TEXT NOT NULL CHECK (LENGTH(address) <= 60),
city TEXT NOT NULL CHECK (LENGTH(city) <= 15),
region TEXT NULL,
postalcode TEXT NULL,
country TEXT NOT NULL CHECK (LENGTH(country) <= 15),
phone TEXT NOT NULL CHECK (LENGTH(phone) <= 24),
mgrid INT NULL,
test_field TEXT NULL CHECK (LENGTH(test_field) <= 25)
);
スクリプト出力が目的の出力とどのように異なるかは、次のとおりです(コメントといくつかの不要な空白文字を削除するためにクリーンアップした後)。
- region TEXT NULL CHECK (LENGTH(region) <= 15),
- postalcode TEXT NULL CHECK (LENGTH(postalcode) <= 10),
+ region TEXT NULL,
+ postalcode TEXT NULL,
その他の提案:
欲しいかも
PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
しれませんempid
postgresqlには、TEXTよりも適しており、変換が簡単なVARCHAR(n)データ型があります
s/NVARCHAR/VARCHAR/
。 VARCHAR の長さは固定されているため、a) 長さ制約チェックは不要で、b) 索引付けと検索が高速です。フィールドがNULLになることを許可することがデフォルトであるため、明示的に定義する必要はありません。