4 คะแนน โดย GN⁺ 2023-07-04 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • inner join ในฐานข้อมูลเชิงสัมพันธ์ไม่ใช่แค่ไวยากรณ์ SQL ธรรมดา แต่สามารถตีความโครงสร้างเดียวกันได้หลายแบบ ทั้งในมุมการค้นหา, nested loop, โมเดลเชิงตรรกะ, การตรวจสอบชนิดข้อมูล และมุมมองพีชคณิต
  • ในตารางที่ผ่านการทำ normalization แล้ว join จะกลายเป็นเครื่องมือที่ใช้งานได้จริงที่สุดในการตาม reference เพื่อประกอบ ข้อมูลที่จัดเก็บไว้โดยไม่ซ้ำซ้อน กลับเข้าด้วยกัน
  • ในมุมมองการ implement สามารถมองได้ว่าเป็นการวนดูคู่ของแถวแล้วเหลือไว้เฉพาะชุดที่ตรงตามเงื่อนไข หรือเป็นการเลือกเฉพาะชุดค่าที่มีอยู่ในความสัมพันธ์ทั้งสองฝั่งจากโดเมนของคอลัมน์
  • ในโมเดลการเขียนโปรแกรม สามารถอธิบาย join ได้ด้วย flatMap, SQL LATERAL, การแก้ ปัญหา N+1 ของ ORM, การตรวจสอบชนิดข้อมูลด้วย trait ใน Rust และ andThen ของ Set monad
  • ในเชิงคณิตศาสตร์ ทั้งเส้นทางบนกราฟ, โมเดลที่เล็กที่สุด, ความสัมพันธ์ที่อนุญาตได้ที่ใหญ่ที่สุด, ขอบเขตบนน้อยสุด ของลำดับบางส่วน และผลคูณแบบริงของนิพจน์ความสัมพันธ์ ล้วนเผยให้เห็นคุณสมบัติเดียวกันของ join

ในข้อมูลที่ผ่าน normalization แล้ว join จะกลายเป็นการค้นหา

  • ในเชิงปฏิบัติที่สุด join สามารถมองได้ว่าเป็นการ ค้นหา ค่าบางอย่าง หรือการแนบข้อมูลที่ซ้ำซ้อนเข้ากับข้อมูลเดิม
  • ตัวอย่างเริ่มจากวิธีเก็บ user, country, country_code ไว้ในตารางเดียว
    • ค่า country เดียวกันจะมี country_code ซ้ำตามไปด้วย ทำให้เกิด ความซ้ำซ้อน
    • หากเป็นข้อมูลที่เปลี่ยนบ่อย ก็ต้องอัปเดตทุกตำแหน่งพร้อมกัน ทำให้มีโอกาสผิดพลาดและไม่มีประสิทธิภาพมากขึ้น
  • ในรูปแบบที่ normalize แล้ว ความสัมพันธ์ระหว่าง country กับ country_code จะถูกแยกเป็นอีกตารางหนึ่ง และตารางผู้ใช้จะอ้างอิงเพียง country_id
  • เมื่อทำ INNER JOIN ระหว่าง users กับ countries ด้วย country_id ก็จะได้รูปแบบเดิมของ user, country, country_code กลับมา
  • คำอธิบายต่อจากนี้สมมติว่า join โดยนัยตามคอลัมน์ที่มีชื่อเดียวกัน แต่จะไม่ยึดติดกับรายละเอียดไวยากรณ์ SQL อย่างเคร่งครัด

มุมมองการ implement: join ที่วนผ่านแถวและคอลัมน์

  • เมื่อมีเซตสองชุด R, S และภาคแสดง p join จะวนผ่าน r ∈ R และ s ∈ S ทุกตัว แล้วส่งออกเฉพาะกรณีที่ p(r, s) เป็นจริง
    • หาก ผลคูณคาร์ทีเซียน ของคอลเลกชันสองชุดคือการเชื่อมแถวที่เป็นไปได้ทั้งหมด join ก็คือสับเซตของสิ่งนั้นที่ตรงตามเงื่อนไข
  • หากมองโดยยึดคอลัมน์เป็นศูนย์กลาง ให้ถือ โดเมน ของแต่ละคอลัมน์เป็นเซตของค่าที่เป็นไปได้ แล้ววนผ่านชุดค่าของคอลัมน์
    • หากมี R(a, b) และ S(b, c) ก็จะวนผ่านโดเมนของ a, b, c
    • จะส่งออก [a, b, c] ก็ต่อเมื่อ (a, b) อยู่ใน R และ (b, c) อยู่ใน S

join ในฐานะความเป็นจริงทางเลือกที่เข้ากันได้

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

join ในโมเดลการเขียนโปรแกรม

  • flatMap คือฟังก์ชันที่สร้างอาร์เรย์ใหม่สำหรับแต่ละองค์ประกอบของอาร์เรย์เดิม แล้วนำผลลัพธ์มาต่อกัน และสามารถใช้ implement join ได้
    • SELECT * FROM r INNER JOIN s ON p เขียนแทนได้เป็น r.flatMap(x => s.filter(y => p(x, y)))
    • ไวยากรณ์ LATERAL ใน SQL บางรูปแบบเปลี่ยน join ให้อยู่ในรูป flatMap
  • หากด้านขวาของ LATERAL ไม่อ้างอิงคอลัมน์ฝั่งซ้าย ก็จะเทียบเท่ากับ ผลคูณคาร์ทีเซียน
    • query decorrelation อาศัยวิธีลบการอ้างอิงคอลัมน์ทางด้านขวาออกผ่านการ rewrite ต่อเนื่อง
  • ปัญหา N+1 ที่พบบ่อยใน ORM ก็สามารถอธิบายด้วย join ได้
    • หากรันคิวรีเพิ่มเติมสำหรับแต่ละแถวใน result set ในฐานข้อมูลที่ใช้ connection อย่าง Postgres ต้นทุนคงที่ของคิวรีแต่ละตัวจะสูง
    • ผลลัพธ์ของการขอให้ฐานข้อมูล “ช่วยทำการค้นหาเหล่านี้ทั้งหมด” ก็คือ join อย่างเช่น users INNER JOIN countries
    • ในฐานข้อมูลแบบ in-process อย่าง Sqlite ปัญหานี้จะน้อยกว่า

เส้นทางบนกราฟและโมเดลเชิงตรรกะ

  • เนื่องจากความสัมพันธ์คือการ “เชื่อมโยง” เซตสองชุดเข้าด้วยกัน จึงสามารถมองเป็นกราฟได้
    • ตาราง users เชื่อมเซตของชื่อผู้ใช้กับเซตของ country_id
    • ความสัมพันธ์ที่เชื่อม country_id กับรหัสประเทศสองตัวอักษรก็สามารถแสดงเป็นกราฟแยกได้เช่นกัน
  • หากเซตทางขวาของกราฟแรกและเซตทางซ้ายของกราฟที่สองใช้ vertex set เดียวกัน ก็สามารถรวมเป็นกราฟเดียวได้
  • เมื่อลิสต์เส้นทางทั้งหมดที่เริ่มจากเซตด้านซ้าย ผ่าน vertex ตรงกลาง ไปยังเซตด้านขวา สิ่งนั้นก็คือ join ของสองความสัมพันธ์
  • ในตรรกะเชิงรูปแบบ ความสัมพันธ์ถูกมองเป็น ภาคแสดง และเซตของข้อเท็จจริงที่ทำให้ชุดประโยคเป็นจริงถูกมองเป็นโมเดล
    • หาก users(A, B) และ countries(B, C, D) เป็นจริง ก็ให้มีนัยว่า Q(A, B, C, D) เป็นจริง
    • โมเดลที่ตรงตามเงื่อนไขนี้อาจมีได้หลายแบบ
    • เพื่อให้ได้ผลลัพธ์มาตรฐาน จึงเลือก โมเดลที่เล็กที่สุด ในบรรดาโมเดลที่ตรงตามเงื่อนไข
    • โมเดลที่เล็กที่สุดนี้เท่ากับผลลัพธ์การ join ของ users กับ country

join ในมุมการตรวจสอบชนิดข้อมูล

  • ระบบชนิดข้อมูลสไตล์ ML คล้ายกับ Prolog และ Datalog อย่างมาก จึงสามารถแสดงในลักษณะที่คล้าย join ได้
  • ในตัวอย่าง Rust ความสัมพันธ์ถูกนิยามเป็น trait
    • Users และ CountryCode ทำหน้าที่เป็นความสัมพันธ์
    • Smudge, Sissel, Petee, Canada, UnitedStates, CA, US ถูกนิยามเป็นชนิดข้อมูลแบบเฉพาะเจาะจง
  • การ implement trait อย่าง (Smudge, Canada): Users, (Canada, CA): CountryCode สอดคล้องกับแถวของความสัมพันธ์
  • เพื่อให้ (A, B, C) อยู่ใน join ต้องเป็นทั้ง (A, B): Users และ (B, C): CountryCode
  • test::<(Smudge, _, CA)>() ตรวจสอบชนิดข้อมูลสำเร็จ แต่ test::<(Smudge, _, US)>() ล้มเหลว เพราะไม่มีการ implement (Canada, US): CountryCode

join ในฐานะการดำเนินการของ Set monad

  • ตัวอย่าง Some และ None ใน JavaScript เริ่มจากวิธีรวม optional record
    • หาก record สองตัวมี country เดียวกัน ก็จะ merge แล้วคืนค่า Some
    • หากเข้ากันไม่ได้หรือไม่มีค่า ก็จะคืนค่า None
  • andThen ดึงค่าภายใน optional value ออกมา แล้วนำฟังก์ชัน merge ไปใช้
  • หากยังใช้ฟังก์ชัน combine เดิม แต่เปลี่ยน container เป็น Rel ก็จะสามารถประมวลผลเซตของความสัมพันธ์ได้
    • Rel.map ใช้ฟังก์ชันกับทุกแถว
    • Rel.andThen นำความสัมพันธ์ที่ได้จากแต่ละแถวมาต่อกันด้วย flatMap
  • เมื่อรัน combine เดียวกันกับความสัมพันธ์ users และความสัมพันธ์ countries จะได้ผลลัพธ์ join ที่เพิ่มรหัสประเทศให้กับ Smudge, Sissel, Petee

ความสัมพันธ์ที่อนุญาตได้ที่ใหญ่ที่สุดและ join ของลำดับบางส่วน

  • นิยามว่าความสัมพันธ์ที่สาม T ซึ่งมีคอลัมน์ทั้งหมดของความสัมพันธ์สองตัว R, S นั้น อนุญาตได้ หากไม่ประดิษฐ์ข้อมูลใหม่ขึ้นมา
    • เมื่อจำกัดแถวใดของ T ให้เหลือเฉพาะคอลัมน์ของ R แถวนั้นต้องอยู่ใน R
    • เช่นเดียวกัน เมื่อจำกัดให้เหลือเฉพาะคอลัมน์ของ S แถวนั้นก็ต้องอยู่ใน S
  • ตัวอย่างเช่น Smudge, Canada, US ไม่ได้รับอนุญาต
    • หากดูเฉพาะ country, country_code จะได้ Canada, US แต่แถวนี้ไม่มีอยู่ใน S
  • ความสัมพันธ์ว่างก็อนุญาตได้ แต่ความสัมพันธ์ที่อนุญาตได้ที่ใหญ่ที่สุดจะมี Smudge-Canada-CA, Sissel-Canada-CA, Petee-United States-US
  • ความสัมพันธ์ที่อนุญาตได้ที่ใหญ่ที่สุด นี้ก็คือ join ของสองความสัมพันธ์
  • ในมุมมองลำดับบางส่วน นิยาม R ≤ Q ดังนี้
    • Q มีคอลัมน์ทั้งหมดของ R
    • เมื่อจำกัดแต่ละแถวของ Q ให้เหลือเฉพาะคอลัมน์ของ R จะได้แถวของ R
  • ในลำดับบางส่วนนี้ จะมีขอบเขตบนน้อยสุดของสองความสัมพันธ์ R, S คือ R ∨ S และสิ่งนี้คือ join ในความหมายเดียวกับ relational join

join ในฐานะผลคูณแบบริง

  • ความสัมพันธ์สามารถแสดงในเชิงพีชคณิตได้เช่นกัน
    • หนึ่งแถวแสดงเป็น ผลคูณ ของคู่คอลัมน์-ค่า
    • หนึ่งความสัมพันธ์แสดงเป็น ผลรวม ของหลายแถว
  • ตัวอย่างเช่น เทอมที่คูณ user = Smudge กับ country_id = 1 จะเป็นหนึ่งแถว
  • มีการเพิ่มกฎเพื่อทำให้นิพจน์ง่ายลง
    • Idempotence: [x = y][x = y] = [x = y]
    • Contradiction: [x = y][x = z] = 0 if y ≠ z
  • เมื่อนำความสัมพันธ์ผู้ใช้ R กับความสัมพันธ์ lookup ประเทศ S มาคูณกัน แล้วกระจายด้วยกฎการแจกแจงและกฎการสลับที่ เทอมที่ขัดแย้งกันจะหายไป เหลือเฉพาะเทอมที่เข้ากันได้
  • นิพจน์ที่เหลือคือ Smudge-1-Canada-CA, Sissel-1-Canada-CA, Petee-2-United States-US ซึ่งก็คือ join ของสองความสัมพันธ์อย่างพอดี
  • วิธีนี้ยังสามารถมองเป็น tensor contraction ได้

1 ความคิดเห็น

 
GN⁺ 2023-07-04
ความคิดเห็นบน Hacker News
  • พอเริ่มมอง join เป็นมิติของพื้นที่ ก็เข้าใจง่ายขึ้นมาก
    ถ้าวางแต่ละมิติเป็นตารางแยกกัน เช่น Dim_X, Dim_Y, Dim_Z แล้วผูกด้วย EntityId เดียวกัน ก็จะมองได้ว่าเป็นการประกอบตำแหน่ง 3 มิติของเอนทิตีหนึ่ง
    ถ้าจะสร้าง 3 มิติ ต้องมี inner join อย่างน้อย 2 ครั้ง และมิติที่ไม่ใช่พื้นที่อย่างเวลาก็ขยายด้วยวิธีเดียวกันได้
    ถ้าไม่จำกัดช่วงเวลาเฉพาะ ก็จะกลายเป็นรายงานที่บรรจุตำแหน่งทั้งหมดที่เอนทิตีหนึ่งเคยอยู่ตามกาลเวลา
    join ประเภทอื่น ๆ ก็เข้าใจได้ง่ายขึ้นในฐานะรูปแบบแปรผันของแนวคิดนี้ ถ้าจับคอนเซปต์ได้ถึงขั้นสามารถ “หมุน” สคีมาในหัวได้

    • นึกถึง HyperDex ขึ้นมา เป็นวิธีที่แฮชค่าตามแอตทริบิวต์เข้าไปใน ปริภูมิหลายมิติ เพื่อใช้ทำดัชนี
      https://dbdb.io/db/hyperdex
    • อันนี้ดูใกล้เคียงกับ การทำ normal form แบบสุดโต่ง ของข้อมูลมากกว่า โดยปกติถ้าเป็น BCNF ก็น่าจะใช้ตารางอย่าง EntityPosition(EntityId, X, Y, Z) ไปเลย
      อย่างไรก็ตาม ในแง่การประกอบชิ้นส่วนจากหลายมิติและการจัดการ aggregation ก็ทำให้นึกถึงฝั่ง data warehouse
    • สงสัยมาตลอดว่าทำไมต้องใช้ไวยากรณ์อย่าง JOIN, INNER JOIN ด้วย การระบุตารางใน FROM แล้วเขียนเงื่อนไข join ในประโยค WHERE เหมือนสมการ ดูชัดเจนกว่ามาก
      ถ้าในประโยค FROM ซับซ้อนมี JOIN หลายตัวปนกันก็อ่านยาก และการอ่านนิพจน์เงื่อนไขที่เทียบเท่ากันใน WHERE ดูตรงไปตรงมากว่า
    • สงสัยว่าเข้าใจได้ไหมว่า join ทั้งหมดเป็นรูปแบบแปรผันของ cross join
  • มุมมองที่ 0 คือ “join เป็นตัวดำเนินการของ พีชคณิตเชิงสัมพันธ์
    https://en.m.wikipedia.org/wiki/Relational_algebra
    natural join R ⋈ S คือเซตของชุด tuple ที่มีชื่อแอตทริบิวต์ร่วมกันเท่ากัน และเป็นการดำเนินการเชิงสัมพันธ์ที่สอดคล้องกับตรรกะ AND
    มองได้ว่าเป็นผลคูณคาร์ทีเซียนที่ใช้ predicate กรองแถวที่ไม่ควรอยู่ในผลลัพธ์ออก และหลายส่วนของ SQL ก็เข้าใจได้ดีจากมุมมองนี้

    • ในการตีความเชิงฟังก์ชันของทฤษฎีความสัมพันธ์ join คือการประกอบฟังก์ชัน แปลกใจที่ไม่มีมุมมองนั้น
    • คำอธิบายว่า “ลูปซ้อนกันบนแถว” มีมุมมอง cross product + predicate อยู่ในนั้นแล้ว
  • หลายวันมานี้กำลังหาข้อมูลเรื่อง การทำงาน/การวางแผนการรันคิวรี อยู่ แต่หาเนื้อหาฝั่งการ implement อย่าง predicate, index ที่มีอยู่, join ได้ยาก
    ผลค้นหาของ Google ปนเปื้อนไปด้วยเนื้อหาวิธีใช้งาน
    เท่าที่หาได้ตอนนี้มีแค่เอกสารของ CMU Database Group ซึ่งยอดเยี่ยม

    • มีหนังสือฟรี 700 หน้าในหัวข้อนี้: “Building Query Compilers”
      https://pi3.informatik.uni-mannheim.de/~moer/querycompiler.p...
      คอร์ส “Database Systems on Modern CPU Architectures” ของ TUM ก็น่าจะช่วยได้ และเอกสารปี 2020 มีวิดีโอบรรยายครบชุด
      https://db.in.tum.de/teaching/ss20/moderndbs/?lang=en
    • ไม่รู้ว่าจะลึกตามที่ต้องการไหม แต่ภาพรวมการ optimize และเอกสาร query planner ของ SQLite ก็น่าอ่าน
      https://www.sqlite.org/optoverview.html
      https://www.sqlite.org/queryplanner.html
    • เวลาแบบนี้มักแนะนำให้อ่าน เอกสาร Postgres กับซอร์สโค้ด ซอร์สก็อ่านค่อนข้างง่ายด้วย
      https://www.postgresql.org/docs/current/planner-optimizer.ht...
    • นี่เป็นหัวข้อที่ค่อนข้างเฉพาะทาง จึงหาตำราดี ๆ ได้ยาก และขึ้นอยู่กับว่าอยากลงลึกแค่ไหน รวมถึงสนใจส่วนไหน
      การรันคิวรีกับการวางแผนคิวรีแทบจะเป็นคนละเรื่องกัน
      สำหรับเปเปอร์ด้านการ optimize join ผมยังคิดว่าเปเปอร์ Selinger ต้นฉบับดีที่สุด
      https://courses.cs.duke.edu/compsci516/cps216/spring03/paper...
      แม้จะไม่รองรับ outer join และมีเทคนิคที่มีประสิทธิภาพกว่านี้ออกมาแล้ว แต่สำหรับคนที่ดู optimizer สาย System R ก็ยังอ่านแล้วคุ้นเคย
      src/backend/optimizer/README ของ Postgres ก็มีเนื้อหาหลายอย่างที่หาอ่านที่อื่นได้ยาก
      บทเรียนของ Andy Pavlo จาก CMU แทบจะเป็นหนึ่งในไม่กี่แหล่งออนไลน์ที่อธิบายเรื่องนี้ และ PDF “Building Query Compilers” แม้จะยังไม่สมบูรณ์ แต่รวบรวมเปเปอร์แกนหลักของ Moerkotte และคนอื่น ๆ ไว้ จึงน่าอ่านถ้าจะทำ implementation สมัยใหม่
      การหา index ที่ใช้ได้มักไม่ยาก เพราะดูว่ามี sargable predicate หรือไม่ก็พอ แต่การประมาณ selectivity นั้นยาก และการประมาณ selectivity หลังผ่าน join แทบจะเป็นหนึ่งในปัญหาที่ยากที่สุดของ optimizer
      เช่น ถ้ามี A=x AND B=y AND C=z และมีข้อมูล selectivity/cardinality ของ index (A,B), (B,C) เท่านั้น การจะประมาณ selectivity ของทั้งสามเงื่อนไขก็ไม่ใช่เรื่องง่าย
      ยังมีเปเปอร์ที่ถึงขั้นเรียกร้องตัวแก้ปัญหา “second-order cone programming” เพื่อแก้เรื่องนี้ด้วย
    • ลองเติม relational algebra หน้า query planning ในคำค้นดูแล้ว แค่มองคร่าว ๆ ก็ได้ผลลัพธ์ฝั่ง implementation มากขึ้น
  • วิธีที่ 14 คือ multi-join และยังเรียกว่า “worst-case optimal join” ด้วย แต่ชื่อนี้ไม่ค่อยดีนัก
    หมายถึงการ join ตารางตั้งแต่ 3 ตารางขึ้นไปเข้าด้วยกันโดยไม่สร้างผลลัพธ์กลาง แทนที่จะ join ตารางทีละสองตารางแล้วสร้างผลลัพธ์กลางไปเรื่อย ๆ
    บทความบล็อกที่เกี่ยวข้องและวิดีโอสั้นอยู่ที่ https://relational.ai/blog/dovetail-join และเปเปอร์ต้นฉบับอยู่ที่ https://dl.acm.org/doi/pdf/10.1145/3180143
    ผมทำงานที่ RelationalAI และพวกเรากับบริษัทฐานข้อมูลสตาร์ทอัพอีกไม่กี่แห่งกำลังนำอัลกอริทึม join ใหม่นี้ ซึ่งมีการวิจัยในวงวิชาการมาราว 10 ปี ออกสู่ตลาด

    • บทความแนะนำ WCOJ ของ Justin ก็ค่อนข้างดี
      https://justinjaffray.com/a-gentle-ish-introduction-to-worst...
    • ถ้าเปลี่ยนอินพุตเป็นรูปปฏิเสธ หรือก็คือ complement ของเซต AND ของ join จะกลายเป็น NOR และ Tetris ใช้ประโยชน์จากจุดนี้
      ขอบเขต worst-case จะไม่ได้แน่นกว่าสำหรับ WCOJ แบบ stateless/streaming แต่ข้อมูลจริงมักมี box certificate ที่เล็กกว่ามาก
      ผมยังไม่เห็นว่า Dovetail join รองรับ recursive query หรือก็คือ datalog ใด ๆ ที่ระบุแค่ความสัมพันธ์ผลลัพธ์ แล้วให้เอนจินจัดการความสัมพันธ์กลางเองหรือไม่
      เลยสงสัยว่ารองรับ query แบบนั้นหรือเปล่า
  • ควรมีบทความแบบนี้มากขึ้น ที่แสดงความละเอียดอ่อนของ relational model ให้เห็น โดยเฉพาะกับ นักพัฒนาระดับแอปพลิเคชัน
    คำอธิบายและการสำรวจจากมุมมอง functional programming ก็กระชับและน่าเชื่อถือ

  • ดูเหมือนจะพลาดโอกาสอีกครั้งในการสอนเรื่อง ปัญหา N+1
    การ join กับ non-clustered index ก็ยังเป็น N+1 อยู่ เพียงแต่ไม่ใช่ N+1 ที่วิ่งไปมาระหว่างเครือข่ายกับดิสก์ แต่เป็น N+1 บนดิสก์เท่านั้น

    • ฟังดูเหมือนแนวว่า “ควรพูดถึงปัญหา X ที่ฉันสนใจ และถ้าบทความยาวขึ้นก็ไม่เป็นไร”
  • inner join คือ Cartesian product ที่มีเงื่อนไขกำกับ

    • มีความต่างด้านประสิทธิภาพมากระหว่างการสร้าง Cartesian product แล้วค่อยกรองด้วยเงื่อนไข กับการสร้างเงื่อนไขโดยตรง
      inner join ที่มีเงื่อนไข equi-join จะสร้างเงื่อนไขโดยตรง ส่วนเงื่อนไข join แบบไม่เท่ากันจำเป็นต้องประเมินจริง
  • เป็นคำอธิบายที่ดี คำว่า “วิธีที่ถูกต้องคือการ normalize ตาราง” นั้นถูกต้องสำหรับ transactional database แต่ใน data warehouse การ denormalize ในระดับหนึ่งเป็นที่ยอมรับกันอย่างแพร่หลาย

  • พอดูตัวอย่าง normalization แล้วนึกถึงสมัยก่อนที่ออกแบบตารางโดยคิดว่า primary key แบบตัวเลขเร็วกว่าสตริง
    ผลคือมี id ที่ไม่มีความหมายเกิดขึ้น และถ้าต้องการค่าที่ unique จริง ๆ ก็ต้อง join
    วันหนึ่งผมตระหนักว่า ถ้าใช้ unique key เดียวกันในสองตาราง ก็ลดการ join ได้ ซึ่งเรียบง่ายแต่ได้ผลมาก

    • ถึงอย่างนั้นก็ยังชอบให้แต่ละตารางมีฟิลด์ id ที่ unique อยู่ดี ช่วยเรื่อง logging และไม่ต้องคอยสนใจ “คีย์จริง” ที่ประกอบด้วยหลายฟิลด์
      แต่จะสร้าง unique index ให้ค่าที่เป็นสตริง และที่สำคัญกว่านั้นคือผูก integrity constraint ไว้กับฝั่งนั้น
      ตารางที่เต็มไปด้วยสตริงที่มีความหมายอ่านง่ายกว่าตารางที่เต็มไปด้วย id ตัวเลขหรือ UUID มาก