정규식 - 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로 변환하려고 합니다.

그래서 해결할 수 없는 두 가지 작은 문제가 있습니다.

shipname       NVARCHAR(40) NOT NULL,

그건

  • (언제나) 공백 두 개

  • 식별자(예: 필드 이름) - 항상 [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'    

그래서,

  • ^문자열의 시작 부분입니다

  • 그 뒤에 공백 두 개

  • 그 뒤에 내 필드 이름 [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의 LENGTH를 취하는 것이 합리적이지 않기 때문입니다. 이것은 조건부 동작입니다. 정규 표현식에서 가능한지 확실하지 않습니다....

ps는 이것이 사소한 일이라고 생각했습니다.

다음과 같은 데이터가 있습니다.

N'Strada Provinciale 1234', N'Reggio Emilia', NULL, N'10289', N'Italy');

N'나는 단지 일반 아포스트로피 '( SQL Server의 일임)로 변경하고 싶지만 빈 문자열로 N'변경하고 싶지 않거나 더 나쁜 경우에는 다음 을 시도했습니다.NULLULL

[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:

a NOT( /NOT/)가 db 구조 내에서 발견되면 첫 번째 sed 명령이 실행되고 q0두 번째 명령문을 실행하지 않고 종료 ( )됩니다.

else:

키워드가 발견되지 않고 NOT두 번째 인스턴스가 실행됩니다.


두 번째 요구 사항의 경우:

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]+) +)NVARCHAR(([0-9]+)) (NOT )?NULL/\1TEXT \4NULL 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 등을 포함하는 awkor 와 같은 완전한 기능을 갖춘 언어가 필요합니다 . 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 변환에 맞게 조정할 수 있습니다.

  • 스크립트는 "원하는 출력"에 표시되는 내용이 아니라 질문에 설명을 구현합니다(예를 들어 region및 둘 다 postalcodeNULL 필드이기 때문에 길이를 확인하지 않습니다).

산출:

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에는 VARCHAR(n) 데이터 유형이 있으며 이는 아마도 TEXT보다 더 적합하고 변환하기가 훨씬 간단합니다 s/NVARCHAR/VARCHAR/. VARCHAR의 길이는 고정되어 있으므로 a) 길이 제약 검사가 필요하지 않으며 b) 색인 및 검색이 더 빠릅니다.

  • 필드가 NULL이 되도록 허용하는 것이 기본값이므로 명시적으로 필드를 정의할 필요가 없습니다.

관련 정보