Hive Join & SubQuery Tutorial พร้อมตัวอย่าง

⚡ สรุปอย่างชาญฉลาด

Hive join จะรวมแถวจากสองตารางขึ้นไปโดยใช้คอลัมน์ที่ตรงกัน และ subquery จะซ้อนคำสั่ง SQL หนึ่งไว้ภายในอีกคำสั่งหนึ่ง ดังนั้นในที่นี้จึงแสดงตัวอย่างทั้งสองอย่างบนตารางตัวอย่างสองตารางที่โหลดมาจากไฟล์ข้อความธรรมดา

  • 🧱 ตารางตัวอย่างสองตาราง: sample_joins เก็บรายละเอียดลูกค้า และ sample_joins1 เก็บรายละเอียดคำสั่งซื้อ โดยเชื่อมโยงกันด้วยคอลัมน์ Id ที่ใช้ร่วมกัน
  • 🔗 การเชื่อมต่อสี่ประเภท: การเชื่อมต่อแบบ Inner join, Left outer join, Right outer join และ Full outer join แต่ละแบบจะคงชุดแถวที่ไม่ตรงกันที่แตกต่างกันไว้
  • ค่า NULL แสดงถึงช่องว่าง: การเชื่อมแบบ Outer Join จะส่งคืนแถวแม้ว่าจะไม่มีการจับคู่ โดยจะเติมค่า NULL ลงในทุกคอลัมน์จากฝั่งที่ไม่มีการจับคู่
  • 🔁 ความสำคัญของการสั่งซื้อ: การเชื่อมต่อไม่เป็นไปตามกฎการสลับที่และเป็นไปตามกฎการจัดกลุ่มทางซ้าย ดังนั้นการสลับตำแหน่งจึงเป็นไปไม่ได้ping ตารางจะเปลี่ยนแปลงผลลัพธ์ของการเชื่อมต่อภายนอก (outer join)
  • 🧮 แบบสอบถามย่อยซ้อนแบบสอบถามหลัก: คำสั่งย่อยจะถูกเขียนไว้ในส่วน FROM หรือส่วน WHERE และคำสั่งหลักจะขึ้นอยู่กับค่าที่คำสั่งย่อยนั้นส่งคืนมา
  • 📜 TRANSFORM ฝังสคริปต์ไว้: สคริปต์ map และ reduce แบบกำหนดเองจะทำงานผ่านเงื่อนไข TRANSFORM เมื่อไม่มีฟังก์ชันในตัวที่เหมาะสม

ตัวอย่างการใช้ join และ subquery ใน Hive

เข้าร่วมแบบสอบถาม

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

  • sample_joins (เกี่ยวข้องกับรายละเอียดลูกค้า)
  • sample_joins1 (เกี่ยวข้องกับรายละเอียดคำสั่งซื้อที่พนักงานสั่งซื้อ)

ขั้นตอน 1) สร้างตาราง “sample_joins” โดยมีคอลัมน์ชื่อ Id, Name, Age, address และ salary ของพนักงาน ภาพหน้าจอด้านล่างแสดงคำสั่ง CREATE TABLE และการยืนยันการสร้างตาราง

คำสั่ง Hive CREATE TABLE สำหรับตารางลูกค้า sample_joins

ขั้นตอน 2) กำลังโหลดและแสดงข้อมูล ภาพหน้าจอถัดไปแสดงคำสั่งโหลดตามด้วยเนื้อหาของตาราง

กำลังโหลดไฟล์ Customers.txt ลงใน sample_joins และแสดงแถวที่โหลดแล้ว

จากภาพหน้าจอข้างต้น:

  1. กำลังโหลดข้อมูลลงใน Sample_joins จาก Customers.txt
  2. กำลังแสดงเนื้อหาตาราง example_joins

ขั้นตอน 3) ขั้นตอนการสร้างตาราง sample_joins1 จากนั้นโหลดและแสดงข้อมูลจากตารางดังกล่าว ดังที่แสดงในภาพหน้าจอด้านล่าง

กำลังสร้าง sample_joins1 โหลด orders.txt และแสดงแถวคำสั่งซื้อ

จากภาพหน้าจอข้างต้น เราสามารถสังเกตได้ดังต่อไปนี้:

  1. สร้างตาราง sample_joins1 โดยมีคอลัมน์ Orderid, Date1, Id และ Amount
  2. กำลังโหลดข้อมูลลงใน Sample_joins1 จาก orders.txt
  3. กำลังแสดงบันทึกที่มีอยู่ใน Sample_joins1

ต่อไป เราจะมาดูประเภทต่างๆ ของการเชื่อมต่อตารางที่เราสร้างขึ้น ก่อนหน้านั้น คุณต้องพิจารณาประเด็นต่อไปนี้เกี่ยวกับการเชื่อมต่อตาราง

ข้อควรระวังบางประการในการใช้คำสั่ง join:

  • การเชื่อมต่อแบบเท่ากันเท่านั้นที่อนุญาตในการเชื่อมต่อแบบ JOIN
  • สามารถรวมตารางมากกว่าสองตารางในแบบสอบถามเดียวกันได้
  • การเชื่อมต่อแบบ LEFT, RIGHT และ FULL OUTER มีไว้เพื่อให้ควบคุมเงื่อนไข ON ได้มากขึ้น ในกรณีที่ไม่มีการจับคู่
  • การเชื่อมต่อไม่เป็นไปตามคุณสมบัติการสลับที่
  • การรวมจะเชื่อมโยงด้านซ้ายโดยไม่คำนึงว่าจะเป็นการรวม LEFT หรือ RIGHT

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

การรวมประเภทต่างๆ

การเชื่อมต่อมี 4 ประเภท ได้แก่:

  • การเข้าร่วมภายใน
  • การรวมภายนอกด้านซ้าย
  • การรวมภายนอกด้านขวา
  • การรวมภายนอกแบบเต็ม

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

การเข้าร่วมภายใน

ข้อมูลที่เหมือนกันในทั้งสองตารางจะถูกดึงมาโดยใช้การเชื่อมต่อภายใน (inner join) นี้ ผลลัพธ์ในภาพหน้าจอด้านล่างจะมีเฉพาะลูกค้าที่มีคำสั่งซื้อที่ตรงกันเท่านั้น

ผลลัพธ์จากการใช้ Hive inner join จะแสดงเฉพาะลูกค้าที่มีคำสั่งซื้อที่ตรงกันเท่านั้น

จากภาพหน้าจอข้างต้น เราสามารถสังเกตได้ดังต่อไปนี้:

  1. ในที่นี้เรากำลังทำการเชื่อมต่อตารางโดยใช้คำสั่ง JOIN ระหว่างตาราง sample_joins และ sample_joins1 โดยมีเงื่อนไขการจับคู่ (c.Id = o.Id)
  2. ผลลัพธ์จะแสดงข้อมูลที่เหมือนกันในทั้งสองตาราง โดยเลือกจากเงื่อนไขที่ระบุไว้ในคำสั่งค้นหา

ค้นหา:

SELECT c.Id, c.Name, c.Age, o.Amount FROM sample_joins c JOIN sample_joins1 o ON(c.Id=o.Id);

เข้าร่วมด้านนอกซ้าย

  • ไฮฟ์คิวแอล LEFT OUTER JOIN จะส่งคืนแถวทั้งหมดจากตารางด้านซ้าย แม้ว่าจะไม่มีข้อมูลที่ตรงกันในตารางด้านขวา
  • หากเงื่อนไข ON ตรงกับข้อมูลศูนย์รายการในตารางด้านขวา การเชื่อมต่อก็ยังคงส่งคืนข้อมูลหนึ่งรายการในผลลัพธ์ โดยมีค่า NULL ในแต่ละคอลัมน์จากตารางด้านขวา

ภาพหน้าจอข้างล่างแสดงให้เห็นว่าลูกค้าทุกคนปรากฏตัว รวมถึงลูกค้าที่ไม่ได้สั่งซื้อสินค้าด้วย

ผลลัพธ์จาก Hive left outer join แสดงค่า NULL สำหรับลูกค้าที่ไม่มีคำสั่งซื้อ

จากภาพหน้าจอข้างต้น เราสามารถสังเกตได้ดังต่อไปนี้:

  1. ในที่นี้เรากำลังทำการเชื่อมตารางโดยใช้คำสั่ง “LEFT OUTER JOIN” ระหว่างตาราง sample_joins และ sample_joins1 โดยมีเงื่อนไขการจับคู่ (c.Id = o.Id) ตัวอย่างเช่น ในที่นี้เราใช้รหัสพนักงานเป็นข้อมูลอ้างอิง โดยจะตรวจสอบว่ารหัสพนักงานนั้นเหมือนกันทั้งในตารางด้านขวาและตารางด้านซ้ายหรือไม่ ซึ่งทำหน้าที่เป็นเงื่อนไขการจับคู่
  2. ผลลัพธ์จะแสดงระเบียนที่เลือกตามเงื่อนไขที่ระบุในแบบสอบถาม ค่า NULL ในผลลัพธ์ข้างต้นคือคอลัมน์ที่ไม่มีค่าจากตารางด้านขวา ซึ่งก็คือ sample_joins1

ค้นหา:

SELECT c.Id, c.Name, o.Amount, o.Date1 FROM sample_joins c LEFT OUTER JOIN sample_joins1 o ON(c.Id=o.Id)

เข้าร่วมด้านนอกขวา

  • คำสั่ง RIGHT OUTER JOIN ใน HiveQL จะส่งคืนแถวทั้งหมดจากตารางด้านขวา แม้ว่าจะไม่มีข้อมูลที่ตรงกันในตารางด้านซ้ายก็ตาม
  • หากเงื่อนไข ON ตรงกับข้อมูลศูนย์รายการในตารางด้านซ้าย การเชื่อมต่อก็ยังคงส่งคืนข้อมูลหนึ่งรายการในผลลัพธ์ โดยมีค่า NULL ในแต่ละคอลัมน์จากตารางด้านซ้าย
  • การเชื่อมตารางแบบ RIGHT จะส่งคืนข้อมูลจากตารางด้านขวาและข้อมูลที่ตรงกันจากตารางด้านซ้ายเสมอ หากตารางด้านซ้ายไม่มีค่าที่ตรงกับคอลัมน์นั้น จะส่งคืนค่า NULL ในตำแหน่งนั้น

ภาพหน้าจอด้านล่างแสดงภาพสะท้อนของผลลัพธ์ก่อนหน้า: คำสั่งซื้อทั้งหมดจะปรากฏขึ้น ไม่ว่าจะตรงกันหรือไม่ก็ตาม

เอาต์พุต Hive right outer join keeping ทุกแถวคำสั่งซื้อจาก sample_joins1

จากภาพหน้าจอข้างต้น เราสามารถสังเกตได้ดังต่อไปนี้:

  1. ในที่นี้เรากำลังทำการเชื่อมต่อตารางโดยใช้คำสั่ง “RIGHT OUTER JOIN” ระหว่างตาราง sample_joins และ sample_joins1 โดยมีเงื่อนไขการจับคู่ (c.Id = o.Id)
  2. ผลลัพธ์จะแสดงระเบียนที่เลือกโดยการตรวจสอบเงื่อนไขที่ระบุไว้ในคำสั่งค้นหา

ค้นหา:

  SELECT c.Id, c.Name, o.Amount, o.Date1 FROM sample_joins c RIGHT OUTER JOIN sample_joins1 o ON(c.Id=o.Id)

เข้าร่วมภายนอกเต็มรูปแบบ

คำสั่งนี้จะรวมข้อมูลจากทั้งสองตาราง sample_joins และ sample_joins1 โดยอิงตามเงื่อนไข JOIN ที่ระบุในคำสั่ง SQL

ฟังก์ชันนี้จะส่งคืนข้อมูลทั้งหมดจากทั้งสองตาราง และเติมค่า NULL ลงในคอลัมน์ที่ไม่มีค่าตรงกันในทั้งสองฝั่ง ดังที่แสดงในภาพหน้าจอด้านล่าง

ผลลัพธ์ของ Hive full outer join ที่รวมแถวที่ไม่ตรงกันจากทั้งสองตาราง

จากภาพหน้าจอข้างต้น เราสามารถสังเกตได้ดังต่อไปนี้:

  1. ในที่นี้เรากำลังทำการเชื่อมต่อตารางโดยใช้คำสั่ง “FULL OUTER JOIN” ระหว่างตาราง sample_joins และ sample_joins1 โดยมีเงื่อนไขการจับคู่ (c.Id = o.Id)
  2. ผลลัพธ์จะแสดงข้อมูลทั้งหมดที่มีอยู่ในทั้งสองตาราง โดยเลือกข้อมูลตามเงื่อนไขที่ระบุไว้ในคำสั่ง SQL ค่า NULL ในผลลัพธ์นี้แสดงถึงค่าที่หายไปในคอลัมน์ของทั้งสองตาราง

ค้นหา:

SELECT c.Id, c.Name, o.Amount, o.Date1 FROM sample_joins c FULL OUTER JOIN sample_joins1 o ON(c.Id=o.Id)

แบบสอบถามย่อย

การเชื่อมตาราง (Join) จะนำตารางมาวางเคียงข้างกัน ส่วนการสืบค้นย่อย (Subquery) ทำงานแตกต่างออกไป คือมันจะซ้อนการสืบค้นหนึ่งไว้ภายในอีกการสืบค้นหนึ่ง เพื่อให้การสืบค้นภายนอกสามารถทำงานกับผลลัพธ์ที่คำนวณไว้แล้วได้

คำสั่งค้นหาที่อยู่ภายในคำสั่งค้นหาหลักเรียกว่า คำสั่งค้นหาย่อย คำสั่งค้นหาหลักจะขึ้นอยู่กับค่าที่ได้จากคำสั่งค้นหาย่อยนั้น

ซับเควรีสามารถแบ่งออกได้เป็นสองประเภท:

  • แบบสอบถามย่อยในส่วน FROM
  • แบบสอบถามย่อยในส่วน WHERE

ควรใช้เมื่อใด:

  • เพื่อให้ได้ค่าเฉพาะที่รวมจากค่าสองคอลัมน์จากตารางที่ต่างกัน
  • ความสัมพันธ์ระหว่างค่าในตารางหนึ่งกับค่าในตารางอื่น
  • การตรวจสอบเปรียบเทียบค่าในคอลัมน์หนึ่งกับตารางอื่นๆ

ไวยากรณ์:

Subquery in FROM clause
SELECT <column names 1, 2…n>From (SubQuery) <TableName_Main >
Subquery in WHERE clause
SELECT <column names 1, 2…n> From<TableName_Main>WHERE col1 IN (SubQuery);

ตัวอย่าง:

SELECT col1 FROM (SELECT a+b AS col1 FROM t1) t2

ในที่นี้ t1 และ t2 คือชื่อตาราง คำสั่งภายในคือซับควอรีที่ดำเนินการกับตาราง t1 โดยที่ a และ b คือคอลัมน์ที่เพิ่มเข้ามาในซับควอรีและกำหนดให้กับ col1 ซึ่ง col1 คือค่าคอลัมน์ที่มีอยู่ในตารางหลัก คอลัมน์ “col1” ในซับควอรีนี้เทียบเท่ากับค่าในคอลัมน์ col1 ของตารางหลัก

การฝังสคริปต์ที่กำหนดเอง

ในขณะที่ซับเควรีปรับเปลี่ยนรูปแบบข้อมูลโดยใช้ HiveQL เพียงอย่างเดียว สคริปต์ที่ฝังอยู่จะส่งแถวข้อมูลไปยังโค้ดที่เขียนขึ้นภายนอก Hive

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

ควรเลือกใช้สคริปต์แบบฝังเมื่อใด:

  • ในกรณีที่ข้อกำหนดเฉพาะของลูกค้าทำให้ผู้พัฒนาต้องเขียนและใช้งานสคริปต์ใน Hive
  • ในกรณีที่ฟังก์ชันในตัวของ Hive ไม่สามารถใช้งานได้กับความต้องการเฉพาะด้านใดด้านหนึ่ง

ด้วยเหตุนี้ Hive จึงใช้คำสั่ง TRANSFORM เพื่อฝังสคริปต์ทั้ง map และ reducer ไว้ในตัวเดียวกัน

ในสคริปต์แบบกำหนดเองที่ฝังอยู่เหล่านี้ เราต้องคำนึงถึงประเด็นต่อไปนี้:

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

ตัวอย่างสคริปต์ฝังตัว:

FROM (
	FROM pv_users
	MAP pv_users.userid, pv_users.date
	USING 'map_script'
	AS dt, uid
	CLUSTER BY dt) map_output

INSERT OVERWRITE TABLE pv_users_reduced
	REDUCE map_output.dt, map_output.uid
	USING 'reduce_script'
	AS date, count;

จากสคริปต์ข้างต้น เราสามารถสังเกตได้ดังต่อไปนี้ นี่เป็นเพียงสคริปต์ตัวอย่างเพื่อความเข้าใจเท่านั้น

  • pv_users คือตารางผู้ใช้ ซึ่งมีฟิลด์ต่างๆ เช่น userid และ date ตามที่กล่าวไว้ใน map_script
  • สคริปต์ตัวลดทอนถูกกำหนดโดยวันที่และจำนวนของตาราง pv_users

คำถามที่พบบ่อย

ในอดีตไม่สามารถทำได้ แต่ตั้งแต่ Hive เวอร์ชัน 2.2.0 เป็นต้นไป อนุญาตให้ใช้การแสดงออกที่ซับซ้อนในส่วน ON ได้ (HIVE-15211) ดังนั้นเงื่อนไขความไม่เท่ากันและช่วงจึงใช้งานได้ ในเวอร์ชันก่อนหน้า ส่วน ON ต้องเป็นการทดสอบความเท่ากันเท่านั้น และเงื่อนไขอื่นๆ จะต้องอยู่ในส่วน WHERE

การเชื่อมต่อแบบ Map Join จะโหลดตารางที่มีขนาดเล็กกว่าลงในหน่วยความจำและข้ามขั้นตอนการลดขนาดไปโดยสิ้นเชิง Hive จะเลือกวิธีการนี้โดยอัตโนมัติเมื่อ hive.auto.convert.join เป็นจริงและตารางมีขนาดตรงตามเกณฑ์ที่กำหนดไว้ ซึ่งทำให้การเชื่อมต่อจากตารางขนาดเล็กไปยังขนาดใหญ่เร็วขึ้นมาก

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

บางส่วนครับ ตั้งแต่ Hive เวอร์ชัน 0.13 เป็นต้นไป ตัวดำเนินการ IN, NOT IN, EXISTS และ NOT EXISTS ยอมรับซับเควรีในส่วน WHERE รวมถึงซับเควรีที่มีความสัมพันธ์กันด้วย แต่ยังมีข้อจำกัดอยู่ ดังนั้นความสัมพันธ์ที่ไม่รองรับมักจะถูกเขียนใหม่เป็น JOIN แทน

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

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

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

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

สรุปโพสต์นี้ด้วย: