มีวิธีที่ดีกว่าในการสร้าง SQL WHERE แบบไดนามิกมากกว่าการใช้ 1 = 1 ที่จุดเริ่มต้นหรือไม่


110

ฉันกำลังสร้างแบบสอบถามSQLใน C # มันจะแตกต่างกันไปขึ้นอยู่กับเงื่อนไขบางอย่างที่เก็บไว้เป็นตัวแปรในโค้ด

string Query="SELECT * FROM Table1 WHERE 1=1 ";
if (condition1) 
    Query += "AND Col1=0 ";
if (condition2) 
    Query += "AND Col2=1 ";
if (condition3) 
    Query += "AND Col3=2 ";

ใช้งานได้ แต่การทดสอบ 1 = 1 ดูเหมือนจะไม่สง่างาม หากฉันไม่ได้ใช้มันฉันจะต้องจำและตรวจสอบทุกครั้งว่ามีการเพิ่มคำหลัก "ที่ไหน" ในข้อความค้นหาแล้วหรือไม่

มีวิธีแก้ไขที่ดีกว่านี้หรือไม่?


118
พูดตามตรง - ฉันก็จะทำแบบนี้เหมือนกัน แต่ฉันจะใช้42 = 42;-)
fero

5
จริงๆแล้วฉันมักจะเขียนข้อความค้นหาของฉันแบบนี้ ทำให้ง่ายต่อการแสดงความคิดเห็นเงื่อนไข
Deruijter

4
@catfood โครงการแรกที่ฉันทำในฐานะนักศึกษาฝึกงานคือการเขียนเครื่องมือเพื่อช่วยวิเคราะห์แบบสอบถามประสิทธิภาพเทียบกับเซิร์ฟเวอร์ Sybase ของเรา การค้นพบที่น่าขบขันคือSelect 42คำค้นหานับแสนที่เราได้รับ (ไม่สนุกคือพยายามติดตามแหล่งที่มา)
Mr. Mindor

24
If I didn't use it, I would have to remember and check every time if "where" keyword was already added or not to the query- 1 = 1นั่นเป็นเหตุผลที่คุณใช้ เครื่องมือฐานข้อมูลจะปรับให้เหมาะสมที่สุดดังนั้นแม้ว่ามันอาจดูน่าเกลียด แต่ก็เป็นวิธีที่ง่ายที่สุดในการแก้ปัญหา
Robert Harvey

4
แม้ว่าคำตอบที่ได้รับจะดีมาก แต่ฉันคิดว่ารหัสเดิมของคุณอ่านง่ายที่สุด
Uooo

คำตอบ:


157

บันทึกเงื่อนไขในรายการ:

List<string> conditions = new List<string>();

if (condition1) conditions.Add("Col1=0");
//...
if (conditions.Any())
    Query += " WHERE " + string.Join(" AND ", conditions.ToArray());

24
ทางออกที่ดี แต่ToArray()ไม่จำเป็นกับ. NET 4 เนื่องจากมีการโอเวอร์โหลดซึ่งยอมรับIEnumerable<string>ไฟล์.
fero

101
ฉันรู้สึกตื่นเต้นสำหรับโอกาสในการฉีด SQL ทั้งหมดที่มีให้
asteri

12
@Jeff หากคุณไม่ได้กำหนดค่าในส่วนคำสั่งที่ยากคุณสามารถมีรายการที่ 2 ด้วย SqlParameters ได้เช่นกัน คุณเพียงแค่ต้องเติมข้อมูลในรายการนั้นพร้อมกันกับรายการเงื่อนไขและเรียกAddRange (พารามิเตอร์ ToArray ())ในตอนท้าย
Scott Chamberlain

5
@ScottChamberlain ใช่คุณสามารถหลีกเลี่ยงสตริงอินพุตก่อนที่จะใส่ในรายการ ส่วนใหญ่ฉันแค่เตือนให้ระวังการโจมตีที่อาจเกิดขึ้นโดยใช้อารมณ์ขัน
asteri

4
@ เจฟฟ์มีความเสี่ยงต่อการแทรก SQL เท่านั้นหากเงื่อนไขรวมถึงการป้อนข้อมูลของผู้ใช้ (ตัวอย่างเดิมไม่มี)
D Stanley

85

วิธีแก้ไขอย่างหนึ่งคืออย่าเขียนแบบสอบถามด้วยตนเองโดยการต่อท้ายสตริง คุณสามารถใช้ ORM เช่นEntity Frameworkและด้วย LINQ to Entities ใช้คุณลักษณะที่ภาษาและเฟรมเวิร์กเสนอให้คุณ:

using (var dbContext = new MyDbContext())
{
    IQueryable<Table1Item> query = dbContext.Table1;

    if (condition1)
    {
        query = query.Where(c => c.Col1 == 0);
    }
    if (condition2)
    {
        query = query.Where(c => c.Col2 == 1);
    }
    if (condition3)
    {
        query = query.Where(c => c.Col3 == 2);
    }   

    PrintResults(query);
}

@vaheeds ฉันไม่เข้าใจคำถามนั้น ทั้งสองเป็น ORM ที่แตกต่างกัน
CodeCaster

ขออภัยฉันกำลังค้นหาเพื่อเปรียบเทียบประสิทธิภาพของ dapper กับ ORM อื่น ๆ และฉันมาถึงที่นี่โดย google ดังนั้นฉันจึงคิดว่าPrintResults(query)แบบสอบถามที่สร้างขึ้นจะใช้ใน dapper เป็นแบบสอบถาม !!
vaheeds

@vaheeds ไม่เป็นไร แต่การไม่เข้าใจคำตอบไม่รับประกันการลดคะแนน หากนั่นคือคุณซึ่งบังเอิญเกิดขึ้นพร้อมกันกับความคิดเห็นของคุณ
CodeCaster

สิทธิของคุณนั่นเป็นความเข้าใจผิด ฉันประสบปัญหา linq ถึงเอนทิตีประสิทธิภาพไม่ดีในการค้นหาที่ซับซ้อน ฉันชดเชยการลงคะแนนด้วยการโหวตคำตอบอื่น ๆ ของคุณ)
vaheeds

นั่นไม่ใช่คำตอบสำหรับคำถาม
HGMamaci

17

มีการ overkill เล็กน้อยในกรณีง่ายๆนี้ แต่ฉันเคยใช้รหัสที่คล้ายกันนี้ในอดีต

สร้างฟังก์ชัน

string AddCondition(string clause, string appender, string condition)
{
    if (clause.Length <= 0)
    {
        return String.Format("WHERE {0}",condition);
    }
    return string.Format("{0} {1} {2}", clause, appender, condition);
}

ใช้แบบนี้ครับ

string query = "SELECT * FROM Table1 {0}";
string whereClause = string.Empty;

if (condition 1)
    whereClause = AddCondition(whereClause, "AND", "Col=1");

if (condition 2)
    whereClause = AddCondition(whereClause, "AND", "Col2=2");

string finalQuery = String.Format(query, whereClause);

วิธีนี้หากไม่พบเงื่อนไขคุณก็ไม่ต้องกังวลกับการโหลดคำสั่ง where ในแบบสอบถามและบันทึกเซิร์ฟเวอร์ sql เป็นเวลาไมโครวินาทีในการประมวลผลขยะโดยที่ประโยคเมื่อแยกวิเคราะห์คำสั่ง sql


ฉันไม่เห็นว่าสิ่งนี้ทำให้สง่างามมากขึ้นได้อย่างไร ยังไม่ชัดเจนว่าเกิดอะไรขึ้นที่นี่ ฉันสามารถมองเห็นการใช้ฟังก์ชันยูทิลิตี้นั้นได้ แต่มันไม่ได้หรูหรากว่า
usr

ให้คะแนนคุณหนึ่งคะแนนเพื่อให้ความกระจ่างแก่เราเกี่ยวกับความสำคัญของ
เสี้ยว

15

มีวิธีแก้ปัญหาอื่นซึ่งอาจไม่สวยงาม แต่ใช้ได้ผลและแก้ปัญหาได้:

String query = "SELECT * FROM Table1";
List<string> conditions = new List<string>();
// ... fill the conditions
string joiner = " WHERE ";
foreach (string condition in conditions) {
  query += joiner + condition;
  joiner = " AND "
}

สำหรับ:

  • ล้างรายการเงื่อนไขผลจะเป็นเพียงSELECT * FROM Table1,
  • เงื่อนไขเดียวก็จะเป็น SELECT * FROM Table1 WHERE cond1
  • แต่ละเงื่อนไขต่อไปนี้จะสร้างเพิ่มเติม AND condN

6
ที่ทิ้งห้อยWHEREหากไม่มีเพรดิเคต 1 = 1 มีอยู่โดยเฉพาะเพื่อหลีกเลี่ยงสิ่งนั้น
Gaius

จึงเปลี่ยนไปใช้String query = "SELECT * FROM Table1";และstring jointer = " WHERE ";?
Brendan Long

@BrendanLong แล้วWHEREเป็นANDที่จะถูกวางไว้ระหว่างเงื่อนไข?
PenguinCoder

@PenguinCoder เป็นการยากที่จะแสดงรหัสทั้งหมดในความคิดเห็น ฉันหมายถึงแทนที่string joinerบรรทัดด้วยstring joiner = " WHERE ";และปล่อยให้joiner = " AND ";บรรทัดเดียวดาย
Brendan Long

@Gaius ฉันคิดว่า coditions ไม่ว่างเปล่า แต่การใส่ WHERE ใน joiner ควรทำเคล็ดลับ ขอบคุณสำหรับคำพูด!
Dariusz

11

เพียงทำสิ่งนี้:

using (var command = connection.CreateCommand())
{
    command.CommandText = "SELECT * FROM Table1";

    var conditions = "";
    if (condition1)
    {    
        conditions += "Col1=@val1 AND ";
        command.AddParameter("val1", 1);
    }
    if (condition2)
    {    
        conditions += "Col2=@val2 AND ";
        command.AddParameter("val2", 1);
    }
    if (condition3)
    {    
        conditions += "Col3=@val3 AND ";
        command.AddParameter("val3", 1);
    }
    if (conditions != "")
        command.CommandText += " WHERE " + conditions.Remove(conditions.Length - 5);
}

มันฉีด SQLปลอดภัยและIMHOก็สวยสะอาด Remove()เพียงแค่เอาสุดท้ายAND;

ใช้งานได้ทั้งสองอย่างหากไม่มีการตั้งเงื่อนไขหากมีการตั้งค่าหรือตั้งค่าไว้หลายรายการ


1
ฉันไม่แน่ใจ (อย่าใช้ C # ตัวเอง) แต่ฉันจะบอกว่าconditions != nullเป็นtrueเช่นนั้นเสมอเมื่อคุณเริ่มต้นด้วย""(เว้นแต่ใน C # "" == null) มันน่าจะเป็นเช็คถ้าconditionsไม่ว่าง… ;-)
siegi

9

เพียงต่อท้ายสองบรรทัดที่ด้านหลัง

string Query="SELECT * FROM Table1 WHERE 1=1 ";
if (condition1) Query+="AND Col1=0 ";
if (condition2) Query+="AND Col2=1 ";
if (condition3) Query+="AND Col3=2 ";
Query.Replace("1=1 AND ", "");
Query.Replace(" WHERE 1=1 ", "");

เช่น

SELECT * FROM Table1 WHERE 1=1 AND Col1=0 AND Col2=1 AND Col3=2 

จะกลายเป็น

SELECT * FROM Table1 WHERE Col1=0 AND Col2=1 AND Col3=2 

ในขณะที่

SELECT * FROM Table1 WHERE 1=1 

จะกลายเป็น

SELECT * FROM Table1

=====================================

ขอขอบคุณที่ชี้ให้เห็นข้อบกพร่องของโซลูชันนี้:

"สิ่งนี้อาจทำลายการสืบค้นได้หากเงื่อนไขใดเงื่อนไขหนึ่งมีข้อความ" 1 = 1 AND "หรือ" WHERE 1 = 1 "ซึ่งอาจเกิดขึ้นได้หากเงื่อนไขมีการสืบค้นย่อยหรือพยายามตรวจสอบว่าบาง ตัวอย่างเช่นคอลัมน์มีข้อความนี้บางทีนี่อาจไม่ใช่ปัญหาในกรณีของคุณ แต่คุณควรระลึกไว้เสมอ… "

ในการกำจัดปัญหานี้เราจำเป็นต้องแยกแยะ "หลัก" WHERE 1 = 1และสิ่งเหล่านั้นออกจากข้อความค้นหาย่อยซึ่งง่ายมาก:

เพียงแค่ทำให้ "หลัก" WHEREพิเศษ: ฉันจะผนวกเข้าสู่ระบบ "$"

string Query="SELECT * FROM Table1 WHERE$ 1=1 ";
if (condition1) Query+="AND Col1=0 ";
if (condition2) Query+="AND Col2=1 ";
if (condition3) Query+="AND Col3=2 ";

จากนั้นต่อท้ายสองบรรทัด:

Query.Replace("WHERE$ 1=1 AND ", "WHERE ");
Query.Replace(" WHERE$ 1=1 ", "");

1
ซึ่งอาจทำลายแบบสอบถามถ้าด้วยเหตุผลใดก็ตามหนึ่งในเงื่อนไขที่ประกอบด้วยข้อความหรือ"1=1 AND " " WHERE 1=1 "กรณีนี้อาจเกิดขึ้นได้หากเงื่อนไขมีแบบสอบถามย่อยหรือพยายามตรวจสอบว่าบางคอลัมน์มีข้อความนี้หรือไม่ บางทีนี่อาจไม่ใช่ปัญหาในกรณีของคุณ แต่คุณควรระลึกไว้เสมอ…
siegi

8

ใช้สิ่งนี้:

string Query="SELECT * FROM Table1 WHERE ";
string QuerySub;
if (condition1) QuerySub+="AND Col1=0 ";
if (condition2) QuerySub+="AND Col2=1 ";
if (condition3) QuerySub+="AND Col3=2 ";

if (QuerySub.StartsWith("AND"))
    QuerySub = QuerySub.TrimStart("AND".ToCharArray());

Query = Query + QuerySub;

if (Query.EndsWith("WHERE "))
    Query = Query.TrimEnd("WHERE ".ToCharArray());

คำตอบนี้จะทำงานและมีอะไรผิดปกติกับมันจริงๆ แต่ผมไม่คิดว่ามันเป็นมากขึ้นสะอาดและเรียบง่ายกว่าคำถามเดิม การค้นหาสตริงQuerySubอยู่ในความคิดของฉันไม่ดีหรือแย่ไปกว่าการใช้where 1=1แฮ็ค แต่เป็นความคิดที่ช่วยสนับสนุน
catfood

3
เกิดข้อผิดพลาด แก้ไขแล้ว คำถามของฉันจะระเบิดหากไม่มีเงื่อนไขใด ๆ :-P ฉันยังคงต้องบอกว่า Ahmed หรือ CodeCaster สำหรับฉันเป็นทางออกที่ดีที่สุด ฉันนำเสนอทางเลือกสำหรับพวกคุณเท่านั้น!
Anshuman

นี้ยังคงผิดโดยทั่วไป สมมติว่ามันเป็น... FROM SOMETABLE WHERE ; แล้วจริงจะลดนี้TrimEnd ... FROM SOMETABLถ้านี่เป็นจริงStringBuilder(ซึ่งควรจะเป็นถ้าคุณมีการจัดการสตริงมากขนาดนี้หรือมากกว่านั้น) คุณก็Query.Length -= "WHERE ".Length;ทำได้
Mark Hurd

มาร์คมันได้ผล ฉันได้ลองสิ่งนี้ในหลายโครงการ ลองแล้วคุณจะพบว่ามัน!
Anshuman

8
น่าเกลียดเหมือนนรก :) บวกกับมันสามารถสร้างได้ถึง 7 สายถ้าฉันนับถูก
Piotr Perak

5

ถ้านี่คือSQL Serverคุณสามารถทำให้โค้ดนี้สะอาดขึ้นมาก

นอกจากนี้ยังถือว่าพารามิเตอร์จำนวนที่ทราบซึ่งอาจเป็นข้อสันนิษฐานที่ไม่ดีเมื่อฉันคิดถึงความเป็นไปได้

ใน C # คุณจะใช้:

using (SqlConnection conn = new SqlConnection("connection string"))
{
    conn.Open();
    SqlCommand command = new SqlCommand()
    {
        CommandText = "dbo.sample_proc",
        Connection = conn,
        CommandType = CommandType.StoredProcedure
    };

    if (condition1)
        command.Parameters.Add(new SqlParameter("Condition1", condition1Value));
    if (condition2)
        command.Parameters.Add(new SqlParameter("Condition2", condition2Value));
    if (condition3)
        command.Parameters.Add(new SqlParameter("Condition3", condition3Value));

    IDataReader reader = command.ExecuteReader();

    while(reader.Read())
    {
    }

    conn.Close();
}

จากนั้นทางด้าน SQL:

CREATE PROCEDURE dbo.sample_proc
(
    --using varchar(50) generically
    -- "= NULL" makes them all optional parameters
    @Condition1 varchar(50) = NULL
    @Condition2 varchar(50) = NULL
    @Condition3 varchar(50) = NULL
)
AS
BEGIN
    /*
    check that the value of the parameter 
    matches the related column or that the 
    parameter value was not specified.  This
    works as long as you are not querying for 
    a specific column to be null.*/
    SELECT *
    FROM SampleTable
    WHERE (Col1 = @Condition1 OR @Condition1 IS NULL)
    AND   (Col2 = @Condition2 OR @Condition2 IS NULL)
    AND   (Col3 = @Condition3 OR @Condition3 IS NULL)
    OPTION (RECOMPILE)
    --OPTION(RECOMPILE) forces the query plan to remain effectively uncached
END

การซ่อนคอลัมน์ของคุณภายในนิพจน์สามารถป้องกันการใช้ดัชนีและเทคนิคนี้เป็นกำลังใจด้วยเหตุนี้ที่นี่
bbsimonbb

นั่นเป็นการค้นพบที่น่าสนใจ ขอบคุณสำหรับข้อมูลนั้น จะอัปเดต
mckeejm

5

ทำไมไม่ใช้ Query Builder ที่มีอยู่ สิ่งที่ต้องการSql กะตะ

รองรับความซับซ้อนที่เงื่อนไขการรวมและการสืบค้นย่อย

var query = new Query("Users").Where("Score", ">", 100).OrderByDesc("Score").Limit(100);

if(onlyActive)
{
   query.Where("Status", "active")
}

// or you can use the when statement

query.When(onlyActive, q => q.Where("Status", "active"))

ใช้งานได้กับ Sql Server, MySql และ PostgreSql


4

วิธีแก้ปัญหาตามตัวอักษรที่เร็วที่สุดสำหรับสิ่งที่คุณกำลังขอที่ฉันคิดได้คือ:

string Query="SELECT * FROM Table1";
string Conditions = "";

if (condition1) Conditions+="AND Col1=0 ";
if (condition2) Conditions+="AND Col2=1 ";
if (condition3) Conditions+="AND Col3=2 ";

if (Conditions.Length > 0) 
  Query+=" WHERE " + Conditions.Substring(3);

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

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


3

ขึ้นอยู่กับเงื่อนไขอาจเป็นไปได้ที่จะใช้ตรรกะบูลีนในแบบสอบถาม สิ่งนี้:

string Query="SELECT * FROM Table1  " +
             "WHERE (condition1 = @test1 AND Col1=0) "+
             "AND (condition2 = @test2 AND Col2=1) "+
             "AND (condition3 = @test3 AND Col3=2) ";

3

ฉันชอบอินเทอร์เฟซที่คล่องแคล่วของ stringbuilder ดังนั้นฉันจึงสร้าง ExtensionMethods ขึ้นมา

var query = new StringBuilder()
    .AppendLine("SELECT * FROM products")
    .AppendWhereIf(!String.IsNullOrEmpty(name), "name LIKE @name")
    .AppendWhereIf(category.HasValue, "category = @category")
    .AppendWhere("Deleted = @deleted")
    .ToString();

var p_name = GetParameter("@name", name);
var p_category = GetParameter("@category", category);
var p_deleted = GetParameter("@deleted", false);
var result = ExecuteDataTable(query, p_name, p_category, p_deleted);


// in a seperate static class for extensionmethods
public StringBuilder AppendLineIf(this StringBuilder sb, bool condition, string value)
{
    if(condition)
        sb.AppendLine(value);
    return sb;
}

public StringBuilder AppendWhereIf(this StringBuilder sb, bool condition, string value)
{
    if (condition)
        sb.AppendLineIf(condition, sb.HasWhere() ? " AND " : " WHERE " + value);
    return sb;
}

public StringBuilder AppendWhere(this StringBuilder sb, string value)
{
    sb.AppendWhereIf(true, value);
    return sb;
}

public bool HasWhere(this StringBuilder sb)
{
    var seperator = new string [] { Environment.NewLine };
    var lines = sb.ToString().Split(seperator, StringSplitOptions.None);
    return lines.Count > 0 && lines[lines.Count - 1].Contains("where", StringComparison.InvariantCultureIgnoreCase);
}

// http://stackoverflow.com/a/4217362/98491
public static bool Contains(this string source, string toCheck, StringComparison comp)
{
    return source.IndexOf(toCheck, comp) >= 0;
}

2

IMHO ฉันคิดว่าแนวทางของคุณผิด:

การค้นหาฐานข้อมูลโดยการต่อสตริงไม่ใช่ความคิดที่ดี (เสี่ยงต่อการแทรก SQLและโค้ดอาจเสียหายได้ง่ายหากคุณทำการเปลี่ยนแปลงบางอย่างที่อื่น)

คุณสามารถใช้ORM (ฉันใช้NHibernate ) หรืออย่างน้อยก็ใช้SqlCommand.Parameters

หากคุณต้องการใช้การต่อสตริงอย่างแท้จริงฉันจะใช้ a StringBuilder(เป็นวัตถุที่เหมาะสมสำหรับการต่อสตริง):

var query = new StringBuilder("SELECT * FROM Table1 WHERE");
int qLength = query.Length;//if you don't want to count :D
if (Condition1) query.Append(" Col1=0 AND");
if (Condition2) query.Append(" Col2=0 AND");
....
//if no condition remove WHERE or AND from query
query.Length -= query.Length == qLength ? 6 : 4;

ตามความคิดสุดท้ายWhere 1=1น่าเกลียดมาก แต่SQL Serverจะปรับให้เหมาะสมต่อไป


SELECT * FROM Table1 WHERE AND Col1=0WHERE 1=1ดูเหมือนจะไม่ถูกต้องซึ่งเป็นจุดรวมของ
Mormegil

2

Dapper SqlBuilderเป็นตัวเลือกที่ดีทีเดียว มันยังใช้ในการผลิตบน StackOverflow

อ่านรายการบล็อกของแซมเกี่ยวกับเรื่องนี้

เท่าที่ฉันรู้มันไม่ได้เป็นส่วนหนึ่งของแพ็คเกจ Nuget ใด ๆ ดังนั้นคุณจะต้องคัดลอกวางโค้ดลงในโปรเจ็กต์ของคุณหรือดาวน์โหลดซอร์ส Dapper และสร้างโปรเจ็กต์ SqlBuilder ไม่ว่าจะด้วยวิธีใดคุณจะต้องอ้างอิง Dapper สำหรับDynamicParametersชั้นเรียนด้วย


1
ฉันไม่คิดว่า SqlBuilder ของ Dapper จะรวมอยู่ในแพ็คเกจนั้น
Ronnie Overby

1

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

ฉันพบว่ามันเป็นเรื่องธรรมดาและง่ายพอที่จะเข้าใจว่ามีคนตรวจสอบโค้ดของคุณ


1
public static class Ext
{
    public static string addCondition(this string str, bool condition, string statement)
    {
        if (!condition)
            return str;

        return str + (!str.Contains(" WHERE ") ? " WHERE " : " ") + statement;
    }

    public static string cleanCondition(this string str)
    {
        if (!str.Contains(" WHERE "))
            return str;

        return str.Replace(" WHERE AND ", " WHERE ").Replace(" WHERE OR ", " WHERE ");
    }
}

สำนึกด้วยวิธีการขยาย.

    static void Main(string[] args)
    {
        string Query = "SELECT * FROM Table1";

        Query = Query.addCondition(true == false, "AND Column1 = 5")
            .addCondition(18 > 17, "AND Column2 = 7")
            .addCondition(42 == 1, "OR Column3 IN (5, 7, 9)")
            .addCondition(5 % 1 > 1 - 4, "AND Column4 = 67")
            .addCondition(Object.Equals(5, 5), "OR Column5 >= 0")
            .cleanCondition();

        Console.WriteLine(Query);
    }

ไปกับธัญพืช!
Ronnie Overby

ขอโทษ? du หมายถึงอะไร?
Maxim Zhukov

0

การใช้stringฟังก์ชันคุณสามารถทำได้ด้วยวิธีนี้:

string Query = "select * from Table1";

if (condition1) WhereClause += " Col1 = @param1 AND "; // <---- put conditional operator at the end
if (condition2) WhereClause += " Col1 = @param2 OR ";

WhereClause = WhereClause.Trim();

if (!string.IsNullOrEmpty(WhereClause))
    Query = Query + " WHERE " + WhereClause.Remove(WhereClause.LastIndexOf(" "));
// else
// no condition meets the criteria leave the QUERY without a WHERE clause  

โดยส่วนตัวแล้วฉันรู้สึกง่ายที่จะลบองค์ประกอบเงื่อนไขในตอนท้ายเนื่องจากตำแหน่งของมันง่ายต่อการทำนาย


0

ฉันคิดหาวิธีแก้ปัญหาที่บางทีอาจจะอ่านได้ง่ายกว่า:

string query = String.Format("SELECT * FROM Table1 WHERE "
                             + "Col1 = {0} AND "
                             + "Col2 = {1} AND "
                             + "Col3 = {2}",
                            (!condition1 ? "Col1" : "0"),
                            (!condition2 ? "Col2" : "1"),
                            (!condition3 ? "Col3" : "2"));

ฉันไม่แน่ใจว่าตัวแปล SQL จะปรับCol1 = Col1เงื่อนไขให้เหมาะสมหรือไม่ (พิมพ์เมื่อcondition1เป็นเท็จ)


0

นี่คือวิธีที่หรูหรากว่า:

    private string BuildQuery()
    {
        string MethodResult = "";
        try
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("SELECT * FROM Table1");

            List<string> Clauses = new List<string>();

            Clauses.Add("Col1 = 0");
            Clauses.Add("Col2 = 1");
            Clauses.Add("Col3 = 2");

            bool FirstPass = true;

            if(Clauses != null && Clauses.Count > 0)
            {
                foreach(string Clause in Clauses)
                {
                    if (FirstPass)
                    {
                        sb.Append(" WHERE ");

                        FirstPass = false;

                    }
                    else
                    {
                        sb.Append(" AND ");

                    }

                    sb.Append(Clause);

                }

            }

            MethodResult = sb.ToString();

        }
        catch //(Exception ex)
        {
            //ex.HandleException()
        }
        return MethodResult;
    }

0

ที่ได้รับการกล่าวว่าการสร้าง SQL โดยเรียงต่อกันไม่เคยมีความคิดที่ดี ไม่ใช่เพียงเพราะการฉีด SQL ส่วนใหญ่เป็นเพราะมันเป็นเพียงแค่น่าเกลียดยากที่จะรักษาและไม่จำเป็นทั้งหมด คุณต้องรันโปรแกรมของคุณด้วยการติดตามหรือดีบักเพื่อดูว่า SQL สร้างอะไร หากคุณใช้QueryFirst (ข้อจำกัดความรับผิดชอบ: ที่ฉันเขียน) สิ่งล่อใจที่ไม่พึงพอใจจะถูกลบออกและคุณสามารถทำได้โดยตรงใน SQL

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

select * from table1
where (col1 = @param1 or @param1 is null)
and (col2 = @param2 or @param2 is null)
and (col3 = @param3 or @param3 is null)
OPTION (RECOMPILE)

QueryFirst ให้ C # null เป็น db NULL ดังนั้นคุณจึงเรียกใช้เมธอด Execute () ด้วย null ตามความเหมาะสมและทุกอย่างก็ใช้ได้ <opinion> เหตุใด C # dev จึงลังเลที่จะทำสิ่งต่างๆใน SQL แม้ว่าจะง่ายกว่าก็ตาม เปลี่ยนใจ </opinion>


0

สำหรับขั้นตอนการกรองที่ยาวขึ้น StringBuilder เป็นแนวทางที่ดีกว่าอย่างที่หลาย ๆ คนบอก

ในกรณีของคุณฉันจะไปกับ:

StringBuilder sql = new StringBuilder();

if (condition1) 
    sql.Append("AND Col1=0 ");
if (condition2) 
    sql.Append("AND Col2=1 ");
if (condition3) 
    sql.Append("AND Col3=2 ");

string Query = "SELECT * FROM Table1 ";
if(sql.Length > 0)
 Query += string.Concat("WHERE ", sql.ToString().Substring(4)); //avoid first 4 chars, which is the 1st "AND "

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.