นี่เป็นปัญหาที่ฉันใช้เวลาหลายชั่วโมงในการค้นคว้าในอดีต สำหรับฉันแล้วดูเหมือนว่าจะเป็นสิ่งที่ควรได้รับการแก้ไขโดยโซลูชันRDBMS ที่ทันสมัยแต่ฉันยังไม่พบสิ่งใดที่ตอบสนองสิ่งที่ฉันเห็นว่าเป็นความต้องการทั่วไปอย่างไม่น่าเชื่อในเว็บหรือแอปพลิเคชัน Windows ที่มีฐานข้อมูลส่วนหลัง
ฉันพูดถึงการเรียงลำดับแบบไดนามิก ในโลกแฟนตาซีของฉันมันควรจะเรียบง่ายเหมือนกับ:
ORDER BY @sortCol1, @sortCol2
นี่คือตัวอย่างมาตรฐานที่กำหนดโดยผู้พัฒนา SQL มือใหม่และStored Procedureทั่วฟอรัมต่างๆบนอินเทอร์เน็ต "ทำไมถึงทำไม่ได้" พวกเขาถาม. ในที่สุดก็มีใครบางคนมาบรรยายเกี่ยวกับลักษณะการรวบรวมของขั้นตอนการจัดเก็บแผนการดำเนินการโดยทั่วไปและเหตุผลอื่น ๆ อีกมากมายว่าทำไมจึงไม่สามารถใส่พารามิเตอร์ลงในORDER BY
ประโยคได้โดยตรง
ฉันรู้ว่าพวกคุณบางคนกำลังคิดอะไรอยู่แล้ว: "ให้ลูกค้าจัดการเรียงลำดับ" โดยปกติแล้วสิ่งนี้จะลดการทำงานออกจากฐานข้อมูลของคุณ ในกรณีของเราเซิร์ฟเวอร์ฐานข้อมูลของเราไม่ได้ทำลายเหงื่อ 99% ของเวลาและยังไม่ได้เป็นแบบมัลติคอร์หรือการปรับปรุงสถาปัตยกรรมระบบอื่น ๆ อีกมากมายที่เกิดขึ้นทุกๆ 6 เดือน ด้วยเหตุนี้เพียงอย่างเดียวการมีฐานข้อมูลของเราจัดการกับการเรียงลำดับจะไม่เป็นปัญหา นอกจากนี้ฐานข้อมูลเป็นอย่างมากเก่งในการจัดเรียง พวกเขาได้รับการปรับให้เหมาะสมและใช้เวลาหลายปีในการทำให้ถูกต้องภาษาที่ใช้ทำนั้นมีความยืดหยุ่นใช้งานง่ายและเรียบง่ายและเหนือสิ่งอื่นใดนักเขียน SQL มือใหม่รู้วิธีการทำและที่สำคัญยิ่งกว่านั้นพวกเขารู้วิธีแก้ไข ทำการเปลี่ยนแปลงทำการบำรุงรักษา ฯลฯ เมื่อฐานข้อมูลของคุณห่างไกลจากการถูกเก็บภาษีและคุณเพียงแค่ต้องการลดความซับซ้อนของเวลาในการพัฒนา (และย่น!) สิ่งนี้ดูเหมือนจะเป็นทางเลือกที่ชัดเจน
แล้วมีปัญหาเว็บ ฉันได้เล่นกับ JavaScript ที่จะทำการจัดเรียงตาราง HTML ฝั่งไคลเอ็นต์ แต่ก็ไม่สามารถยืดหยุ่นได้เพียงพอสำหรับความต้องการของฉันและอีกครั้งเนื่องจากฐานข้อมูลของฉันไม่ได้เก็บภาษีมากเกินไปและสามารถจัดเรียงได้อย่างง่ายดายจริงๆฉัน มีช่วงเวลาที่ยากลำบากในการระบุเวลาที่ต้องใช้ในการเขียนซ้ำหรือม้วนตัวเรียงลำดับ JavaScript ของฉันเอง โดยทั่วไปจะใช้กับการเรียงลำดับฝั่งเซิร์ฟเวอร์แม้ว่าจะเป็นที่ต้องการมากกว่า JavaScript อยู่แล้วก็ตาม ฉันไม่ใช่คนที่ชอบค่าใช้จ่ายของชุดข้อมูลเป็นพิเศษดังนั้นฟ้องฉัน
แต่สิ่งนี้ทำให้จุดที่เป็นไปไม่ได้กลับมา - หรือไม่ใช่เรื่องง่าย ฉันเคยทำกับระบบก่อนหน้านี้เป็นวิธีแฮ็คที่เหลือเชื่อในการจัดเรียงแบบไดนามิก มันไม่สวยหรือใช้งานง่ายเรียบง่ายหรือยืดหยุ่นและนักเขียน SQL มือใหม่จะหายไปภายในไม่กี่วินาที สิ่งนี้ดูเหมือนจะไม่ใช่ "วิธีแก้ปัญหา" มากนัก แต่เป็น "ภาวะแทรกซ้อน"
ตัวอย่างต่อไปนี้ไม่ได้มีไว้เพื่อแสดงแนวทางปฏิบัติที่ดีที่สุดหรือรูปแบบการเข้ารหัสที่ดีหรือสิ่งใด ๆ และไม่ได้บ่งบอกถึงความสามารถของฉันในฐานะโปรแกรมเมอร์ T-SQL พวกเขาคือสิ่งที่พวกเขาเป็นและฉันยอมรับอย่างเต็มที่ว่าพวกเขาสับสนรูปแบบไม่ดีและเป็นเพียงการแฮ็กธรรมดา
เราส่งค่าจำนวนเต็มเป็นพารามิเตอร์ไปยังโพรซีเดอร์ที่เก็บไว้ (ขอเรียกพารามิเตอร์ว่า "sort") และจากนั้นเรากำหนดตัวแปรอื่น ๆ ตัวอย่างเช่น ... สมมติว่า sort คือ 1 (หรือค่าเริ่มต้น):
DECLARE @sortCol1 AS varchar(20)
DECLARE @sortCol2 AS varchar(20)
DECLARE @dir1 AS varchar(20)
DECLARE @dir2 AS varchar(20)
DECLARE @col1 AS varchar(20)
DECLARE @col2 AS varchar(20)
SET @col1 = 'storagedatetime';
SET @col2 = 'vehicleid';
IF @sort = 1 -- Default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'asc';
SET @sortCol2 = @col2;
SET @dir2 = 'asc';
END
ELSE IF @sort = 2 -- Reversed order default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'desc';
SET @sortCol2 = @col2;
SET @dir2 = 'desc';
END
คุณสามารถดูได้แล้วว่าถ้าฉันประกาศตัวแปร @colX เพิ่มเติมเพื่อกำหนดคอลัมน์อื่น ๆ ฉันจะสามารถสร้างสรรค์คอลัมน์เพื่อจัดเรียงตามค่าของ "sort" ได้อย่างไร ... ในการใช้งานมักจะมีลักษณะดังนี้ ประโยคที่ยุ่งอย่างไม่น่าเชื่อ:
ORDER BY
CASE @dir1
WHEN 'desc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir1
WHEN 'asc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END,
CASE @dir2
WHEN 'desc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir2
WHEN 'asc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END
เห็นได้ชัดว่านี่เป็นตัวอย่างที่ถูกถอดออกมาก ของจริงเนื่องจากโดยปกติแล้วเราจะมีสี่หรือห้าคอลัมน์เพื่อรองรับการเรียงลำดับโดยแต่ละคอลัมน์จะมีคอลัมน์รองหรือแม้แต่คอลัมน์ที่สามที่เป็นไปได้ที่จะจัดเรียงนอกเหนือจากนั้น (เช่นวันที่จากมากไปหาน้อยจากนั้นเรียงลำดับที่สองตามชื่อจากน้อยไปหามาก) และแต่ละคอลัมน์รองรับ bi- การเรียงลำดับตามทิศทางซึ่งเพิ่มจำนวนเคสได้เป็นสองเท่า ใช่ ... มันมีขนขึ้นอย่างรวดเร็ว
แนวคิดก็คือเราสามารถ "เปลี่ยนกรณีการจัดเรียง" ได้อย่างง่ายดายเพื่อให้ vehicleid ถูกจัดเรียงก่อนเวลาจัดเก็บข้อมูล ... แต่ความยืดหยุ่นหลอกอย่างน้อยในตัวอย่างง่ายๆนี้ก็จบลงที่นั่นจริงๆ โดยพื้นฐานแล้วแต่ละกรณีที่ไม่ผ่านการทดสอบ (เนื่องจากวิธีการเรียงลำดับของเราใช้ไม่ได้ในเวลานี้) จะแสดงค่าเป็น NULL ดังนั้นคุณจึงจบลงด้วยประโยคที่ทำหน้าที่ดังต่อไปนี้:
ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah
คุณจะได้รับความคิด ทำงานได้เนื่องจาก SQL Server ละเว้นค่า null อย่างมีประสิทธิภาพตามลำดับ นี่เป็นเรื่องยากที่จะรักษาอย่างไม่น่าเชื่อเนื่องจากทุกคนที่มีความรู้พื้นฐานเกี่ยวกับ SQL อาจเห็นได้ ถ้าฉันสูญเสียคุณไปอย่ารู้สึกแย่ เราใช้เวลานานในการทำให้มันใช้งานได้และเรายังคงสับสนในการพยายามแก้ไขหรือสร้างขึ้นใหม่เช่นนี้ โชคดีที่ไม่จำเป็นต้องเปลี่ยนบ่อยๆมิฉะนั้นจะกลายเป็น "ไม่คุ้มกับปัญหา" อย่างรวดเร็ว
มันได้ผล
คำถามของฉันคือ: มีวิธีที่ดีกว่านี้หรือไม่?
ฉันโอเคกับโซลูชันอื่นที่ไม่ใช่ขั้นตอนที่เก็บไว้เนื่องจากฉันรู้ว่ามันอาจไม่ใช่วิธีที่จะไป โดยเฉพาะอย่างยิ่งฉันต้องการทราบว่ามีใครทำได้ดีกว่านี้ภายใน Stored Procedure แต่ถ้าไม่คุณจะจัดการอย่างไรให้ผู้ใช้จัดเรียงตารางข้อมูลแบบไดนามิก (แบบสองทิศทางด้วย) ด้วย ASP.NET
และขอขอบคุณที่อ่าน (หรืออย่างน้อยก็อ่าน) คำถามยาว ๆ !
PS: ดีใจที่ฉันไม่ได้แสดงตัวอย่างของขั้นตอนการจัดเก็บที่รองรับการเรียงลำดับแบบไดนามิกการกรองแบบไดนามิก / การค้นหาข้อความของคอลัมน์การแบ่งหน้าผ่าน ROWNUMBER () OVER และลอง ... จับด้วยการย้อนธุรกรรมเมื่อเกิดข้อผิดพลาด ... "behemoth-sized" ไม่ได้เริ่มอธิบายด้วยซ้ำ
ปรับปรุง:
- ฉันต้องการที่จะหลีกเลี่ยงการ SQL การแยกสตริงเข้าด้วยกันและเรียกใช้ EXEC กับมันเอาชนะวัตถุประสงค์ของการจัดเก็บโพรซีเดอร์ไว้ตั้งแต่แรก บางครั้งฉันก็สงสัยว่าข้อเสียของการทำสิ่งนั้นจะไม่คุ้มค่าหรือไม่อย่างน้อยในกรณีการเรียงลำดับไดนามิกพิเศษเหล่านี้ ถึงกระนั้นฉันก็มักจะรู้สึกสกปรกทุกครั้งที่ทำสตริง SQL แบบไดนามิกเช่นนั้นเหมือนฉันยังคงอยู่ในโลก ASP แบบคลาสสิก
- จำนวนมากของเหตุผลที่เราต้องการวิธีการจัดเก็บในสถานที่แรกสำหรับการรักษาความปลอดภัย ฉันไม่ได้รับสายเกี่ยวกับข้อกังวลด้านความปลอดภัยขอแนะนำวิธีแก้ไขเท่านั้น ด้วย SQL Server 2005 เราสามารถตั้งค่าการอนุญาต (ตามผู้ใช้แต่ละรายหากจำเป็น) ที่ระดับสคีมาบนโพรซีเดอร์ที่จัดเก็บแต่ละโพรซีเดอร์จากนั้นปฏิเสธการสืบค้นใด ๆ กับตารางโดยตรง การวิพากษ์วิจารณ์ข้อดีข้อเสียของแนวทางนี้อาจเป็นคำถามอื่น แต่ก็ไม่ใช่การตัดสินใจของฉันอีกครั้ง ฉันเป็นแค่ลิงรหัสผู้นำ :)