web-dev-qa-db-ja.com

この正規表現はT-SQLから角かっこを確実に削除しますか?

SQL Server Management Studio 17.4を使用しています。これは、Microsoftバージョンの正規表現(regex)を使用した検索と置換をサポートしています。

既存のデータベースからスクリプト化された大きなCREATE TABLEステートメントなど、角かっこをソースコードから確実に削除できる正規表現式が必要です。

私は現在、「検索」ダイアログでこの正規表現を使用しています。

\[((?!\d+)(?!PRIMARY))(([A-Z]|_|[0-9])*?)\]

「置き換え」は$2です。

それは非常にうまく機能しているように見えますが、状況によっては必要な角括弧がなくなる可能性があることを心配しています。

上記の正規表現では、(?!PRIMARY)によってON [PRIMARY]で角括弧が削除されないようになっています。注意すべき他の例外はありますか?

[GROUP]のように、列名が予約語である可能性を排除します。私が作業している環境には、通常その可能性を排除する特定の命名規則があります。

4
Max Vernon

基本的に、PRIMARYで行ったことは、人々がSQLインジェクションを(SQLを "クレンジング"して)阻止しようとするときのように、例外のリストの作成を開始します。現在および将来のすべての予約語に対してこれを実行しますか?厳密な環境であっても、特に予約語ではない場合まだが予約語になる場合後で(文字列分割関数に名前を付けた人について考えるdbo.STRING_SPLIT() SQL Server 2016より前)。

ここに私が袖口から考え出したほんの一握りがあります。数十とそれ以上あります。 RegExはおそらくこれらの一部(ダッシュやスペースなど)をキャプチャしますが、RegExの実行後に残っている角括弧を削除すると、このスクリプトは壊れます。

CREATE TABLE dbo.[Create]
(
  [Alter]      int,
  [Drop]       int,
  [Begin]      int,
  [End]        int,
  [Grant]      int,
  [Deny]       int,
  [Revoke]     int,
  [Truncate]   int,
  [Identity]   int,
  [Select]     int,
  [Update]     int,
  [Insert]     int,
  [Delete]     int,
  [Merge]      int,
  [Procedure]  int,
  [Trigger]    int,
  [Table]      int,
  [Function]   int,
  [View]       int,
  [Raiserror]  int,
  [bad-name]   int,
  [worse name] int,
  [0xd83d]     int,
  [????]        int
);
GO

角かっこの憎しみを乗り越えて慣れる-彼らはいつか何かを間違って名前を付けた人からあなたを守るでしょう。

3
Aaron Bertrand

私が最初にこの質問に答えたとき、どの文字が有効か無効かを決定するルールは少し曖昧でした(ドキュメントは完全に明確ではなく、いくつかの点で誤解を招きます)。ただし、それ以来、このDBAに関連する有効なregular識別子(つまり、"name"または[name]で区切る必要のない識別子)の特定の規則について調査しました。 .SEの質問:

nicodeパラメータと変数名の作成方法

正確なルールを発見しました。これらの正確なルールを取得するための調査により、「ブロック」ではなくUnicodeカテゴリのリストが見つかり、「最初」/「開始」文字と「後続」/「継続」文字の両方を決定するために組み合わせて使用​​できます。その調査はこの投稿の終わりに近づいています(ステップ7および8):

The Uni-Code:The Search for the True List of Valid Characters for T-SQL Regular Identifiers、Part 2

あなたのパターンの作り直し私は以下を思いつきました:

(?!\[(?i:PRIMARY|GROUP)\])\[([\p{L}\p{Nl}\u0023\u005F\uFF3F][\p{L}\p{Nl}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\p{Cf}\u0023\u0024\u0040]*)\](?!\])

いくつかのメモ:

  1. 既にリストになっているので、文字クラス(つまり、|)内の[stuff_here]と一緒に個別の項目を「または」する必要はありません。実際、パイプ記号を含めることは、実際には、許可された文字のリストに追加したことを意味します;-)。

  2. PRIMARY|GROUP...[でラップできるように、除外リスト(つまり、])を分離する必要があります。それ以外の場合は、[PRIMARYa]などをスキップして区切り文字を保持することができます。

  3. 埋め込まれたエスケープされた終了区切り文字を考慮して、(?!\])を最後に追加しました:]]

  4. このパターンは99.5%の効果があります。それがキャッチしない唯一のものはあなたが両方を持っている場合です:

    • 埋め込まれた先行区切り文字:[[〜#〜] and [〜#〜]
    • no埋め込まれたエスケープされた終了区切り文字:]

    つまり、[Te[st]は誤って区切りを解除して[Test(おっと)になりますが、[Te[s]]t][Tes]]t]の両方が期待どおりに区切られたままになります。そして本当に、デリミタが埋め込まれている頻度はどれくらいですか?そして、それでも、終了区切り文字ではなく開始区切り文字がどれくらいの頻度であるのでしょうか?とても珍しいと思います。

もちろん、効果は99.5%でしたが、2つの予約語しかキャッチできませんでした。 予約語のリスト を取得できるため、修正が簡単です。すべての185の予約語を追加すると(まあ、184にはスペースが含まれているため「WITHIN GROUP」は省略されているため、当然除外されます)、次のようになります。

(コードブロックではなくコメントにしたので、[コピーして[検索]ボックスに貼り付けることができます)1行なので、読みやすくなっています)

(?!\ [(?i:ADD | ALL | ALTER | AND | ANY | AS | ASC | AUTHORIZATION | BACKUP | BEGIN | BETWEEN | BREAK | BROWSE | BULK | BY | CASCADE | CASE | CHECK | CHECKPOINT | CLOSE | CLUSTERED | COALESCE | COLLATE | COLUMN | COMMIT | COMPUTE | CONSTRAINT | CONTAINS | CONTAINSTABLE | CONTINUE | CONVERT | CREATE | CROSS | CURRENT | CURRENT_DATE | CURRENT_TIME | CURRENT_TIMESTAMP | CURRENT_USER | CURSOR | DATABASE | DBCC | DEALLOCATE | DECLARE | | DESC | DISK | DISTINCT | DISTRIBUTED | DOUBLE | DROP | DUMP | ELSE | END | ERRLVL | ESCAPE | EXCEPT | EXEC | EXECUTE | EXISTS | EXIT | EXTERNAL | FETCH | FILE | FILLFACTOR | FOR | FOREIGN | FREETEXT | FREETEXTTABLE | FROM | FULL | FUNCTION | GOTO | GRANT | GROUP | HAVING | HOLDLOCK | IDENTITY | IDENTITY_INSERT | IDENTITYCOL | IF | IN | INDEX | INNER | INSERT | INTERSECT | INTO | IS | JOIN | KEY | KILL | LEFT | LIKE | LINENO | LOAD | MERGE | NATIONAL | NOCHECK | NONCLUSTERED | NOT | NULL | NULLIF | OF | OFF | OFFSETS | ON | OPEN | OPENDATASOURCE | OPENQUERY | OPENROWSET | OPENXML | OPTION | OR | ORDER | OUTER | OVER | PERCENT | PIVOT | PLAN | PRECISION |プライマリ|印刷|手順|手順|公開| RAISERROR |読み取り|読み取りテキスト|再構成|参照|複製|復元|復元|返却|戻る| R EVERT | REVOKE | RIGHT | ROLLBACK | ROWCOUNT | ROWGUIDCOL | RULE | SAVE | SCHEMA | SECURITYAUDIT | SELECT | SEMANTICKEYPHRASETABLE | SEMANTICSIMILARITYDETAILSTABLE | SEMANTICSIMILARITYTABLE | SESSION_USER | SET | SETUSER | SHUTDOWN | SOME | STATISTICS | SAMPLETABLE | SYSTEM_USER TO | TOP | TRAN | TRANSACTION | TRIGGER | TRUNCATE | TRY_CONVERT | TSEQUAL | UNION | UNIQUE | UNPIVOT | UPDATE | UPDATETEXT | USE | USER | VALUES | VARYING | VIEW | WAITFOR | WHEN | WHERE | WHILE | WITH | WRITETEXT)\] )\ [([\ p {L}\p {Nl}\u0023\u005F\uFF3F] [\ p {L}\p {Nl}\p {Mn}\p {Mc}\p {Nd}\p {Pc}\p {Cf}\u0023\u0024\u0040] *)\](?!\])

また、新しいパターンでは、置換を$1ではなく$2に変更する必要があります。

私は以下でテストしました:

SELECT [f], [p$o],   -- match these
       [prImary], [12y], [a b], [g%g], [j^6], [f¾], [u²], [h⁊], [s|w]
FROM   dbo.[$a],
       dbo.[#a], dbo.[@d], dbo.[_a] -- match these
WHERE  f.[&col1] = N'it will also [match] <- that :-(';
/* and [this] */

--[PRIMARY]
--[PRIMARy]
--[PRIMARYg]
--[1PRIMARYg]
--[PRIMARY g]
--[GROUP]
--[GROUPBY]
--[@Test]
--[Te]]st]
--[Te]]st]]]
--SELECT 1 AS [Te]]st]]a[b];        --OOPS
--[Te]]s[t]]ab]
--[Te]]s[t]]ab]]
--SELECT 2 AS [Te]]st]]], 2.1 AS [p];
--SELECT 3 AS [Tes[tab];            --OOPS
--[Te[st]                           --OOPS
--[Te[s]]t]
--[Tes]]t]
--[T[e[s]]t]
--[dbo].[TableName]
--[dbo].[1TableName]
--[dbo.1TableName]
--[[t]                              --OOPS
--SELECT 4 AS []]t];
--[[t]]]
-- SELECT 1 AS [d], '---' AS [1f], '$' AS [g]
-- SELECT 1 AS d, '---' AS [1f], '$' AS g
--[OPTION]
--[OPTIONt]

そのすべてが言われているので、私は質問のコメントで最初に言ったことを繰り返します:

スクリプトを "見栄えよく"することは、潜在的なエラーのリスクを伴う価値がありますか? 「いいえ」と言います。ほぼ同じ理由で角かっこを削除するのが常でしたが、「保証された動作」が美学を打ち負かすので、今は常に角かっこを追加する場所になりました。私のために。私は皆のためではないことを理解しています。もちろん、.NET RegExに慣れている場合は、 SQL# (私が書いた)にはRegEx_Replaceが無料である????。

また、これはグローバルな検索と置換であるため、コメントや文字列リテラルにある場合でも、このパターンの文字列に一致することに注意してください。そのような文字列がある場合、これは望ましくない可能性があり、それら(特に文字列リテラル)を除外する方法を見つけるか、この戦いをあきらめる必要があります;)。

4
Solomon Rutzky