正規表現 - SQL操作

正規表現 - 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に変換しようとしています。

それで、私が解決できない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]+)

そして代替

TEXTNOT 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'NULLULL

[pol@fedora data]$ sed 's/N\'\'/g TSQLV5.sql 

しかし得る

sed: -e expression #1, char 7: unterminated `s' command

sed私はそれをたくさん使ってきたことを知っていますが、awk必要なことを行うすべてのコマンドで開いています。

答え1

あなたが使用した後fedoraGNU 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:

キーワードが見つからない場合は、NOT2 番目のインスタンスが実行されます。


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。最初のテストが成功した場合は、最後の行に出会う前に強制終了するためにここで使用しています。quitsedsed

答え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になることを許可することがデフォルトであるため、明示的に定義する必要はありません。

関連情報