มันถือว่าเป็นรูปแบบการต่อต้านการเขียน SQL ในซอร์สโค้ดหรือไม่?


87

มันถือว่าเป็นรูปแบบการต่อต้านการ hardcode SQL ในแอปพลิเคชันเช่นนี้หรือไม่:

public List<int> getPersonIDs()
{    
    List<int> listPersonIDs = new List<int>();
    using (SqlConnection connection = new SqlConnection(
        ConfigurationManager.ConnectionStrings["Connection"].ConnectionString))
    using (SqlCommand command = new SqlCommand())
    {
        command.CommandText = "select id from Person";
        command.Connection = connection;
        connection.Open();
        SqlDataReader datareader = command.ExecuteReader();
        while (datareader.Read())
        {
            listPersonIDs.Add(Convert.ToInt32(datareader["ID"]));
        }
    }
    return listPersonIDs;
}

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

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

  1. ใช้ LINQ
    หรือ
  2. ใช้กระบวนงานที่เก็บไว้สำหรับ SQL

ฉันถูกไหม? มันถือว่าเป็นรูปแบบการต่อต้านการเขียน SQL ในซอร์สโค้ดหรือไม่? เราเป็นทีมเล็ก ๆ ที่ทำงานในโครงการนี้ ประโยชน์ของการจัดเก็บฉันคิดว่าเป็นนักพัฒนา SQL สามารถมีส่วนร่วมกับกระบวนการพัฒนา (การเขียนขั้นตอนการจัดเก็บ ฯลฯ )

แก้ไขที่ พูดถึงการเชื่อมโยงต่อไปนี้เกี่ยวกับคำสั่ง SQL เขียนยาก: https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/hard-coded-sql-statements มีประโยชน์ในการจัดทำคำสั่ง SQL หรือไม่


31
"ใช้ Linq" และ "ใช้กระบวนงานที่เก็บไว้" ไม่ใช่เหตุผล; พวกเขาเป็นเพียงข้อเสนอแนะ รอสองสัปดาห์แล้วถามเขาด้วยเหตุผล
Robert Harvey

47
เครือข่าย Stack Exchange ใช้ micro-ORM เรียกว่า Dapper ฉันคิดว่ามันสมเหตุสมผลที่จะบอกว่าส่วนใหญ่ของรหัส Dapper คือ "hardcoded SQL" (มากหรือน้อย) ดังนั้นหากเป็นการปฏิบัติที่ไม่ดีก็เป็นวิธีปฏิบัติที่ไม่ดีซึ่งได้รับการยอมรับจากหนึ่งในแอปพลิเคชันเว็บที่โดดเด่นที่สุดในโลก
Robert Harvey

16
ในการตอบคำถามของคุณเกี่ยวกับที่เก็บ SQL แบบตายตัวยังคงเป็น SQL แบบตายตัวไม่ว่าคุณจะใส่ไว้ที่ไหน ความแตกต่างคือที่เก็บให้คุณเป็นสถานที่ในการแค็ปซูล SQL ที่ตายตัว เป็นชั้นของนามธรรมที่ซ่อนรายละเอียดของ SQL จากส่วนที่เหลือของโปรแกรม
Robert Harvey

26
ไม่ SQL ภายในรหัสไม่ใช่รูปแบบการต่อต้าน แต่นั่นเป็นจำนวนมากของรหัสจานหม้อไอน้ำสำหรับแบบสอบถาม SQL ง่าย
GrandmasterB

45
มีความแตกต่างระหว่าง 'ในซอร์สโค้ด' และ 'กระจายไปทั่วซอร์สโค้ด'
2560

คำตอบ:


112

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

ผลลัพธ์คือ: SQL นั้นใช้ได้ในเลเยอร์การคงอยู่ที่เฉพาะกับเทคโนโลยี SQL (เช่น SQL นั้นใช้ได้ใน a SQLCustomerRepositoryแต่ไม่ใช่ใน a MongoCustomerRepository) นอกเลเยอร์คงอยู่, SQL แบ่ง abstraction ของคุณและถือว่าเป็นการปฏิบัติที่แย่มาก (โดยฉัน)

สำหรับเครื่องมืออย่าง LINQ หรือ JPQL: สิ่งเหล่านี้สามารถแยกรสชาติของ SQL ออกมาได้ การมี LINQ-Code หรือ JPQL เคียวรีภายนอกที่เก็บแบ่งการคงอยู่ของ abstraction เช่นเดียวกับ SQL ดิบ


ข้อดีอีกอย่างหนึ่งของเลเยอร์การคงอยู่ที่แยกต่างหากคือมันช่วยให้คุณสามารถ unittest รหัสตรรกะทางธุรกิจของคุณโดยไม่ต้องตั้งค่าเซิร์ฟเวอร์ DB

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

ในสถาปัตยกรรม MVC + Service นี่เป็นงานง่าย ๆ ของการเยาะเย้ยอินสแตนซ์ที่เก็บสร้างข้อมูลจำลองในหน่วยความจำและกำหนดว่าที่เก็บควรส่งคืนข้อมูลจำลองนั้นเมื่อเรียกใช้ getter ที่แน่นอน จากนั้นคุณสามารถกำหนดข้อมูลทดสอบต่อ unittest และไม่ต้องกังวลกับการล้างฐานข้อมูลในภายหลัง

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


80
แนวคิดที่ว่า "เราสามารถเปลี่ยนเทคโนโลยีการคงอยู่" นั้นไม่สมจริงและไม่จำเป็นในโครงการส่วนใหญ่ของโลกแห่งความเป็นจริง ฐานข้อมูล SQL / เชิงสัมพันธ์เป็นสัตว์ที่แตกต่างจากฐานข้อมูล NoSQL อย่าง MongoDB อย่างสิ้นเชิง ดูบทความโดยละเอียดเกี่ยวกับเรื่องนี้ ในที่สุดโครงการที่เริ่มใช้ NoSQL ในที่สุดก็จะตระหนักถึงความผิดพลาดของพวกเขาแล้วเปลี่ยนไปใช้ RDBMS จากประสบการณ์ของฉันการอนุญาตให้ใช้ Query Language เชิงวัตถุโดยตรง (เช่น JPA-QL) จากรหัสธุรกิจให้ผลลัพธ์ที่ดีที่สุด
Rogério

6
จะเกิดอะไรขึ้นถ้ามีโอกาสน้อยมากที่ชั้นการคงอยู่จะเปลี่ยนไป จะเกิดอะไรขึ้นถ้าไม่มีการแมปแบบหนึ่งต่อหนึ่งเมื่อเปลี่ยนเลเยอร์การคงอยู่ อะไรคือข้อดีของการเพิ่มระดับของนามธรรม?
pllee

19
@ Rogérioการโยกย้ายจากที่เก็บ NoSQL ไปเป็น RDBMS หรือในทางกลับกันเป็นไปตามที่คุณพูดว่าอาจไม่สมจริง (สมมติว่าตัวเลือกเทคโนโลยีนั้นเหมาะสมที่จะเริ่มต้นด้วย) อย่างไรก็ตามฉันมีส่วนเกี่ยวข้องกับโครงการในโลกแห่งความจริงจำนวนหนึ่งซึ่งเราย้ายจาก RDBMS หนึ่งไปยังอีกโครงการหนึ่ง ในสถานการณ์นั้นเลเยอร์การคงอยู่ในแพ็กเกจนั้นเป็นข้อดี
David

6
"ความคิดที่ว่า" เราสามารถเปลี่ยนเทคโนโลยีการคงอยู่ "นั้นไม่สมจริงและไม่จำเป็นในโครงการส่วนใหญ่ในโลกแห่งความจริง" บริษัท ของฉันเคยเขียนโค้ดตามข้อสันนิษฐานนั้น เราต้องปรับเปลี่ยน codebase ทั้งหมดเพื่อรองรับไม่ใช่หนึ่งเดียว แต่ระบบจัดเก็บ / สืบค้นที่แตกต่างกันทั้งหมดสามระบบและกำลังพิจารณาที่สามและสี่ ถึงแม้ว่าเราจะมีเพียงฐานข้อมูลเดียว แต่การขาดการรวมก็นำไปสู่การทำบาปทุกชนิด
NPSF3000

3
@ Rogérioคุณรู้หรือไม่ว่า "โครงการส่วนใหญ่ในโลกแห่งความเป็นจริง"? ใน บริษัท ของฉันแอปพลิเคชันส่วนใหญ่สามารถทำงานร่วมกับหนึ่งใน DBMS ทั่วไปและเป็นประโยชน์อย่างมากเนื่องจากลูกค้าของเราส่วนใหญ่มีข้อกำหนดที่ไม่เกี่ยวกับการทำงาน
BartoszKP

55

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

  • ชั้นในใบสมัครของคุณมีความรับผิดชอบใด

  • ชั้นที่ฟังก์ชั่นดังกล่าวมาจาก

ไม่มีวิธีแก้ปัญหา "one-size-fits-all" นี้ ในบางแอปพลิเคชันนักออกแบบต้องการใช้กรอบ ORM และให้กรอบสร้าง SQL ทั้งหมด ในบางแอปพลิเคชันนักออกแบบต้องการจัดเก็บ SQL ดังกล่าวโดยเฉพาะในขั้นตอนการจัดเก็บ สำหรับบางแอปพลิเคชันจะมีเลเยอร์การคงอยู่ (หรือพื้นที่เก็บข้อมูล) ที่เขียนด้วยมือซึ่ง SQL อาศัยอยู่และสำหรับแอปพลิเคชันอื่น ๆ มันก็โอเคที่จะกำหนดข้อยกเว้นภายใต้สถานการณ์บางอย่างจากการวาง SQL

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


1
ฉันได้แก้ไขคำถาม ไม่แน่ใจว่ามันหายไปแสงใด ๆ
w0051977

18
@ w0051977: ลิงค์ที่คุณโพสต์ไม่ได้บอกอะไรเราเกี่ยวกับใบสมัครของคุณใช่ไหม? ดูเหมือนว่าคุณกำลังมองหากฎง่ายๆที่จะวาง SQL หรือไม่เหมาะกับทุกแอปพลิเคชัน ไม่มีเลย นี่คือการตัดสินใจออกแบบที่คุณต้องทำเกี่ยวกับแอปพลิเคชันของคุณ (อาจรวมกับทีมของคุณ)
Doc Brown

7
+1 โดยเฉพาะอย่างยิ่งฉันจะทราบว่าไม่มีความแตกต่างจริงระหว่าง SQL ในวิธีการและ SQL ในขั้นตอนการจัดเก็บในแง่ของวิธี "hard coded" บางสิ่งบางอย่างคือหรือวิธีนำมาใช้ใหม่, บำรุงรักษาได้ ฯลฯ สิ่งที่เป็นนามธรรม ขั้นตอนสามารถทำได้ด้วยวิธีการ แน่นอนว่ากระบวนการนั้นมีประโยชน์ แต่กฎการท่องจำ "เราไม่สามารถมี SQL ในวิธีการดังนั้นให้ใส่ทุกอย่างไว้ในขั้นตอนการ" อาจจะผลิต cruft จำนวนมากที่มีประโยชน์เพียงเล็กน้อย

1
@ user82096 ฉันจะพูดโดยทั่วไปว่าการใส่รหัสการเข้าถึงฐานข้อมูลใน SP นั้นเป็นอันตรายในเกือบทุกโครงการที่ฉันเห็น (สแต็ก MS) นอกเหนือจากการลดประสิทธิภาพตามจริง (แคชแผนปฏิบัติการที่เข้มงวด) การบำรุงรักษาระบบทำได้ยากขึ้นโดยแยกตรรกะออกจากกันและ SP ดูแลรักษาโดยกลุ่มคนที่แตกต่างจากผู้พัฒนาตรรกะแอป โดยเฉพาะอย่างยิ่งใน Azure ที่การเปลี่ยนรหัสระหว่าง Deployment Slots นั้นเหมือนกับการทำงานไม่กี่วินาที แต่การโยกย้ายสคีมาที่ไม่ใช่โค้ดแรก / db- แรกระหว่างสล็อตนั้นค่อนข้างจะทำให้ปวดหัวในการดำเนินงาน
Sentinel

33

Marstato ให้คำตอบที่ดี แต่ฉันต้องการเพิ่มความเห็นเพิ่มเติม

SQL in source ไม่ใช่ anti-pattern แต่สามารถทำให้เกิดปัญหาได้ ฉันจำได้เมื่อคุณต้องใส่แบบสอบถาม SQL ลงในคุณสมบัติของส่วนประกอบที่ลดลงในทุกรูปแบบ นั่นทำให้สิ่งต่าง ๆ น่าเกลียดอย่างรวดเร็วและคุณต้องข้ามห่วงเพื่อค้นหาคำสั่ง ฉันกลายเป็นผู้สนับสนุนที่แข็งแกร่งของการรวมศูนย์การเข้าถึงฐานข้อมูลให้มากที่สุดเท่าที่จะทำได้ภายใต้ข้อ จำกัด ของภาษาที่ฉันทำงานด้วย เพื่อนร่วมงานของคุณอาจย้อนเวลากลับไปสู่ยุคมืด

ตอนนี้ความคิดเห็นบางส่วนกำลังพูดถึงการล็อคอินของผู้ขายราวกับว่ามันเป็นสิ่งที่ไม่ดีโดยอัตโนมัติ มันไม่ใช่ หากฉันลงนามในการตรวจสอบรูปหกในแต่ละปีเพื่อใช้ Oracle คุณสามารถเดิมพันได้ว่าฉันต้องการให้แอปพลิเคชันใด ๆ ที่เข้าถึงฐานข้อมูลนั้นใช้ไวยากรณ์ของ Oracle เพิ่มเติมอย่างเหมาะสมแต่เต็มไปด้วย 'มัน ฉันจะไม่มีความสุขถ้าฐานข้อมูลที่แวววาวของฉันพิการโดย coders ที่เขียนวานิลลา ANSI SQL ไม่ดีเมื่อมี "วิธี Oracle" ในการเขียน SQL ที่ไม่ทำลายฐานข้อมูล ใช่การเปลี่ยนแปลงฐานข้อมูลจะยากขึ้น แต่ฉันเคยเห็นมันทำที่ไซต์ลูกค้าขนาดใหญ่สองสามครั้งในรอบกว่า 20 ปีและหนึ่งในกรณีเหล่านั้นย้ายจาก DB2 -> Oracle เพราะเมนเฟรมที่โฮสต์ DB2 ล้าสมัยและเลิกใช้งานแล้ว . ใช่แล้วนั่นคือการล็อคอินของผู้ขาย แต่สำหรับลูกค้าองค์กรมันเป็นที่พึงปรารถนาที่จะจ่ายสำหรับ RDBMS ที่มีความสามารถราคาแพงเช่น Oracle หรือ Microsoft SQL Server จากนั้นใช้ประโยชน์ให้เต็มที่ คุณมีข้อตกลงการสนับสนุนเป็นผ้าห่มความสะดวกสบายของคุณ ถ้าฉันจ่ายเงินให้กับฐานข้อมูลที่มีการใช้งานโพรซีเดอร์ที่เก็บไว้มากมาย

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

ไม่มีข้อแก้ตัวไม่ซ่อนตัวอยู่หลังกำแพง Hibernate ORMs ไม่ดีใช้สามารถจริงๆทำลายประสิทธิภาพของการใช้งาน ฉันจำได้ว่าเห็นคำถามเกี่ยวกับ Stack Overflow เมื่อไม่กี่ปีที่ผ่านมาตามแนวของ:

ใน Hibernate ฉันกำลังทำซ้ำถึง 250,000 ระเบียนการตรวจสอบค่าของคุณสมบัติสองอย่างและอัปเดตวัตถุที่ตรงกับเงื่อนไขบางอย่าง มันใช้งานช้าฉันจะทำอะไรได้บ้างเพื่อเร่งความเร็ว

วิธีการเกี่ยวกับ "ตารางการปรับปรุง SET field1 = โดยที่ field2 เป็น True และ Field3> 100" การสร้างและกำจัดวัตถุ 250,000 รายการอาจเป็นปัญหาของคุณ ...

ie ละเว้นไฮเบอร์เนตเมื่อไม่เหมาะสมที่จะใช้ ทำความเข้าใจกับฐานข้อมูล

ดังนั้นโดยสรุปการฝัง SQL ในโค้ดอาจเป็นแนวปฏิบัติที่ไม่ดี แต่มีสิ่งที่แย่กว่านั้นคือคุณสามารถพยายามหลีกเลี่ยงการฝัง SQL ได้


5
ฉันไม่ได้ระบุว่า "การล็อคอินของผู้ขาย" เป็นสิ่งที่ไม่ดี ฉันกล่าวว่ามันมาพร้อมกับการแลกเปลี่ยน ในตลาดทุกวันนี้ที่มี db เป็น xaaS เช่นสัญญาและไลเซนส์เก่าสำหรับการรันฐานข้อมูลแบบโฮสด้วยตนเองนั้นจะหายากขึ้นเรื่อย ๆ วันนี้มันง่ายกว่ามากที่จะเปลี่ยน db จากผู้ขาย A เป็น B เมื่อเทียบกับเมื่อ 10 ปีที่แล้ว หากคุณอ่านความคิดเห็นฉันไม่ชอบที่จะใช้ประโยชน์จากคุณสมบัติใด ๆ ของการจัดเก็บข้อมูล ดังนั้นจึงเป็นเรื่องง่ายที่จะใส่รายละเอียดผู้ขายเฉพาะลงในสิ่งที่ (แอพ) หมายถึงการเป็นผู้ไม่เชื่อเรื่องพระเจ้า เรามุ่งมั่นที่จะติดตาม SOLiD เพียงแค่วางรหัสเพื่อเก็บข้อมูลเดียว ไม่ใช่เรื่องใหญ่
Laiv

2
นี่คือคำตอบที่ดี บ่อยครั้งที่แนวทางที่ถูกต้องคือแนวทางที่นำไปสู่สถานการณ์ที่เลวร้ายที่สุดหากมีการเขียนรหัสที่แย่ที่สุด รหัส ORM ที่แย่ที่สุดที่เป็นไปได้อาจจะแย่กว่า SQL แบบฝังตัวที่แย่ที่สุด
jwg

@ คะแนนที่ดีของ Laiv PaaS เป็นตัวเปลี่ยนเกม - ฉันต้องคิดเกี่ยวกับความหมายเพิ่มเติม
2560

3
@jwg ฉันจะมีความโน้มเอียงที่จะบอกว่ารหัสข้างต้นออมเฉลี่ยอยู่ที่เลวร้ายยิ่งกว่าต่ำกว่าค่าเฉลี่ยของ SQL ฝัง :)
mcottle

@macottle สมมติว่า SQL ฝังตัวด้านล่างโดยเฉลี่ยถูกเขียนโดย ppl เหนือกว่าค่าเฉลี่ยของผู้เขียน ORM สิ่งที่ฉันสงสัยมันมาก ผู้พัฒนาหลายรายไม่สามารถเขียนส่วนที่เหลือของ JOIN ได้ให้ตรวจสอบ SO ก่อน
Laiv

14

ใช่การเข้ารหัสสตริง SQL ลงในโค้ดของแอปพลิเคชั่นโดยทั่วไปเป็นรูปแบบการต่อต้าน

ลองแยกความอดทนที่เราได้พัฒนามานานหลายปีเพื่อดูสิ่งนี้ในรหัสการผลิต การผสมภาษาที่แตกต่างอย่างสมบูรณ์กับไวยากรณ์ที่แตกต่างกันในไฟล์เดียวกันโดยทั่วไปไม่ใช่เทคนิคการพัฒนาที่ต้องการ สิ่งนี้แตกต่างจากภาษาแม่แบบเช่นมีดโกนซึ่งออกแบบมาเพื่อให้ความหมายเชิงบริบทกับหลายภาษา ตามที่Sava B.ระบุไว้ในความคิดเห็นด้านล่าง SQL ในภาษา C # ของคุณหรือภาษาแอปพลิเคชันอื่น (Python, C ++ ฯลฯ ) เป็นสตริงที่เหมือนกันและไม่มีความหมาย เช่นเดียวกันเมื่อใช้มากกว่าหนึ่งภาษาในกรณีส่วนใหญ่แม้ว่าจะมีสถานการณ์ที่เห็นได้ชัดว่ายอมรับได้เช่น inline assembly ใน C ตัวอย่างเล็ก ๆ และเข้าใจได้ของ CSS ใน HTML (สังเกตว่า CSS ถูกออกแบบให้ผสมกับ HTML ), และคนอื่น ๆ.

ทำความสะอาดรหัสโดย Robert C. Martin, pg.  288 (โรเบิร์ตซี. มาร์ตินเกี่ยวกับภาษาผสม, รหัสสะอาด , บทที่ 17, "รหัสกลิ่นและการวิเคราะห์ปัญหา", หน้า 288)

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

  • ตรรกะฐานข้อมูลยากต่อการค้นหา คุณค้นหาอะไรเพื่อค้นหาคำสั่ง SQL ทั้งหมดของคุณ? มากับ "SELECT", "UPDATE", "MERGE" ฯลฯ ไหม?
  • การปรับโครงสร้างการใช้ SQL เดียวกันหรือคล้ายกันกลายเป็นเรื่องยาก
  • การเพิ่มการรองรับฐานข้อมูลอื่นนั้นทำได้ยาก เราจะทำสิ่งนี้ให้สำเร็จได้อย่างไร? เพิ่มถ้า .. แล้วคำสั่งสำหรับแต่ละฐานข้อมูลและเก็บแบบสอบถามทั้งหมดเป็นสตริงในวิธีการหรือไม่
  • นักพัฒนาอ่านคำแถลงในภาษาอื่นและหันเหความสนใจโดยการเปลี่ยนจุดสนใจจากจุดประสงค์ของวิธีการเป็นรายละเอียดการใช้งานของวิธีการ
  • ในขณะที่หนึ่ง liners อาจไม่มากเกินไปปัญหาสตริง SQL แบบอินไลน์เริ่มที่จะกระจุยเป็นงบที่ซับซ้อนมากขึ้น คุณจะทำอย่างไรกับคำสั่งบรรทัด 113? ใส่ 113 บรรทัดทั้งหมดในวิธีการของคุณ?
  • นักพัฒนาทำการย้ายแบบสอบถามไปมาระหว่างตัวแก้ไข SQL (SSMS, SQL Developer, etc. ) และซอร์สโค้ดอย่างมีประสิทธิภาพได้อย่างไร @คำนำหน้าของ C # ทำให้การดำเนินการนี้ง่ายขึ้น แต่ฉันได้เห็นโค้ดจำนวนมากที่อ้างอิงแต่ละบรรทัด SQL และหนีการขึ้นบรรทัดใหม่ "SELECT col1, col2...colN"\ "FROM painfulExample"\ "WHERE maintainability IS NULL"\ "AND modification.effort > @necessary"\
  • อักขระการเยื้องที่ใช้เพื่อจัดแนว SQL กับโค้ดแอ็พพลิเคชันโดยรอบจะถูกส่งผ่านเครือข่ายด้วยการประมวลผลแต่ละครั้ง นี่อาจไม่สำคัญสำหรับแอปพลิเคชั่นขนาดเล็ก แต่สามารถเพิ่มได้เมื่อการใช้งานของซอฟต์แวร์เพิ่มขึ้น

ORM เต็มรูปแบบ (ตัวแม็พ Object-Relational เช่น Entity Framework หรือ Hibernate) สามารถกำจัด SQL ที่ใช้ peppered แบบสุ่มในโค้ดแอปพลิเคชัน การใช้งาน SQL และไฟล์ทรัพยากรของฉันเป็นเพียงตัวอย่าง ORMs คลาสผู้ช่วยและอื่น ๆ ทั้งหมดสามารถช่วยให้บรรลุเป้าหมายของโค้ดที่สะอาดขึ้น

ดังที่ Kevin กล่าวไว้ในคำตอบก่อนหน้านี้ SQL ในโค้ดสามารถยอมรับได้ในโครงการขนาดเล็ก แต่โครงการขนาดใหญ่เริ่มต้นจากโครงการขนาดเล็กและความน่าจะเป็นที่ทีมส่วนใหญ่จะกลับไปทำในสิ่งที่ถูกต้องมักจะแปรผกผัน

มีหลายวิธีที่ง่ายในการเก็บ SQL ไว้ในโครงการ หนึ่งในวิธีการที่ฉันมักใช้คือการใส่คำสั่ง SQL แต่ละคำลงในไฟล์ทรัพยากร Visual Studio โดยปกติจะมีชื่อว่า "sql" ไฟล์ข้อความเอกสาร JSON หรือแหล่งข้อมูลอื่นอาจมีเหตุผลขึ้นอยู่กับเครื่องมือของคุณ ในบางกรณีคลาสแยกต่างหากที่อุทิศให้กับการเชื่อมสตริง SQL อาจเป็นตัวเลือกที่ดีที่สุด แต่อาจมีบางประเด็นที่อธิบายไว้ข้างต้น

ตัวอย่าง SQL: รูปลักษณ์ใดที่สวยงามกว่า:

using(DbConnection connection = Database.SystemConnection()) {
    var eyesoreSql = @"
    SELECT
        Viewable.ViewId,
        Viewable.HelpText,
        PageSize.Width,
        PageSize.Height,
        Layout.CSSClass,
        PaginationType.GroupingText
    FROM Viewable
    LEFT JOIN PageSize
        ON PageSize.Id = Viewable.PageSizeId
    LEFT JOIN Layout
        ON Layout.Id = Viewable.LayoutId
    LEFT JOIN Theme
        ON Theme.Id = Viewable.ThemeId
    LEFT JOIN PaginationType
        ON PaginationType.Id = Viewable.PaginationTypeId
    LEFT JOIN PaginationMenu
        ON PaginationMenu.Id = Viewable.PaginationMenuId
    WHERE Viewable.Id = @Id
    ";
    var results = connection.Query<int>(eyesoreSql, new { Id });
}

กลายเป็น

using(DbConnection connection = Database.SystemConnection()) {
    var results = connection.Query<int>(sql.GetViewable, new { Id });
}

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

SQL ในทรัพยากร

วิธีการง่ายๆนี้ดำเนินการค้นหาแบบแยกเดี่ยว จากประสบการณ์ของฉันผลประโยชน์จะเพิ่มขึ้นเมื่อใช้ "ภาษาต่างประเทศ" เพิ่มความซับซ้อนมากขึ้น การใช้ไฟล์ทรัพยากรของฉันเป็นเพียงตัวอย่าง วิธีการที่แตกต่างกันอาจเหมาะสมกว่าขึ้นอยู่กับภาษา (SQL ในกรณีนี้) และแพลตฟอร์ม

วิธีนี้และวิธีอื่น ๆ แก้ไขรายการข้างต้นในลักษณะดังต่อไปนี้:

  1. รหัสฐานข้อมูลนั้นง่ายต่อการค้นหาเนื่องจากมีการรวมศูนย์ไว้แล้ว ในโครงการขนาดใหญ่กลุ่มเช่น-SQL SQLเป็นไฟล์แยกต่างหากบางทีภายใต้โฟลเดอร์ชื่อ
  2. รองรับฐานข้อมูลที่สอง, สามและอื่น ๆ ได้ง่ายขึ้น สร้างอินเทอร์เฟซ (หรือภาษาอื่น ๆ ที่เป็นนามธรรม) ที่ส่งคืนคำสั่งเฉพาะของฐานข้อมูลแต่ละตัว การใช้งานสำหรับแต่ละฐานข้อมูลจะน้อยกว่าคำสั่งที่คล้ายกับ: return SqlResource.DoTheThing;จริงการใช้งานเหล่านี้สามารถข้ามทรัพยากรและมี SQL ในสตริง แต่ปัญหาบางอย่าง (ไม่ใช่ทั้งหมด) ข้างต้นจะยังคงปรากฏ
  3. การปรับโครงสร้างใหม่นั้นง่าย - เพียงใช้ทรัพยากรเดียวกันซ้ำ คุณยังสามารถใช้รายการทรัพยากรเดียวกันสำหรับระบบ DBMS ที่แตกต่างกันได้บ่อยครั้งด้วยคำสั่งรูปแบบบางอย่าง ฉันทำสิ่งนี้บ่อยๆ
  4. การใช้ภาษาทุติยภูมิสามารถใช้ชื่อที่สื่อความหมายเช่นsql.GetOrdersForAccountแทนที่จะเป็นป้านมากกว่าSELECT ... FROM ... WHERE...
  5. คำสั่ง SQL ถูกเรียกด้วยหนึ่งบรรทัดโดยไม่คำนึงถึงขนาดและความซับซ้อน
  6. SQL สามารถคัดลอกและวางระหว่างเครื่องมือฐานข้อมูลเช่น SSMS และ SQL Developer โดยไม่ต้องดัดแปลงหรือคัดลอกอย่างระมัดระวัง ไม่มีเครื่องหมายคำพูด ไม่มีแบ็กสแลชต่อท้าย ในกรณีของตัวแก้ไขทรัพยากร Visual Studio โดยเฉพาะคลิกเดียวเน้นคำสั่ง SQL CTRL + C แล้ววางลงในตัวแก้ไข SQL

การสร้าง SQL ในทรัพยากรนั้นรวดเร็วดังนั้นจึงมีแรงกระตุ้นเล็กน้อยในการผสมการใช้ทรัพยากรกับ SQL-in-code

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


13
ฉันคิดว่าสิ่งนี้เน้นมากเกินไปในการวิจารณ์ที่ไม่ดีของการมี SQL ในซอร์สโค้ด การมีสองภาษาที่แตกต่างกันในไฟล์เดียวกันนั้นไม่น่ากลัว ขึ้นอยู่กับหลายสิ่งหลายอย่างเช่นความสัมพันธ์และการนำเสนอแต่ละครั้ง
jwg

9
"การผสมภาษาที่แตกต่างอย่างสิ้นเชิงกับไวยากรณ์ที่แตกต่างในไฟล์เดียวกัน" นี่เป็นข้อโต้แย้งที่แย่มาก มันจะใช้กับเอ็นจิ้นมุมมองมีดโกนและ HTML, CSS และ Javascript รักนิยายภาพของคุณ!
Ian Newson

4
นี่แค่เพิ่มความซับซ้อนที่อื่น ใช่รหัสเดิมของคุณสะอาดกว่าเดิมโดยเพิ่มทางอ้อมที่นักพัฒนาต้องไปเพื่อการบำรุงรักษา เหตุผลที่แท้จริงที่คุณต้องทำคือถ้าใช้คำสั่ง SQL แต่ละคำสั่งในหลาย ๆ ที่ในรหัส ด้วยวิธีดังกล่าวจึงไม่แตกต่างจากการปรับโครงสร้างปกติอื่น ๆ ("วิธีการแยก")
Robert Harvey

3
เพื่อความชัดเจนนี่ไม่ใช่คำตอบสำหรับคำถาม "ฉันควรทำอย่างไรกับสตริง SQL" เท่าที่เป็นคำตอบของคำถาม "ฉันต้องทำอย่างไรกับคอลเลกชันของสตริงที่ซับซ้อนเพียงพอ" ซึ่งเป็นคำถาม คำตอบของคุณพูดตรงไปตรงมา
Robert Harvey

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

13

มันขึ้นอยู่กับ. มีหลายวิธีที่สามารถทำงานได้:

  1. ทำให้ SQL เป็นโมดูล, และแยกมันออกเป็นชุดของคลาส, ฟังก์ชั่น, หรืออะไรก็ตามที่หน่วยนามธรรมของกระบวนทัศน์ของคุณใช้, จากนั้นเรียกมันด้วยตรรกะแอปพลิเคชัน.
  2. ย้าย SQL ที่ซับซ้อนทั้งหมดไปยังมุมมองจากนั้นทำ SQL ที่ง่ายมากในตรรกะของแอปพลิเคชันเท่านั้นเพื่อที่คุณจะได้ไม่ต้องทำให้เป็นโมดูล
  3. ใช้ไลบรารีการทำแผนที่วัตถุสัมพันธ์
  4. ใช่, เพียงแค่เขียน SQL โดยตรงในแอปพลิเคชันตรรกะ.

ตามปกติแล้วหากโครงการของคุณเลือกหนึ่งในเทคนิคเหล่านี้แล้วคุณควรสอดคล้องกับส่วนที่เหลือของโครงการ

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

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

(2) ไม่ค่อยเห็นในประสบการณ์ของฉัน ปัญหาคือร้านค้าหลายแห่งห้ามไม่ให้มีการแก้ไข SWE โดยตรงกับสคีมาฐานข้อมูล (เพราะ "นั่นเป็นหน้าที่ของ DBA") สิ่งนี้ไม่จำเป็นต้องเป็นสิ่งเลวร้ายในตัวมันเอง การเปลี่ยนแปลงแบบแผนมีความเป็นไปได้ที่สำคัญสำหรับการทำลายสิ่งต่าง ๆ และอาจจำเป็นต้องมีการพิจารณาอย่างรอบคอบ อย่างไรก็ตามเพื่อให้ (2) ทำงาน SWEs อย่างน้อยควรสามารถแนะนำมุมมองใหม่และปรับเปลี่ยนแบบสอบถามการสำรองข้อมูลของมุมมองที่มีอยู่ด้วยขั้นต่ำหรือไม่มีระบบราชการ หากไม่ใช่ในกรณีที่คุณจ้างงาน (2) อาจไม่เหมาะกับคุณ

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

สำหรับการอ่านโพรซีเดอร์ที่เก็บนั้นเป็นเพียงเวอร์ชัน crappier ของ (2) ฉันไม่แนะนำพวกเขาในฐานะดังกล่าว แต่คุณอาจต้องการเขียนไว้หากฐานข้อมูลของคุณไม่รองรับมุมมองที่อัปเดตได้หรือถ้าคุณต้องการทำอะไรที่ซับซ้อนกว่าการแทรกหรืออัปเดตทีละแถว (เช่น การทำธุรกรรมอ่านแล้วเขียน ฯลฯ ) คุณสามารถจับคู่กระบวนงานที่เก็บไว้กับมุมมองโดยใช้ทริกเกอร์ (เช่นCREATE TRIGGER trigger_name INSTEAD OF INSERT ON view_name FOR EACH ROW EXECUTE PROCEDURE procedure_name;) แต่ความคิดเห็นแตกต่างกันมากว่านี่เป็นความคิดที่ดีหรือไม่ ผู้เสนอจะบอกคุณว่ามันเก็บ SQL ที่แอปพลิเคชันของคุณดำเนินการได้ง่ายที่สุด ผู้แจ้งจะบอกคุณว่านี่เป็นระดับ "เวทมนตร์" ที่ยอมรับไม่ได้และคุณควรดำเนินการตามขั้นตอนโดยตรงจากแอปพลิเคชันของคุณ ผมว่านี่เป็นความคิดที่ดีกว่าถ้าขั้นตอนการเก็บของคุณดูหรือทำหน้าที่เป็นจำนวนมากเช่นINSERT, UPDATEหรือDELETEและเป็นความคิดที่เลวร้ายยิ่งถ้ามันจะทำอย่างอื่น ในที่สุดคุณจะต้องตัดสินใจด้วยตัวเองว่าสไตล์ไหนเหมาะสมกว่า

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


2 ตามที่เขียนเป็นหลักถือว่าฐานข้อมูลแบบอ่านอย่างเดียว ขั้นตอนการจัดเก็บที่ใช้ไม่ใช่เรื่องแปลกสำหรับการสืบค้นที่แก้ไข
jpmc26

@ jpmc26: ฉันครอบคลุมการเขียนในวงเล็บ ... คุณมีคำแนะนำเฉพาะสำหรับการปรับปรุงคำตอบนี้หรือไม่?
เควิน

ฮึ่ม ขออภัยในการแสดงความคิดเห็นก่อนที่จะตอบคำถามให้ครบถ้วนและเสียสมาธิ แต่มุมมองที่อัปเดตได้ทำให้การติดตามตำแหน่งที่อัปเดตทำได้ยาก การ จำกัด การอัปเดตให้กับตารางโดยตรงโดยไม่คำนึงถึงกลไกจะมีข้อดีในแง่ของการทำความเข้าใจกับตรรกะของโค้ด หากคุณกำลัง จำกัด ตรรกะให้กับฐานข้อมูลคุณจะไม่สามารถบังคับใช้ได้หากไม่มีกระบวนงานที่เก็บไว้ พวกเขายังมีข้อได้เปรียบในการอนุญาตการดำเนินการหลายอย่างด้วยการโทรเพียงครั้งเดียว บรรทัดล่าง: ฉันไม่คิดว่าคุณควรจะค่อนข้างยากกับพวกเขา
jpmc26

@ jpmc26: มันยุติธรรม
เควิน

8

มันถือว่าเป็นรูปแบบการต่อต้านการเขียน SQL ในซอร์สโค้ดหรือไม่?

ไม่จำเป็น. หากคุณอ่านความคิดเห็นทั้งหมดที่นี่คุณจะพบข้อโต้แย้งที่ถูกต้องสำหรับคำสั่ง hardcoding SQL ในซอร์สโค้ด

ปัญหาที่เกิดขึ้นมาพร้อมกับที่คุณวางงบ หากคุณวางคำสั่ง SQL ทั่วทั้งโครงการคุณอาจเพิกเฉยต่อหลักการบางประการที่เรามักจะพยายามทำตาม

คิดว่าเขาหมายถึง; ทั้ง:

1) ใช้ LINQ

หรือ

2) ใช้โพรซีเดอร์ที่เก็บไว้สำหรับ SQL

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

สิ่งนี้ไม่จำเป็นว่าจะผิดหรือเลว ฉันแค่ชี้ไปที่ความจริง

การเพิกเฉยหลักการ SOLID และการล็อคผู้ขายมีผลกระทบที่เป็นไปได้ที่คุณอาจละเลย จึงเป็นเรื่องดีที่จะนั่งกับทีมและแสดงความสงสัยของคุณ

ประโยชน์ของการจัดเก็บฉันคิดว่า SQL Developers สามารถมีส่วนร่วมกับกระบวนการพัฒนา (การเขียนขั้นตอนการจัดเก็บ ฯลฯ )

ฉันคิดว่ามันไม่มีอะไรเกี่ยวข้องกับข้อดีของขั้นตอนการจัดเก็บ นอกจากนี้หากเพื่อนร่วมงานของคุณไม่ชอบฮาร์ดโค้ด SQL ก็เป็นไปได้ว่าการย้ายธุรกิจไปยังขั้นตอนการจัดเก็บจะไม่ชอบเขาเช่นกัน

แก้ไข: การพูดคุยเกี่ยวกับการเชื่อมโยงต่อไปนี้คำสั่ง SQL ยากรหัส:  https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/hard-coded-sql-statements มีประโยชน์ในการจัดทำคำสั่ง SQL หรือไม่

ใช่. โพสต์ enums ข้อดีของงบเตรียม มันเป็น SQL templating ปลอดภัยยิ่งกว่าการต่อสตริง แต่โพสต์ไม่สนับสนุนให้คุณไปทางนี้หรือยืนยันว่าคุณถูกต้อง มันอธิบายว่าเราจะใช้ hardcoded SQL ในวิธีที่ปลอดภัยและมีประสิทธิภาพได้อย่างไร

สรุปลองถามเพื่อนร่วมงานของคุณก่อน ส่งจดหมายโทรศัพท์ถึงเขา ... ไม่ว่าเขาจะตอบรับหรือไม่ก็ตามนั่งกับทีมและแสดงความสงสัยของคุณ ค้นหาโซลูชันที่ตรงกับความต้องการของคุณที่สุด อย่าตั้งสมมติฐานที่ผิดพลาดตามสิ่งที่คุณอ่าน


1
ขอบคุณ +1 สำหรับ "คำสั่ง Hardcoding SQL อาจนำคุณไปสู่การเชื่อมต่อแอปพลิเคชันของคุณกับ db enginee ให้แน่น" ฉันหลงถ้าเขาอ้างถึง SQL Server มากกว่า SQL คือการใช้ SQLConnection มากกว่า dbConnection; SQLCommand มากกว่า dbCommand และ SQLDataReader แทนที่จะเป็น dbDataReader
w0051977

ไม่ฉันกำลังอ้างถึงการใช้งานนิพจน์ที่ไม่สอดคล้องกับ ANSI ตัวอย่างเช่น: select GETDATE(), ....GETDATE () เป็นฟังก์ชันวันที่ของ SqlServer ใน enginees อื่น ๆ ฟังก์ชั่นมีชื่อที่แตกต่างกัน, การแสดงออกที่แตกต่างกัน ...
LAIV

14
"ผู้ขายล็อคใน" ... บ่อยแค่ไหนคน / สถานประกอบการจริงโยกย้ายฐานข้อมูลผลิตภัณฑ์ที่มีอยู่ของพวกเขาที่จะ RDBMS อื่นได้หรือไม่ แม้แต่ในโครงการที่ใช้ ORM โดยเฉพาะฉันไม่เคยเห็นสิ่งนั้นเกิดขึ้น: โดยปกติซอฟต์แวร์จะถูกเขียนใหม่ตั้งแต่ต้นเช่นกันเนื่องจากความต้องการมีการเปลี่ยนแปลงระหว่างนี้ ดังนั้นการคิดเกี่ยวกับเรื่องนี้ดูเหมือนจะค่อนข้างเหมือน "การเพิ่มประสิทธิภาพก่อนวัยอันควร" สำหรับฉัน ใช่มันเป็นอัตวิสัยทั้งหมด
Olivier Grégoire

1
ฉันไม่สนใจว่าจะเกิดขึ้นบ่อยแค่ไหน ฉันสนใจมันอาจเกิดขึ้น ในโครงการกรีนฟิลด์ค่าใช้จ่ายในการใช้ DAL ที่ถูกสรุปจากฐานข้อมูลนั้นเกือบ 0 การโยกย้ายที่เป็นไปได้ไม่ใช่ ข้อโต้แย้งกับ ORM ค่อนข้างอ่อนแอ ORM นั้นไม่เหมือนเมื่อ 15 ปีก่อนเมื่อไฮเบอร์เนตเป็นสีเขียว สิ่งที่ฉันได้เห็นคือการกำหนดค่า "โดยค่าเริ่มต้น" จำนวนมากและการออกแบบตัวแบบข้อมูลที่ค่อนข้างแย่ มันเหมือนกับเครื่องมืออื่น ๆ หลายคนทำแบบฝึกหัด "เริ่มต้นใช้งาน" และพวกเขาไม่สนใจสิ่งที่เกิดขึ้นภายใต้ประทุน เครื่องมือไม่ใช่ปัญหา ปัญหาคือใครไม่รู้ว่าจะใช้อย่างไรและเมื่อไหร่
Laiv

ในทางกลับกันการล็อคอินของผู้ขายไม่จำเป็นต้องเลวร้าย ถ้าผมถูกถามผมจะบอกว่าผมไม่ชอบ อย่างไรก็ตามฉันถูกล็อคใน Spring, Tomcat หรือ JBoss หรือ Websphere (ยังแย่กว่านั้น) ที่ฉันสามารถหลีกเลี่ยงฉันทำ คนที่ฉันไม่สามารถอยู่ด้วยได้ มันเป็นเรื่องของการตั้งค่า
Laiv

3

ฉันคิดว่ามันเป็นการปฏิบัติที่ไม่ดีใช่ คนอื่น ๆ ชี้ให้เห็นถึงข้อดีของการเก็บรหัสการเข้าถึงข้อมูลทั้งหมดของคุณในเลเยอร์ของตัวเอง คุณไม่ต้องไปหามันมันง่ายกว่าที่จะทำการออปติไมซ์และทดสอบ ... แต่ภายในเลเยอร์นั้นคุณมีทางเลือกน้อย: ใช้ ORM ใช้ sprocs หรือฝังเคียวรี SQL เป็นสตริง ฉันจะบอกว่าสตริงแบบสอบถาม SQL เป็นตัวเลือกที่แย่ที่สุด

ด้วย ORM การพัฒนากลายเป็นเรื่องง่ายขึ้นและมีข้อผิดพลาดน้อยลง ด้วย EF คุณสามารถกำหนด schema ของคุณเพียงแค่สร้างคลาสโมเดลของคุณ (คุณจะต้องสร้างคลาสเหล่านี้ต่อไป) การสืบค้นด้วย LINQ นั้นเป็นเรื่องง่าย - คุณมักจะออกไปกับ c # 2 บรรทัดซึ่งคุณต้องเขียนและบำรุงรักษา sproc IMO นี่เป็นข้อได้เปรียบอย่างมากเมื่อพูดถึงประสิทธิภาพและการบำรุงรักษา - รหัสน้อยลงปัญหาน้อยลง แต่มีค่าใช้จ่ายด้านประสิทธิภาพแม้ว่าคุณจะรู้ว่าคุณกำลังทำอะไรอยู่

Sprocs (หรือฟังก์ชั่น) เป็นตัวเลือกอื่น ที่นี่คุณเขียนแบบสอบถาม SQL ของคุณด้วยตนเอง แต่อย่างน้อยคุณก็ได้รับการรับรองว่าถูกต้อง หากคุณกำลังทำงานใน. NET Visual Studio จะทิ้งข้อผิดพลาดคอมไพเลอร์ถ้า SQL ไม่ถูกต้อง มันเยี่ยมมาก หากคุณเปลี่ยนหรือลบคอลัมน์บางส่วนและบางคำถามของคุณไม่ถูกต้องอย่างน้อยคุณก็น่าจะรู้เกี่ยวกับมันในระหว่างการรวบรวม นอกจากนี้ยังเป็นวิธีที่ง่ายกว่าในการรักษา sprocs ในไฟล์ของตัวเอง - คุณอาจได้รับการเน้นไวยากรณ์, เติมข้อความอัตโนมัติ .. ฯลฯ

หากแบบสอบถามของคุณถูกเก็บไว้เป็น sprocs คุณสามารถเปลี่ยนแปลงได้โดยไม่ต้องรวบรวมใหม่และปรับใช้แอปพลิเคชันอีกครั้ง หากคุณสังเกตเห็นว่ามีบางอย่างใน SQL ของคุณเสีย DBA ก็สามารถแก้ไขได้โดยไม่จำเป็นต้องเข้าถึงรหัสแอปของคุณ โดยทั่วไปหากแบบสอบถามของคุณเป็นตัวอักษรสตริงคุณ dbas ไม่สามารถทำงานได้อย่างง่ายดาย

แบบสอบถาม SQL เป็นตัวอักษรสตริงจะทำให้การเข้าถึงข้อมูลของคุณรหัส c # อ่านน้อย

ค่าคงที่เวทย์มนตร์นั้นไม่ดีเท่ากฎทั่วไป นั่นรวมถึงตัวอักษรสตริง


การอนุญาตให้ DBA เปลี่ยน SQL ของคุณโดยไม่ต้องอัปเดตแอปพลิเคชันจะทำให้แอปพลิเคชันของคุณแยกออกเป็นสองส่วนแยกจากกันโดยแยกออกจากกัน ตอนนี้คุณจำเป็นต้องจัดการสัญญาส่วนต่อประสานระหว่างสิ่งเหล่านั้นซิงโครไนซ์การปล่อยการเปลี่ยนแปลงแบบพึ่งพาซึ่งกันและกัน ฯลฯ ซึ่งอาจเป็นสิ่งที่ดีหรือไม่ดี แต่มันมีมากกว่า "การวาง SQL ทั้งหมดไว้ในที่เดียว - เข้าถึงการตัดสินใจทางสถาปัตยกรรม
IMSoP

2

มันไม่ได้เป็นรูปแบบการต่อต้าน (คำตอบนี้เป็นอันตรายอย่างใกล้ชิดกับความเห็น) แต่การจัดรูปแบบรหัสมีความสำคัญและสตริง SQL ควรจัดรูปแบบจึงแยกจากรหัสที่ใช้อย่างชัดเจน ตัวอย่างเช่น

    string query = 
    @"SELECT foo, bar
      FROM table
      WHERE id = @tn";

7
ปัญหาที่เห็นได้ชัดที่สุดคือปัญหาที่ค่า 42 มาจากไหน คุณกำลังต่อค่าลงในสตริงหรือไม่? ถ้าเป็นเช่นนั้นมาจากไหน มีวิธีการขัดเพื่อลดการโจมตีของการฉีด SQL? ทำไมตัวอย่างนี้จึงไม่แสดงข้อความค้นหาที่กำหนดพารามิเตอร์
Craig

ฉันแค่ตั้งใจจะแสดงการจัดรูปแบบ ขออภัยฉันลืมกำหนดพารามิเตอร์ ตอนนี้ได้รับการแก้ไขหลังจากอ้างถึงmsdn.microsoft.com/library/bb738521(v=vs.100).aspx
2560

+1 สำหรับความสำคัญของการจัดรูปแบบ แต่ฉันยืนยันว่าสตริง SQL นั้นมีกลิ่นรหัสโดยไม่คำนึงถึงการจัดรูปแบบ
Charles Burns

1
คุณยืนยันในการใช้ ORM หรือไม่? เมื่อ ORM ไม่ได้ใช้งานสำหรับโครงการขนาดเล็กฉันใช้คลาส 'shim' ที่มี SQL แบบฝัง
rleir

สตริง SQL นั้นแน่นอนที่สุดไม่ใช่กลิ่นรหัส หากปราศจาก ORM ในฐานะผู้จัดการและสถาปนิกฉันอยากจะสนับสนุนพวกเขาให้ใช้ SPs ทั้งในด้านการบำรุงรักษาและบางครั้งเหตุผลด้านประสิทธิภาพ
Sentinel

1

ฉันไม่สามารถบอกคุณได้ว่าเพื่อนร่วมงานของคุณหมายถึงอะไร แต่ฉันสามารถตอบคำถามนี้:

มีประโยชน์ในการจัดทำคำสั่ง SQL หรือไม่

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

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

List<UUID> ids = select(person.id).from(person).fetch();

อย่างที่คุณเห็นไม่มีสตริงตัวเดียวที่นี่ทำให้ SQL injection neighbour เป็นไปไม่ได้ นอกจากนี้ฉันมีโค้ดที่สมบูรณ์เมื่อเขียนสิ่งนี้และ IDE ของฉันสามารถ refactor ได้ฉันควรเลือกที่จะเปลี่ยนชื่อคอลัมน์ id นอกจากนี้ฉันสามารถค้นหาแบบสอบถามทั้งหมดสำหรับตารางบุคคลได้โดยถาม IDE ของฉันว่าpersonเข้าถึงตัวแปรได้ที่ไหน โอ้และคุณสังเกตเห็นไหมว่าสายตาของคุณสั้นกว่าและง่ายกว่าโค้ดของคุณเล็กน้อย?

ฉันสามารถสันนิษฐานได้ว่าบางสิ่งเช่นนี้มีให้สำหรับ C # ด้วย ตัวอย่างเช่นฉันได้ยินสิ่งที่ยอดเยี่ยมเกี่ยวกับ LINQ

โดยสรุปการแทนเคียวรีในฐานะสตริง SQL ทำให้ยากสำหรับ IDE ที่จะช่วยในการเขียนและการปรับโครงสร้างเคียวรีใหม่อย่างมีความหมาย defers การตรวจสอบไวยากรณ์และข้อผิดพลาดประเภทจากเวลาคอมไพล์จนถึงเวลารันไทม์ ใช่มีเหตุผลที่ถูกต้องว่าทำไมบางคนอาจไม่ต้องการสตริง SQL ในซอร์สโค้ด

[1]: ใช่การใช้คำสั่งที่จัดทำอย่างถูกต้องจะช่วยป้องกันการฉีด SQL แต่คำตอบอื่นในหัวข้อนี้มีความเสี่ยงต่อการฉีด SQL จนกระทั่งผู้วิจารณ์ชี้ให้เห็นว่าไม่ได้สร้างแรงบันดาลใจให้ความมั่นใจในโปรแกรมเมอร์จูเนียร์อย่างถูกต้องใช้พวกเขาเมื่อใดก็ตามที่จำเป็น ...


เพื่อความชัดเจน: การใช้คำสั่งที่เตรียมไว้ไม่ได้ป้องกันการฉีด SQL การใช้ข้อความที่เตรียมไว้พร้อมกับตัวแปรที่ผูกไว้ทำ เป็นไปได้ที่จะใช้คำสั่งที่เตรียมไว้ซึ่งสร้างขึ้นจากข้อมูลที่ไม่น่าเชื่อถือ
Andy Lester

1

คำตอบที่ดีมากมายที่นี่ นอกจากสิ่งที่พูดไปแล้วฉันต้องการเพิ่มอีกอย่างหนึ่ง

ฉันไม่ได้พิจารณาใช้ inline SQL ในการต่อต้านรูปแบบ สิ่งที่ฉันทำพิจารณาการต่อต้านแบบคือโปรแกรมเมอร์ทุกคนทำสิ่งของตัวเอง คุณต้องตัดสินใจเป็นทีมในมาตรฐานทั่วไป หากทุกคนใช้ linq คุณต้องใช้ linq ถ้าทุกคนใช้มุมมองและขั้นตอนการจัดเก็บคุณก็ต้องทำเช่นนั้น

อย่าลืมเปิดใจเสมอและอย่าหลงเชื่อ หากร้านค้าของคุณเป็นร้าน "linq" คุณใช้สิ่งนั้น ยกเว้นที่เดียวที่ linq ไม่ทำงาน (แต่คุณไม่ควร 99.9% ของเวลา)

ดังนั้นสิ่งที่ฉันจะทำคือการวิจัยรหัสในรหัสฐานและตรวจสอบวิธีการทำงานของเพื่อนร่วมงานของคุณ


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

0

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

วิธีนี้เป็นส่วนหนึ่งของเลเยอร์แม้ว่า OP คิดว่าแตกต่างกัน (การเข้าถึงฐานข้อมูลธรรมดาไม่มีการผสมกับสิ่งอื่นใด) มันอาจจะอยู่ในรหัสที่ไม่ดี วิธีนี้เป็นของชั้นแยก "ชั้นเชื่อมต่อฐานข้อมูล" มันจะเป็นรูปแบบการต่อต้านที่จะวางไว้ในชั้นสามัญถัดจากวิธีการที่ใช้กิจกรรมที่ไม่ใช่ฐานข้อมูล

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


0

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

ข้อดีอีกอย่างคือมันยากขึ้นมากเมื่อจัดการกับคิวรีแบบไดนามิกที่ควรสร้างตามเงื่อนไขบางประการ

var query = "select * from accounts";

if(users.IsInRole('admin'))
{
   query += " join secret_balances";
}

ฉันขอแนะนำให้ใช้ตัวสร้างแบบสอบถาม SQL


อาจเป็นเพียงการใช้ชวเลข แต่ก็เป็นความคิดที่ดีที่จะใช้ "เลือก *" อย่างสุ่มสี่สุ่มห้าเพราะคุณจะนำฟิลด์กลับมาที่คุณไม่ต้องการ (แบนด์วิดท์ !!) นอกจากนี้แม้ว่าฉันจะไม่ได้มีปัญหากับประโยค Admin แบบมีเงื่อนไข มันนำไปสู่ความชันที่ลื่นมากไปยังประโยค "WHERE" ที่มีเงื่อนไขและนั่นคือถนนที่นำไปสู่นรกที่มีประสิทธิภาพ
mcottle

0

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

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

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


ข้อความต้นฉบับด้านล่างสำหรับบริบท

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

ในหลายองค์กรรหัสสามารถผลักดันให้ผลิตหลังจากรอบ QA ฯลฯ ซึ่งอาจใช้เวลาหลายวันหรือหลายสัปดาห์ หากระบบของคุณพัฒนาปัญหาประสิทธิภาพฐานข้อมูลเนื่องจากการเปลี่ยนแปลงรูปแบบการใช้งานหรือการเพิ่มขนาดของตารางเป็นต้นมันอาจเป็นเรื่องยากที่จะติดตามปัญหาที่เกิดขึ้นจากการสืบค้นแบบใช้รหัสยากในขณะที่ฐานข้อมูลจะมีเครื่องมือ / รายงานอื่น ๆ . ด้วยวิธีการจัดเก็บวิธีการแก้ปัญหาสามารถทดสอบ / มาตรฐานในการผลิตและปัญหาสามารถแก้ไขได้อย่างรวดเร็วโดยไม่ต้องผลักดันรุ่นใหม่ของแอพพลิเคชั่นซอฟต์แวร์

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


ผิด ...........
Sentinel

คุณคิดว่าความคิดเห็นของคุณมีประโยชน์กับทุกคนหรือไม่?
bikeman868

นี่เป็นเพียงข้อผิดพลาดธรรมดา สมมติว่าเรามีโครงการ Web API2 ที่มี EF เพื่อเข้าถึงข้อมูลและ Azure Sql เป็นที่เก็บข้อมูล ไม่งั้นกำจัด EF และใช้ handcrafted SQL db schema ถูกเก็บไว้ในโครงการ SSDT SQL Server การโยกย้าย schema ของ DB เกี่ยวข้องกับการทำให้แน่ใจว่า codebase นั้นถูกซิงค์กับ schema db และทดสอบอย่างสมบูรณ์ก่อนที่จะผลักดันการเปลี่ยนแปลงการทดสอบ db ในการเปลี่ยนรหัส Azure.A เกี่ยวข้องกับการผลักออกไปยังสล็อตการปรับใช้ App Service ซึ่งใช้เวลา 2 วินาที นอกจากนี้ SQL แบบ handcrafted ยังมีประสิทธิภาพการทำงานมากกว่าขั้นตอนการจัดเก็บโดยทั่วไปการพูดแม้ว่า 'ปัญญา' จะตรงกันข้าม
Sentinel

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

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