รูปแบบ MySQL หนึ่งต่อหลายถึง JSON

ฉันมีตาราง MySQL สองตาราง:

User (id, name)
Sale (id, user, item)

โดยที่ Sale(user) เป็นคีย์ต่างประเทศของ User(id) ดังนั้นนี่คือความสัมพันธ์แบบหนึ่งต่อกลุ่ม (ผู้ใช้รายหนึ่งสามารถสร้างยอดขายได้มาก)

ฉันกำลังพยายามรับสิ่งนี้จากฐานข้อมูลและส่งคืนในรูปแบบ JSON สำหรับผู้ใช้หลายคน ดังนั้นมันจะมีลักษณะดังนี้:

[
  {
    "id": 1,
    "name": "User 1",
    "sales": [
      {
        "id": 1,
        "item": "t-shirt"
      },
      {
        "id": 2,
        "item": "jeans"
      }
    ]
  },
  {
    "id": 2,
    "name": "User 2",
    "sales": [
      {
        "id": 3,
        "item": "sweatpants"
      },
      {
        "id": 4,
        "item": "gloves"
      }
    ]
  }
]

โดยที่เอนทิตี "การขาย" ซ้อนอยู่ภายในเอนทิตีของ "ผู้ใช้" ที่เกี่ยวข้อง

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


person Jordan    schedule 26.01.2019    source แหล่งที่มา


คำตอบ (1)


นี่คือแบบสอบถาม SQL ที่อาจตรงตามความต้องการของคุณ โดยใช้ ฟังก์ชันรวม MySQL JSON_ARRAYAGG() เพื่อสร้างอาร์เรย์ของออบเจ็กต์ JSON (ซึ่งสร้างขึ้นโดยใช้ JSON_OBJECT())

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

SELECT
  JSON_ARRAYAGG(JSON_OBJECT('id', u.id, 'name', u.name, 'sales', s.sales))
FROM
    user u
    LEFT JOIN (
        SELECT 
            user, 
            JSON_ARRAYAGG(JSON_OBJECT('id', id, 'item', item)) sales 
        FROM sale 
        GROUP BY user
    ) s ON s.user = u.id

สาธิตบน DB Fiddle

หากคุณล้อมค่าที่ส่งคืนด้วย JSON_PRETTY ผลลัพธ์จะเป็นดังนี้:

[
  {
    "id": 1,
    "name": "User 1",
    "sales": [
      {
        "id": 1,
        "item": "t-shirt"
      },
      {
        "id": 2,
        "item": "jeans"
      }
    ]
  },
  {
    "id": 2,
    "name": "User 2",
    "sales": [
      {
        "id": 3,
        "item": "sweatpants"
      },
      {
        "id": 4,
        "item": "gloves"
      }
    ]
  }
]

แก้ไข : นี่คือโซลูชัน (น่าเกลียด) สำหรับ MySQL ‹ 5.7 โดยที่ไม่รองรับ JSON มันอาศัยฟังก์ชันการจัดการสตริงเท่านั้น โปรดทราบว่าการดำเนินการนี้จะใช้ได้ตราบใดที่ช่อง varchar ไม่มีอักขระ " :

SELECT
    CONCAT(
        '[', 
        GROUP_CONCAT( CONCAT( '{ "id":', u.id, ', "name":"', u.name, '", "sales":', s.sales, ' }' )  SEPARATOR ', ' ),
        ']'
    )
FROM 
    user u
    LEFT JOIN (
        SELECT 
            user, 
            CONCAT( 
               '[', 
                GROUP_CONCAT( CONCAT( '{ "id":', id, ', "item":"', item, '" }' ) SEPARATOR ', '),
                ']'
            ) sales 
    FROM sale
    GROUP BY user ) s ON s.user = u.id

สาธิตเกี่ยวกับ DB Fiddle

person GMB    schedule 26.01.2019
comment
นี่มันยอดเยี่ยมมาก มีวิธีให้เติมฟิลด์ JSON_OBJECT ด้วยฟิลด์ตารางโดยอัตโนมัติหรือไม่ ดังนั้นฉันจึงไม่จำเป็นต้องอัปเดตแบบสอบถามหากฉันเพิ่มฟิลด์ลงในตาราง User เป็นต้น - person Jordan; 27.01.2019
comment
อ่า ฉันเพิ่งรู้ว่าไม่มี JSON_ARRAYAGG จนกระทั่ง MySQL 5.7 ฉันติดอยู่ที่ 5.6 เนื่องจากฉันใช้ AWS Aurora Serverless มีข้อเสนอแนะอื่นๆ อีกไหม? - person Jordan; 27.01.2019
comment
@Jordan : ดูเหมือนว่า ตอนนี้ Aurora รองรับ MySQL 5.7... - person GMB; 27.01.2019
comment
การบรรลุสิ่งที่คุณต้องการใน MySQL 5.6 จะซับซ้อนมากขึ้นเนื่องจากไม่มีการรองรับ JSON ในเวอร์ชันนี้... เราจะต้องทำโดยใช้การต่อข้อมูลและการรวมสตริงล้วนๆ... - person GMB; 27.01.2019
comment
@Jordan: อัปเดตคำตอบของฉันด้วยวิธีแก้ปัญหาด้วยฟังก์ชันการต่อสตริง /aggregation เท่านั้น - person GMB; 27.01.2019
comment
Aurora (อินสแตนซ์) รองรับ แต่ Aurora (ไร้เซิร์ฟเวอร์) ไม่รองรับ 5.7 - person Jordan; 28.01.2019
comment
@Jordan: โปรดตรวจสอบคำถามที่สองของฉันซึ่งเหมาะกับกรณีการใช้งานของคุณหรือไม่ ฉันทดสอบบน MySQL 5.6 - person GMB; 28.01.2019
comment
ขอบคุณสำหรับข้อความค้นหาที่อัปเดต น่าเสียดายที่ฉันไม่สามารถรับประกันได้ว่าจะไม่มีอักขระเครื่องหมายคำพูดในฟิลด์ ดังนั้นฉันคิดว่าฉันจะต้องดำเนินการด้วยวิธีที่มีประสิทธิภาพน้อยกว่ามาก (เว้นแต่คุณจะมีแนวคิดอื่นใด) - person Jordan; 28.01.2019