การลบนามแฝงออกจากคำสั่ง SQL select โดยใช้ C# และนิพจน์ทั่วไป

ฉันกำลังเรียนรู้สำนวนปกติและเล่นกับมันนิดหน่อย ฉันเสนอตัวเองให้ทำแบบฝึกหัดโดยฉันมีวิธีการลบนามแฝงของคอลัมน์ในคำสั่ง SQL Select สิ่งนี้ควรทำงานเช่นนี้:

  • วิธีการนี้สามารถลบนามแฝงในคำสั่ง SQL select ด้วยคีย์เวิร์ด AS: “select ColumnA AS A”
  • วิธีการนี้สามารถลบนามแฝงในคำสั่ง SQL select โดยไม่มีคีย์เวิร์ด AS: “select ColumnB B”
  • วิธีการสามารถลบนามแฝงในคำสั่ง SQL select ที่มี "อักขระการดำเนินการ" (เช่นอักขระการดำเนินการต่อข้อมูล): "select 'Hello' || 'โลก!' AS 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" เพื่อให้ตรงกับส่วนถัดไปของ regex ในกรณีนี้จะพบว่าเป็นส่วนหนึ่งของกลุ่มถัดไป ดังนั้น คุณควรใช้ \w+ เหมือนที่คุณทำในฟังก์ชัน RemAliases จะดีกว่ามาก

นั่นเท่าที่ฉันได้ไปตอนนี้ ฉันจะแก้ไขเพิ่มเติมในภายหลังหากฉันพบสิ่งอื่น ในระหว่างนี้ เนื่องจากคุณกำลังเรียนรู้ นี่เป็นข้อมูลอ้างอิงที่ดีทีเดียวที่ฉันมักจะใช้เมื่อต้องเขียน regex: การอ้างอิง Regex

person fire.eagle    schedule 08.07.2010

ส่วนสาเหตุที่ RemAliases2 ใช้ไม่ได้กับ cols1 ของคุณก็คือ .+ นั้นโลภมาก -- ต้องใช้ให้มากที่สุดเท่าที่จะทำได้

(.+) จะใช้ทั้งบรรทัด จากนั้นกลไก regex จะถอยหลังเพื่อพยายามจับคู่ส่วนที่เหลือของ regex ดังนั้นการแข่งขันจะเป็นดังนี้:

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

โดยที่ฉันแยกแต่ละส่วนของ regex ออกเป็นบรรทัดแยกกัน และแสดงส่วนของสตริงของคุณที่ตรงกันภายใน "" หลัง -->

คุณกำลังใช้ regexe แต่ละรายการตามลำดับ แต่จะมีผลกับสตริงทั้งหมด -- มันเกิดขึ้นเพราะลำดับของข้อความในสตริงทดสอบของคุณ จึงดูเหมือนว่าจะใช้งานได้ -- แต่จะไม่ปรับขนาดเลย

การเริ่มต้นที่ดีกว่าที่เป็นไปได้อย่างหนึ่ง (สำหรับกรณี) คือ:

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

ฉันเปลี่ยนเครื่องหมาย + ให้เป็นแบบไม่โลภ (+?) ฉันได้เพิ่มตัวเลือกช่องว่างหลังชื่อคอลัมน์นามแฝง แต่อยู่หน้าเครื่องหมายจุลภาค (\s*) และฉันได้เพิ่มเครื่องหมายจุลภาคสลับกับจุดสิ้นสุด ของบรรทัด เพื่อสิ้นสุดนิพจน์ (,|$) อย่างถูกต้อง เพื่อให้คุณสามารถวนซ้ำได้หลายครั้งสำหรับแต่ละฟิลด์ในส่วนคำสั่งที่เลือก

อย่างไรก็ตาม นี่เป็นการจับคู่เพียงครั้งเดียวเท่านั้น ไม่ใช่หลายรายการ (โปรดทราบว่าฉันรู้จัก regex'es แต่ไม่ใช่ C# ดังนั้นฉันจึงไม่สามารถบอกได้อย่างแน่ชัดว่าสิ่งนี้ทำงานอย่างไรใน C# แต่แนวคิดค่อนข้างทั่วไป) คุณจำเป็นต้องวนซ้ำผ่านสตริงหลายๆ ครั้ง หรือเรียกใช้ฟังก์ชันด้วยการตั้งค่าสถานะโกลบอล (ใน Java คุณจะทำได้โดยการเรียกแทนที่All() แทนการแทนที่() -- ฉันถือว่า C# มีโครงสร้างที่คล้ายกัน)

ใช้ทั่วโลก และการแทรกทั้ง $1 และ $3 ลงในสตริงการแทนที่ของคุณจะให้ cols1 ที่แก้ไขเป็น:

ColA, ColB AliasB, อัส, อาซาซาส

ถ้าอย่างนั้น คุณมีกรณีที่ไม่มี AS ซึ่งยากกว่า!

person Mike Ryan    schedule 08.07.2010

เพื่อให้นิพจน์ทั่วไปของคุณทำงานได้อย่างน่าเชื่อถือ คุณต้องขยันมากขึ้นในการสะกดว่าควรจับคู่อะไร แทนที่จะพยายามใช้ทางลัดด้วย .+ คำอธิบายด้านล่างนี้ค่อนข้างจะยืดเยื้อ มันเป็นกระบวนการคิดที่ฉันปฏิบัติตามเมื่อสร้าง regex

จากตัวอย่างของคุณ คุณอาจมีตัวระบุ เช่น colA หรือการต่อสตริงที่มีเครื่องหมายคำพูดเดี่ยว เช่น 'aaa' || 'bbb' คุณสามารถจับคู่ตัวระบุกับ \w+ และสตริงด้วย '[^']*'(?:\s*\|\|\s*'[^']*')* regex ของฉันสำหรับสตริงอนุญาตให้มีการเชื่อมโยงสตริงจำนวนเท่าใดก็ได้ รวมถึงการไม่มีการต่อกัน (เช่น สตริงที่มีเครื่องหมายคำพูดเดี่ยวเพียงสตริงเดียว)

เพื่อให้ตรงกับสิ่งใดสิ่งหนึ่งจากสองสิ่งนี้ เราสามารถใช้ \b\w+\s+|'[^']*'(?:\s*\|\|\s*'[^']*')*\s* ฉันได้เพิ่ม \s+ หลังตัวระบุ เนื่องจากจะต้องแยกออกจากสิ่งที่ตามมาด้วยการเว้นวรรค สำหรับสตริงที่ต่อกัน \s* ทำให้การเว้นวรรคเป็นทางเลือก

ตัวระบุหรือสตริงสามารถตามด้วยคีย์เวิร์ด As ก็ได้ หากมีคำหลักอยู่จะต้องตามด้วยช่องว่าง เราสามารถเขียนโค้ดนี้เป็น (As\s+)?

ในที่สุด ทั้งหมดนี้ตามมาด้วยตัวระบุอื่น อันนี้จับคู่กับ \w+ ได้อย่างง่ายดาย

เมื่อรวมทุกอย่างเข้าด้วยกันแล้ว เราจะได้ regex นี้:

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

ฉันวางกลุ่มดักจับไว้รอบส่วนแรก เราจำเป็นต้องใช้สิ่งนั้นในการค้นหาและแทนที่ การแทนที่การจับคู่ regex นี้ด้วยเพียงชื่อคอลัมน์หรือการต่อสตริงจะเป็นการลบส่วน "as" ออกอย่างมีประสิทธิภาพ ข้อความแทนที่คือ $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

วิธีการที่ไม่ใช่ 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