เหตุใด RDBMS จึงไม่ส่งคืนตารางที่เข้าร่วมในรูปแบบที่ซ้อนกัน


14

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

SELECT * FROM users user 
    LEFT JOIN emails email ON email.user_id=user.id
    LEFT JOIN phones phone ON phone.user_id=user.id

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

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

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

เราสามารถแก้ไขได้โดยใช้ NoSQL แต่ไม่ควรมีพื้นกลางบ้าง

ฉันพลาดอะไรไปรึเปล่า? ทำไมจึงไม่มีสิ่งนี้

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

ฉันรู้ว่านี้สามารถทำได้ในภาษาสคริปต์ที่คุณเลือก แต่ต้องว่าเซิร์ฟเวอร์ SQL ทั้งส่งข้อมูลซ้ำซ้อน (ตัวอย่างด้านล่าง) SELECT email FROM emails WHERE user_id IN (/* result of first query */)หรือว่าคุณจะออกคำสั่งหลายตัวเช่น


แทนที่จะให้ MySQL คืนสิ่งที่คล้ายกับนี้:

[
    {
        "name": "John Smith",
        "dob": "1945-05-13",
        "fav_color": "red",
        "email": "johnsmith45@gmail.com",
    },
    {
        "name": "John Smith",
        "dob": "1945-05-13",
        "fav_color": "red",
        "email": "john@smithsunite.com",
    },
    {
        "name": "Jane Doe",
        "dob": "1953-02-19",
        "fav_color": "green",
        "email": "originaljane@deerclan.com",
    }
]

แล้วต้องจัดกลุ่มตัวระบุที่ไม่ซ้ำกัน (ซึ่งหมายความว่าฉันต้องดึงข้อมูลนั้นด้วย!) ฝั่งไคลเอ็นต์เพื่อจัดรูปแบบผลลัพธ์ใหม่ตามที่คุณต้องการเพียงคืนค่านี้:

[
    {
        "name": "John Smith",
        "dob": "1945-05-13",
        "fav_color": "red",
        "emails": ["johnsmith45@gmail.com", "john@smithsunite.com"]
    },
    {
        "name": "Jane Doe",
        "dob": "1953-02-19",
        "fav_color": "green",
        "emails": ["originaljane@deerclan.com"],
    }
]

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


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

54
SQL ไม่ส่งคืนข้อมูลที่ไม่ดีคุณทำการสืบค้นสิ่งที่คุณต้องการได้ไม่ดี ตามกฎทั่วไปแล้วหากคุณคิดว่าเครื่องมือที่ใช้กันอย่างแพร่หลายนั้นมีลักษณะเป็นบั๊กกี้หรือแตกสำหรับกรณีใช้งานทั่วไปปัญหาก็คือคุณ
Sean McSomething

12
@SeanMcSomething เพื่อให้เป็นจริงมันเจ็บฉันไม่สามารถพูดได้ดีกว่าตัวเอง
WernerCD

5
นี่เป็นคำถามที่ยอดเยี่ยม คำตอบที่พูดว่า "นี่คือวิธีที่เป็นอยู่" หายไปในจุดนี้ เหตุใดจึงไม่สามารถส่งคืนแถวที่มีชุดรวมของแถวฝังตัวได้
Chris Pitman

8
@SeanMcSomething: ยกเว้นว่าเครื่องมือที่ใช้กันอย่างแพร่หลายคือ C ++ หรือ PHP ซึ่งในกรณีนี้คุณอาจถูกต้อง ;)
Mason Wheeler

คำตอบ:


11

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

ด้วยการทำงานกับแถวเท่านั้นและส่งคืนแถวเท่านั้นระบบจะสามารถจัดการกับหน่วยความจำและทราฟฟิกเครือข่ายได้ดีขึ้น

ดังที่กล่าวมาสิ่งนี้ช่วยให้สามารถทำการปรับแต่งบางอย่างได้อย่างสมบูรณ์ (ดัชนีเข้าร่วมสหภาพ ฯลฯ ... )

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

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

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

นอกจากนี้ยังมีประวัติการทำงานที่นี่ การถ่ายโอนข้อมูลที่มีโครงสร้างเป็นสิ่งที่น่าเกลียดในสมัยก่อน ดูรูปแบบ EDI เพื่อรับความคิดเกี่ยวกับสิ่งที่คุณอาจขอ ต้นไม้หมายถึงการเรียกซ้ำ - ซึ่งภาษาบางภาษาไม่สนับสนุน (สองภาษาที่สำคัญที่สุดของวันเก่าไม่สนับสนุนการเรียกซ้ำ - การเรียกซ้ำไม่ได้เข้าสู่ Fortran จนกระทั่ง F90และ COBOL ในยุคนั้นไม่ได้เป็นอย่างนั้น)

และในขณะที่ภาษาของวันนี้มีการรองรับการเรียกซ้ำและชนิดข้อมูลขั้นสูงมากขึ้น แต่ก็ไม่มีเหตุผลที่ดีที่จะเปลี่ยนแปลงสิ่งต่างๆ พวกเขาทำงานและทำงานได้ดี คนที่จะเปลี่ยนแปลงสิ่งที่มีฐานข้อมูล NoSQL คุณสามารถจัดเก็บต้นไม้ในเอกสารตามเอกสารได้ LDAP (เก่ามากจริง ๆ ) เป็นระบบแบบทรี (แม้ว่ามันอาจจะไม่ใช่สิ่งที่คุณต้องการ) ใครจะรู้บางทีสิ่งต่อไปในฐานข้อมูล nosql จะเป็นสิ่งที่ส่งคืนแบบสอบถามเป็นวัตถุ json

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

  1. ในการออกแบบโปรโตคอลความสมบูรณ์แบบนั้นไม่ได้มาถึงเมื่อไม่มีอะไรเหลือให้เพิ่ม แต่เมื่อไม่มีอะไรเหลือให้นำออกไป

จากRFC 1925 - ความจริงด้านเครือข่ายสิบสอง


"ถ้ามีใครต้องการโครงสร้างต้นไม้ซ้อนกันสิ่งนี้ต้องการให้ดึงข้อมูลทั้งหมดในครั้งเดียว Gone คือการปรับให้เหมาะสมสำหรับเคอร์เซอร์ที่ด้านฐานข้อมูล" - นั่นไม่ได้ฟังดูจริง มันจะต้องรักษาเคอร์เซอร์คู่: หนึ่งสำหรับตารางหลักและจากนั้นหนึ่งสำหรับแต่ละตารางเข้าร่วม ขึ้นอยู่กับอินเทอร์เฟซมันอาจส่งคืนหนึ่งแถวและตารางที่เข้าร่วมทั้งหมดในก้อนเดียว (สตรีมบางส่วน) หรือสามารถสตรีม subtrees (และอาจไม่ได้สอบถามพวกเขา) จนกว่าคุณจะเริ่มทำซ้ำพวกเขา แต่ใช่ว่าสิ่งที่ซับซ้อนมาก
mpen

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

51

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

สิ่งที่คุณกำลังประสบอยู่เรียกว่า " Object / Relational Impedance Mismatch " ปัญหาทางเทคนิคที่เกิดขึ้นจากข้อเท็จจริงที่ว่าตัวแบบข้อมูลเชิงวัตถุและตัวแบบข้อมูลเชิงสัมพันธ์นั้นมีความแตกต่างกันหลายประการ LINQ และเฟรมเวิร์กอื่น ๆ (รู้จักกันในชื่อ ORMs, Object / Relational Mappers, ไม่เกิดขึ้นโดยบังเอิญ,) ไม่น่าอัศจรรย์ "หลีกเลี่ยงสิ่งนี้" พวกเขาเพียงแค่ออกแบบสอบถามที่แตกต่างกัน มันสามารถทำได้ใน SQL ด้วย นี่คือวิธีที่ฉันจะทำ:

SELECT * FROM users user where [criteria here]

วนซ้ำรายการผู้ใช้และสร้างรายการ ID

SELECT * from EMAILS where user_id in (list of IDs here)
SELECT * from PHONES where user_id in (list of IDs here)

แล้วคุณก็เข้าร่วมฝั่งลูกค้า นี่คือวิธีที่ LINQ และกรอบงานอื่นทำ ไม่มีเวทย์มนตร์ที่เกี่ยวข้อง แค่ชั้นของสิ่งที่เป็นนามธรรม


14
+1 สำหรับ "สิ่งที่คุณขอ" บ่อยครั้งที่เราข้ามไปสู่ข้อสรุปว่ามีบางอย่างผิดปกติกับเทคโนโลยีมากกว่าข้อสรุปที่เราต้องเรียนรู้วิธีการใช้เทคโนโลยีอย่างมีประสิทธิภาพ
Matt

1
ไฮเบอร์เนตจะดึงข้อมูลเอนทิตีรากและคอลเลกชันบางอย่างในแบบสอบถามเดียวเมื่อใช้โหมดการดึงข้อมูลกระตือรือร้นสำหรับการรวบรวมเหล่านั้น ในกรณีนั้นจะทำการลดคุณสมบัติของเอนทิตีรูทในหน่วยความจำ ORM อื่น ๆ มีแนวโน้มที่จะทำเช่นเดียวกัน
Mike Partridge

3
จริงๆแล้วนี่ไม่ใช่การตำหนิโมเดลเชิงสัมพันธ์ ขอบคุณมากสำหรับความสัมพันธ์ที่ซ้อนกัน นี่เป็นข้อบกพร่องของการนำไปใช้งานจริงใน SQL เวอร์ชันก่อนหน้า ฉันคิดว่ารุ่นล่าสุดเพิ่มเติมได้เพิ่มว่า
John Nilsson

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

@ MarkJ คุณควรเขียนมันขึ้นมาเป็นคำตอบ
Mr.Mindor

12

คุณสามารถใช้ฟังก์ชันที่มีอยู่แล้วภายในเพื่อเชื่อมข้อมูลเข้าด้วยกัน ใน MySQL คุณสามารถใช้GROUP_CONCAT()ฟังก์ชันและใน Oracle คุณสามารถใช้LISTAGG()ฟังก์ชันได้

นี่คือตัวอย่างของแบบสอบถามที่อาจมีลักษณะเป็นใน MySQL:

SELECT user.*, 
    (SELECT GROUP_CONCAT(DISTINCT emailAddy) FROM emails email WHERE email.user_id = user.id
    ) AS EmailAddresses,
    (SELECT GROUP_CONCAT(DISTINCT phoneNumber) FROM phones phone WHERE phone.user_id = user.id
    ) AS PhoneNumbers
FROM users user 

สิ่งนี้จะส่งคืนสิ่งที่ต้องการ

username    department       EmailAddresses                        PhoneNumbers
Tim_Burton  Human Resources  hr@m.com, tb@me.com, nunya@what.com   231-123-1234, 231-123-1235

นี่น่าจะเป็นทางออกที่ใกล้เคียงที่สุด (ใน SQL) กับสิ่งที่ OP พยายามทำ เขาอาจยังต้องทำการประมวลผลฝั่งไคลเอ็นต์เพื่อแยกผลลัพธ์ EmailAddresses และ PhoneNumbers ออกเป็นรายการ
Mr.Mindor

2
จะเกิดอะไรขึ้นถ้าหมายเลขโทรศัพท์มี "ประเภท" เช่น "เซลล์", "บ้าน" หรือ "ที่ทำงาน" นอกจากนี้คอมม่าได้รับอนุญาตทางเทคนิคในที่อยู่อีเมล (ถ้าพวกเขาอ้างถึง) - ฉันจะแยกมันได้อย่างไร
mpen

10

ปัญหานี้คือการคืนชื่อผู้ใช้ DOB สีที่ชอบและข้อมูลอื่น ๆ ทั้งหมดที่เก็บไว้

ปัญหาคือคุณไม่ได้เลือกอย่างเพียงพอ คุณขอทุกสิ่งเมื่อคุณพูด

Select * from...

... และคุณได้รับมัน (รวมถึง DOB และสีที่ชอบ)

คุณน่าจะได้มากกว่านี้ (อะแฮ่ม) ... เลือกแล้วพูดว่า:

select users.name, emails.email_address, phones.home_phone, phones.bus_phone
from...

อาจเป็นไปได้ว่าคุณอาจเห็นระเบียนที่มีลักษณะซ้ำเนื่องจาก a userอาจเข้าร่วมหลายemailระเบียน แต่เขตข้อมูลที่แยกความแตกต่างสองรายการนี้ไม่ได้อยู่ในSelectคำสั่งของคุณ ดังนั้นคุณอาจต้องการพูดอะไรบางอย่างเช่น

select distinct users.name, emails.email_address, phones.home_phone, phones.bus_phone
from...

... ซ้ำแล้วซ้ำอีกสำหรับแต่ละระเบียน ...

LEFT JOINนอกจากนี้ผมสังเกตเห็นว่าคุณกำลังทำ นี่จะเข้าร่วมบันทึกทั้งหมดทางด้านซ้ายของการเข้าร่วม (เช่นusers) ไปยังระเบียนทั้งหมดทางด้านขวาหรือในคำอื่น ๆ :

การรวมภายนอกด้านซ้ายส่งกลับค่าทั้งหมดจากการรวมภายในและค่าทั้งหมดในตารางด้านซ้ายที่ไม่ตรงกับตารางด้านขวา

( http://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join )

ดังนั้นคำถามอื่นคือคุณต้องการเข้าร่วมซ้ายจริง ๆหรือจะINNER JOINเพียงพอหรือไม่ พวกเขาเป็นประเภทที่แตกต่างกันมากเข้าร่วม

จะไม่ดีกว่าถ้ามันส่งคืนแถวเดียวสำหรับผู้ใช้แต่ละรายและภายในบันทึกนั้นมีรายการอีเมล

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


ในที่สุดฉันคิดว่าปัญหาของคุณอาจได้รับการแก้ไขถ้าคุณเขียนข้อความค้นหาของคุณใกล้เคียงกับสิ่งนี้:

select distinct users.name, users.id, emails.email_address, phones.phone_number
from users
  inner join emails on users.user_id = emails.user_id
  inner join phones on users.user_id = phones.user_id

1
ใช้ * หมดกำลังใจ แต่ไม่ใช่ปัญหาของเขา แม้ว่าเขาจะเลือกคอลัมน์ผู้ใช้ 0 คนเขาก็ยังอาจประสบกับความซ้ำซ้อนเนื่องจากทั้งโทรศัพท์และอีเมลมีความสัมพันธ์กับผู้ใช้ 1 ราย ความแตกต่างจะไม่ป้องกันไม่ให้หมายเลขโทรศัพท์ปรากฏสองครั้ง ala phone1/name@hotmail.com, phone1/name@google.com
mike30

6
-1: "ปัญหาของคุณอาจได้รับการแก้ไข" กล่าวว่าคุณไม่ทราบว่าการเปลี่ยนแปลงจะเปลี่ยนจากleft joinเป็นinner joinอย่างไร ในกรณีนี้สิ่งนี้จะไม่ลด "การทำซ้ำ" ที่ผู้ใช้ร้องเรียน มันจะละเว้นผู้ใช้เหล่านั้นที่ไม่มีโทรศัพท์หรืออีเมล การปรับปรุงใด ๆ แทบจะไม่ เมื่อตีความ "บันทึกทั้งหมดทางด้านซ้ายไปยังระเบียนทั้งหมดทางด้านขวา" จะข้ามONเกณฑ์ซึ่งจะตัดการเชื่อมโยงความผิด 'ผิด' ทั้งหมดที่มีอยู่ในผลิตภัณฑ์คาร์ทีเซียน
Javier

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

3
คุณไม่มีจุดคำถาม ในตัวอย่างสมมุตินี้ฉันต้องการข้อมูลผู้ใช้ทั้งหมด (ชื่อ dob ฯลฯ ) และฉันต้องการหมายเลขโทรศัพท์ของเขา / เธอทั้งหมด การเข้าร่วมภายในไม่รวมผู้ใช้ที่ไม่มีอีเมลหรือไม่มีโทรศัพท์ - สิ่งนี้จะช่วยได้อย่างไร
mpen

4

คำค้นหาจะสร้างชุดข้อมูลแบบสี่เหลี่ยมผืนผ้า (ไม่ขรุขระ) เป็นตาราง ไม่มีชุดย่อยที่ซ้อนอยู่ภายในชุด ในโลกแห่งการกำหนดทุกอย่างเป็นสี่เหลี่ยมผืนผ้าที่ไม่ซ้อนกันบริสุทธิ์

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

เพื่อให้ได้สิ่งที่คุณต้องการคุณต้องใช้แบบสอบถามแยกต่างหากเช่น Mason Wheeler อธิบาย

select * from Phones where user_id=344;

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


2

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

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


"ไม่มีเหตุผลที่ฐานข้อมูลส่วนใหญ่ไม่สามารถส่งคืนชุดข้อมูลที่แยกจากกัน 3 ชุดภายในการโทรหนึ่งครั้งและไม่มีการเข้าร่วม" - คุณจะให้ชุดข้อมูล 3 ชุดแยกกันอย่างไรด้วยการโทรครั้งเดียว ฉันคิดว่าคุณต้องส่งข้อความค้นหาต่าง ๆ 3 ข้อซึ่งจะทำให้เกิดความหน่วงแฝงระหว่างกัน
mpen

กระบวนงานที่เก็บไว้สามารถถูกเรียกใน 1 ธุรกรรมแล้วส่งคืนชุดข้อมูลได้มากเท่าที่คุณต้องการ อาจจำเป็นต้องใช้ "SelectUserWithEmailsPhones" sproc
เกรแฮม

1
@ Mark: คุณสามารถส่ง (ในเซิร์ฟเวอร์ sql อย่างน้อย) มากกว่าหนึ่งคำสั่งที่เป็นส่วนหนึ่งของแบทช์เดียวกัน cmdText = "select * from b; select * from a; select * from c" จากนั้นใช้เป็นข้อความคำสั่งสำหรับ sqlcommand
jmoreno

2

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

ปัญหาที่ 2 แบบสอบถามที่แตกต่างกันแทนที่จะเป็นแบบสอบถามที่มีการเข้าร่วม

ในโพรซีเดอร์ที่เก็บไว้หรืออินไลน์ parameterized sql craft 2 เคียวรีและส่งคืนผลลัพธ์ของทั้งสองแบ็ก ฐานข้อมูลและภาษาส่วนใหญ่รองรับชุดผลลัพธ์หลายชุด

ยกตัวอย่างเช่น SQL Server และ C # IDataReader.NextResult()บรรลุฟังก์ชั่นนี้โดยใช้


1

คุณขาดอะไรไป หากคุณต้องการทำให้ข้อมูลของคุณเป็นปกติคุณต้องทำเอง

;with toList as (
    select  *, Stuff(( select ',' + (phone.phoneType + ':' + phone.PhoneNumber) 
                    from phones phone
                    where phone.user_id = user.user_id
                    for xml path('')
                  ), 1,1,'') as phoneNumbers
from users user
)
select *
from toList

1

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

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

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

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


-4

กรุณาอ้างถึงการใช้งานฟังก์ชั่น STUFF ซึ่งกลุ่มหลายแถว (หมายเลขโทรศัพท์) ของคอลัมน์ (ติดต่อ) ที่สามารถแยกเป็นเซลล์เดียวของค่าที่คั่นของแถว (ผู้ใช้)

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


5
โปรดขยายวิธีแก้ปัญหาของคำถาม แทนที่จะพูดกับ "กรุณาอ้างถึงการใช้งาน" ให้ตัวอย่างของสิ่งนี้จะบรรลุคำถามที่ถาม นอกจากนี้ยังมีประโยชน์ในการอ้างอิงแหล่งข้อมูลบุคคลที่สามซึ่งทำให้สิ่งต่าง ๆ ชัดเจน
bitsoflogic

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