Menghapus alias dari pernyataan pemilihan SQL, menggunakan C# dan ekspresi reguler

Saya mempelajari ekspresi reguler dan sedikit bermain-main dengannya. Saya mengusulkan sendiri sebuah latihan di mana saya memiliki metode yang menghapus alias kolom dalam pernyataan SQL Select. Ini seharusnya berfungsi seperti ini:

  • Metode ini dapat menghapus alias dalam pernyataan pemilihan SQL dengan kata kunci AS: “pilih KolomA AS A”
  • Metode ini dapat menghapus alias dalam pernyataan pemilihan SQL tanpa kata kunci AS: “pilih KolomB B”
  • Metode ini dapat menghapus alias dalam pernyataan pemilihan SQL yang berisi “karakter operasi” (seperti karakter operasi gabungan): “pilih 'Halo' || 'dunia!' SEBAGAI HelloWorld”

Sejauh ini saya telah membuat dua metode yang hanya berfungsi pada kasus tertentu. Kode berikut memberikan ringkasan tentang apa yang telah saya lakukan dan masalah yang saya hadapi.

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;
}

Saya tidak mengharapkan "RemAliases()" berfungsi dengan baik dengan "cols2", karena "\w+" tidak cocok dengan "|" karakter. Meskipun demikian, saya mengharapkan "RemAliases2()" juga berfungsi dengan baik dengan "cols1". Bisakah seseorang memberi saya bantuan untuk mengetahui mengapa "RemAliases2()" tidak berfungsi dengan baik untuk kasus "cols1"? Mohon, jangan ragu untuk memberikan saran lain tentang cara saya menggunakan ekspresi reguler ini.

Terima kasih sebelumnya.

PS: Saya menggunakan .NET 2.0


person Ricardo    schedule 08.07.2010    source sumber


Jawaban (4)


Satu masalah dengan (.+)\s+AS\s+\w+ adalah (.+) itu serakah. Artinya, baris tersebut akan terus berjalan hingga tidak ada lagi karakter yang cocok, yang berarti baris baru. Jika Anda menaruh ? setelah tanda + akan membuatnya malas, sehingga akan berhenti setelah menemukan spasi pertama karena spasinya cocok dengan \s.

Masalah selanjutnya adalah. cocok dengan ruang putih juga. Jadi, jika Anda memiliki kasus ColB AliasB ,, kasus tersebut akan terus berjalan hingga mendapatkan " AS" yang cocok dengan bagian berikutnya dari regex. Dalam hal ini, itu ditemukan sebagai bagian dari kelompok berikutnya. Jadi, lebih baik Anda menggunakan \w+ seperti yang Anda lakukan pada fungsi RemAliases.

Sejauh itulah yang telah saya lakukan saat ini. Saya akan mengedit lebih lanjut nanti jika saya menemukan hal lain. Sementara itu, karena Anda sedang belajar, inilah referensi bagus yang biasanya saya gunakan ketika saya perlu menulis beberapa regex: Referensi Regex

person fire.eagle    schedule 08.07.2010

Adapun mengapa RemAliases2 tidak berfungsi untuk cols1 Anda adalah karena .+ serakah -- dibutuhkan sebanyak mungkin.

(.+) akan mengambil seluruh baris. Dan kemudian mesin regex akan mundur untuk mencoba mencocokkan bagian regex yang tersisa. Jadi, pertandingannya adalah:

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

Di mana saya telah membagi setiap bagian regex menjadi baris terpisah, dan menunjukkan bagian string Anda yang cocok di dalam "" setelah -->.

Anda menerapkan masing-masing regex secara bergantian, namun regex ini berlaku untuk seluruh string -- kebetulan karena urutan teks dalam string pengujian Anda, regex tersebut tampak berfungsi -- namun tidak dapat diskalakan sama sekali.

Salah satu kemungkinan awal yang lebih baik (untuk kasus as) adalah:

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

Saya telah mengubah + menjadi tidak serakah (+?), saya telah menambahkan opsi spasi setelah nama kolom alias tetapi sebelum koma (\s*), dan saya telah menambahkan koma bergantian dengan akhir baris, untuk mengakhiri ekspresi (,|$) dengan benar sehingga Anda dapat mengulangi beberapa kali untuk setiap bidang dalam klausa pilih.

Namun, ini hanya melakukan satu kecocokan, bukan beberapa (perhatikan bahwa saya tahu regex tetapi tidak C#, jadi saya tidak bisa mengatakan dengan tepat cara kerjanya di C#, tetapi konsepnya cukup umum). Entah Anda perlu mengulangi string beberapa kali, atau memanggil fungsi dengan tanda global. (Di Java, Anda akan melakukannya dengan memanggil replaceAll() alih-alih replace() -- Saya berasumsi C# memiliki konstruksi yang serupa.)

Diterapkan secara global, dan memasukkan $1 dan $3 ke dalam string pengganti Anda akan menghasilkan cols1 yang dimodifikasi:

ColA, ColB AliasB, As, Asasasas

Kemudian, Anda memiliki kasus tanpa AS, yang mana lebih sulit!

person Mike Ryan    schedule 08.07.2010

Agar ekspresi reguler Anda berfungsi dengan andal, Anda harus lebih rajin mengeja apa yang cocok, daripada mencoba mengambil jalan pintas dengan .+. Penjelasan di bawah ini agak bertele-tele. Ini adalah proses berpikir yang saya ikuti saat membuat regex.

Berdasarkan contoh Anda, Anda memiliki pengenal seperti colA atau rangkaian string yang diberi tanda kutip tunggal seperti 'aaa' || 'bbb'. Anda dapat mencocokkan pengidentifikasi dengan \w+ dan string dengan '[^']*'(?:\s*\|\|\s*'[^']*')*. Regex saya untuk string memungkinkan sejumlah string untuk digabungkan, termasuk tanpa penggabungan (yaitu hanya satu string yang diberi tanda kutip tunggal).

Untuk mencocokkan salah satu dari keduanya kita dapat menggunakan \b\w+\s+|'[^']*'(?:\s*\|\|\s*'[^']*')*\s*. Saya telah menambahkan \s+ setelah pengenal karena harus dipisahkan dari yang berikutnya dengan spasi. Untuk string gabungan, \s* menjadikan spasi pembatas opsional.

Pengidentifikasi atau string secara opsional dapat diikuti dengan kata kunci As. Jika kata kunci ada maka harus diikuti spasi. Kita dapat mengkodekannya sebagai (As\s+)?.

Akhirnya, semua ini diikuti oleh pengenal lain. Yang ini mudah dicocokkan dengan \w+.

Menggabungkan semuanya, kita mendapatkan regex ini:

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

Saya menempatkan grup penangkap di sekitar bagian pertama. Kami memerlukannya untuk pencarian dan penggantian. Mengganti pencocokan regex ini hanya dengan nama kolom atau rangkaian string secara efektif menghilangkan bagian "sebagai". Teks penggantinya hanyalah $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

Pendekatan Non Regex:

/// <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