Удаление псевдонимов из оператора select SQL с использованием C# и регулярных выражений

Я изучаю регулярные выражения и немного играю с ними. Я предложил себе упражнение, в котором у меня есть метод, удаляющий псевдонимы столбцов в операторе SQL Select. Это должно работать следующим образом:

  • Метод может удалять псевдонимы в операторе выбора SQL с ключевым словом AS: «выберите ColumnA AS A».
  • Метод может удалять псевдонимы в операторе выбора SQL без ключевого слова AS: «выберите ColumnB B»
  • Этот метод может удалять псевдонимы в операторе выбора SQL, который содержит «символы операции» (например, символ операции конкатенации): «выберите «Привет» || ‘мир!’ КАК HelloWorld”

На данный момент я создал два метода, которые работают только в определенных случаях. В следующем коде приводится сводка того, что я сделал, и проблем, с которыми я столкнулся.

static void Main(string[] args)
{
    string cols1 = "ColA as AliasA, ColB   AliasB  , As As ASasas, Asasasas as As";
    string cols2 = "'aaa' || 'bbb'  AS   AliasC , 'ccc' || 'ddd' AliasD";

    string answer1 = RemAliases(cols1);     // Works fine
    string answer2 = RemAliases2(cols2);    // Works fine
    string answer3 = RemAliases2(cols1);    // Doesn't work
    string answer4 = RemAliases(cols2);     // Doesn't work            
}

static string RemAliases2(string inputSql)
{
    string pattern1 = @"(.+)\s+AS\s+\w+";
    string replacement1 = "$1";
    string pattern2 = @"(.+)\s+\w+";
    string replacement2 = "$1";
    string result = Regex.Replace(inputSql, pattern1, replacement1, RegexOptions.IgnoreCase);
    result = Regex.Replace(result, pattern2, replacement2, RegexOptions.IgnoreCase);
    return result;
}

static string RemAliases(string inputSql)
{
    string pattern1 = @"(\w+)\s+AS\s+\w+";
    string replacement1 = "$1";
    string pattern2 = @"(\w+)\s+\w+";
    string replacement2 = "$1";
    string result = Regex.Replace(inputSql, pattern1, replacement1, RegexOptions.IgnoreCase);
    result = Regex.Replace(result, pattern2, replacement2, RegexOptions.IgnoreCase);
    return result;
}

Я не ожидал, что «RemAliases()» будет нормально работать с «cols2», потому что «\w+» не совпадает с «|». персонаж. Хотя я ожидал, что «RemAliases2()» также будет нормально работать с «cols1». Может ли кто-нибудь помочь мне, чтобы узнать, почему «RemAliases2()» не работает нормально для случая «cols1»? Пожалуйста, не стесняйтесь вносить любые другие предложения о том, как я использовал эти регулярные выражения.

Заранее спасибо.

PS: я использую .NET 2.0


person Ricardo    schedule 08.07.2010    source источник


Ответы (4)


Одна проблема с (.+)\s+AS\s+\w+ заключается в том, что (.+) жадный. Это означает, что он будет продолжаться до тех пор, пока не перестанет соответствовать ни одному символу, что означает новую строку. Если поставить ? после + это сделает его ленивым, поэтому он остановится, как только найдет первый пробел, потому что пробел соответствует \s.

Следующая проблема заключается в том, что . также соответствует пустому пространству. Итак, когда у вас есть случай ColB AliasB ,, он продолжает работать до тех пор, пока не получит «AS», чтобы соответствовать следующей части регулярного выражения. В данном случае это найдено как часть следующей группы. Итак, вам действительно лучше использовать \w+, как вы делали это в функции RemAliases.

Это то, что я сделал на данный момент. Я отредактирую позже, если найду другие вещи. А пока, поскольку вы учитесь, вот довольно хороший справочник, который я обычно использую, когда мне нужно написать регулярное выражение: Ссылка на регулярное выражение

person fire.eagle    schedule 08.07.2010

Что касается того, почему RemAliases2 не работает для вашего cols1, так это то, что .+ жадный - он берет столько, сколько может.

(.+) займет всю линию. И тогда механизм регулярных выражений сделает шаг назад, чтобы попытаться сопоставить оставшиеся части регулярного выражения. Итак, матчи будут:

(.+) --> "ColA as AliasA, ColB   AliasB  , As As ASasas, Asasasas"
\s+  --> " "
AS   --> "as"
\s+  --> " "
\w+  --> "As"

Где я разбил каждую часть регулярного выражения на отдельные строки и показал часть вашей строки, которая соответствует внутри "" после -->.

Вы применяете каждое из регулярных выражений по очереди, но оно применяется ко всей строке — просто из-за порядка текста в вашей тестовой строке кажется, что оно работает — но оно вообще не масштабируется.

Возможно, лучшим началом (для случая as) будет:

(.+?)(\s+as\s+\w+\s*)(,|$)

Я изменил + на нежадный (+?), я добавил параметр пробела после имени столбца псевдонима, но перед запятой (\s*), и я добавил запятую, чередующуюся с концом строки, чтобы правильно завершить выражение (,|$), чтобы вы могли выполнять несколько итераций для каждого поля в предложении select.

Однако это только одно совпадение, а не несколько (обратите внимание, что я знаю регулярные выражения, но не С#, поэтому я не могу точно сказать, как это работает в С#, но концепции довольно общие). Либо вам нужно перебирать строку несколько раз, либо вызывать функцию с глобальным флагом. (В Java вы бы сделали это, вызвав replaceAll() вместо replace() — я предполагаю, что C# имеет аналогичную конструкцию.)

Применяется глобально, и вставка $1 и $3 в вашу строку замены даст измененный cols1:

ColA, ColB AliasB, As, Asasasas

Тогда у вас есть случай без AS, что сложнее!

person Mike Ryan    schedule 08.07.2010

Чтобы ваше регулярное выражение работало надежно, вам нужно гораздо усерднее указывать, чему оно должно соответствовать, а не пытаться срезать путь с помощью .+. Объяснение ниже довольно длинное. Это мыслительный процесс, которому я следовал при построении регулярного выражения.

Судя по вашим примерам, у вас есть либо идентификатор, например colA, либо объединение строк в одинарных кавычках, например 'aaa' || 'bbb'. Вы можете сопоставить идентификатор с \w+ и строки с '[^']*'(?:\s*\|\|\s*'[^']*')*. Мое регулярное выражение для строк позволяет конкатенировать любое их количество, включая отсутствие конкатенации (т.е. только одну строку в одинарных кавычках).

Чтобы сопоставить любой из этих двух, мы можем использовать \b\w+\s+|'[^']*'(?:\s*\|\|\s*'[^']*')*\s*. Я добавил \s+ после идентификатора, потому что он должен быть отделен от следующего пробелом. Для объединенных строк \s* делает разделительные пробелы необязательными.

За идентификатором или строками может дополнительно следовать ключевое слово As. Если ключевое слово присутствует, за ним должен следовать пробел. Мы можем закодировать это как (As\s+)?.

Наконец, за всем этим следует еще один идентификатор. Этот легко сочетается с \w+.

Собрав все это вместе, мы получаем это регулярное выражение:

(\b\w+\s+|'[^']*'(?:\s*\|\|\s*'[^']*')*\s*)(As\s+)?\w+

Я поставил группу захвата вокруг первой части. Нам это понадобится для поиска и замены. Замена этого регулярного выражения только именем столбца или конкатенацией строк эффективно удаляет часть «как». Текст замены просто $1.

Or in C#:

result = Regex.Replace(inputSql, 
    @"(\b\w+\s+|'[^']*'(?:\s*\|\|\s*'[^']*')*\s*)(As\s+)?\w+", "$1",
    RegexOptions.IgnoreCase);
person Jan Goyvaerts    schedule 10.07.2010

Подход без регулярных выражений:

/// <summary>
/// Remove SQL aliases from a string of selects
/// </summary>
/// <param name="select">A string of selects</param>
/// <returns>A string of selects without any aliases</returns>
public static string RemoveAliases(string select)
{
  string[] originalSelect = select.Split(',');
  string[] newSelect = (string[])originalSelect.Clone();
  string alias = " as ";
  for (int i = 0; i < originalSelect.Length; i++)
  {
    int aliasIndex = originalSelect[i].IndexOf(alias, StringComparison.InvariantCultureIgnoreCase);
    if (aliasIndex >= 0)
    {
      string withoutAlias = originalSelect[i].Substring(0, aliasIndex);
      newSelect[i] = withoutAlias;
    }
  }

  StringBuilder sbNoAliases = new StringBuilder();
  for (int i = 0; i < newSelect.Length - 1; i++)
  {
    sbNoAliases.Append(newSelect[i] + ",");
  }
  sbNoAliases.Append(newSelect[newSelect.Length - 1]);

  return sbNoAliases.ToString();
}
person outofcoolnames    schedule 03.04.2020