รับหลายคอลัมน์จากแบบสอบถามย่อยที่เลือก


24
SELECT 
   *, 
   p.name AS name, 
   p.image, 
   p.price,
   ( 
       SELECT ps.price 
       FROM product_special ps 
       WHERE p.id = ps.id
         AND ps.date < NOW() 
       ORDER BY ps.priority ASC, LIMIT 1
   ) AS special_price,
   ( 
       SELECT ps.date 
       FROM product_special ps 
       WHERE p.id = ps.id
         AND ps.date < NOW() 
       ORDER BY ps.priority ASC, LIMIT 1
   ) AS date
FROM product p LEFT JOIN product_special ps ON (p.id = ps.id)

อย่างที่คุณเห็นฉันกำลังทำแบบสอบถามย่อยเดิมซ้ำเพื่อให้คอลัมน์อื่นออกมา ฉันสงสัยว่าจะมีวิธีที่ดีกว่าในการทำเช่นนี้?

idเป็นคีย์หลักในทั้งสองตาราง ฉันไม่มีปัญหาในการสร้างproduct_special.priority ที่ไม่ซ้ำหากสามารถช่วยได้

คำตอบ:


11

การรวมกันproduct_special.id, product_special.priorityถือว่าเป็นเอกลักษณ์

 SELECT p.*, special_price,special_date
 FROM product p
 LEFT JOIN 
 (
     SELECT ps.id, ps.price as special_price, ps.`date` as special_date
     FROM product_special ps
     INNER JOIN 
     (
       SELECT id, MIN(priority) as min_priority 
       FROM product_special
       GROUP BY id
     ) ps2 
     ON (ps2.id = ps.id)
 )a ON (a.id=p.id)

5

ยกเว้นว่าคุณต้องการส่งคืนฟิลด์เป็น special_price.price และ date.date ทำไมไม่ตั้งชื่อภายในเคียวรีย่อย? เช่น

SELECT p.*, p.name AS  name, p.image, p.price, ps.*
FROM product p
LEFT JOIN
   (SELECT
      psi.price as special_price, psi.date as my_date 
    FROM product_special psi
    WHERE 
      p.id = psi.id AND
      psi.date < NOW()
    ORDER BY psi.priority ASC, LIMIT 1
   ) AS ps ON
  p.id = ps.id

ภาษาข้อความค้นหาของคุณมีฟังก์ชั่นรวมครั้งแรก () หรือไม่ ไม่แน่ใจว่าคุณสามารถทำให้ PK ของ product_special เป็นการรวมกันระหว่าง id และลำดับความสำคัญ (เรียงลำดับ ASC ทั้งคู่) และเปลี่ยนส่วนคำสั่ง ORDER เป็นGROUP BY id, psi.priority

คุณอาจสามารถลบคำสั่งย่อย ORDER BY ทั้งหมดและใช้งานได้ HAVING MIN(psi.priority)


2

โปรดทราบว่ากลไก "cross Apply" จาก SQL Server จะแก้ปัญหานี้ แต่ไม่มีใน PostgreSQL โดยทั่วไปมันเป็นวิธีแก้ปัญหาของพวกเขาสำหรับวิธีการส่งผ่านพารามิเตอร์ (ซึ่งมักจะอ้างอิงถึงคอลัมน์ภายนอกการแสดงออกของตารางปัจจุบัน) ไปยังฟังก์ชั่นที่เรียกว่าเป็นนิพจน์ตารางในส่วนคำสั่ง FROM แต่กลับกลายเป็นว่ามีประโยชน์สำหรับทุกสถานการณ์ที่คุณต้องการหลีกเลี่ยงการซ้อนย่อยเคียวรีอีกระดับหรือย้ายสิ่งต่าง ๆ จากส่วนคำสั่ง FROM ไปยังส่วนคำสั่ง SELECT PostgreSQL ทำให้มันเป็นไปได้ในการทำสิ่งนี้โดยการยกเว้น - คุณสามารถส่งพารามิเตอร์แบบนั้นได้ถ้านิพจน์เป็นการเรียกใช้ฟังก์ชันอย่างง่าย แต่ไม่พูดถึง SELECT ที่ฝังไว้อย่างเคร่งครัด ดังนั้น

left join highestPriorityProductSpecial(p.id) on true

ก็โอเค แต่ไม่ใช่

left join (select * from product_special ps where ps.id = p.id order by priority desc limit 1) on true

แม้ว่าความหมายของฟังก์ชั่นนั้นแน่นอน

ในความเป็นจริงแล้วนั่นคือทางออกที่สะดวก (ใน 9.1 อย่างน้อย): สร้างฟังก์ชั่นเพื่อแยกแถวลำดับความสำคัญสูงสุดของคุณ

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


6
cross apply คือมีอยู่ใน Postgres เริ่มต้นด้วย 9.3 (ปล่อยตัวในปี 2013) แต่พวกเขาเลือกที่จะเป็นไปตามมาตรฐาน SQL และใช้มาตรฐานlateralผู้ประกอบการ ในข้อความค้นหาที่สองของคุณแทนที่left joinด้วยleft join lateral
a_horse_with_no_name

2

ลองใช้คำสั่ง SQL ต่อไปนี้:

SELECT p.name,p.image,p.price,pss.price,pss.date
FROM Product p OUTER APPLY(SELECT TOP(1)* 
FROM ProductSpecial ps
WHERE p.Id = ps.Id ORDER BY ps.priority )as pss

1
คุณช่วยกรุณาเพิ่มข้อมูลลงในคำตอบของคุณได้
ไหม

รหัสในคำถามใช้LIMITและไม่ได้ติดแท็กด้วย DBMS (ดังนั้นอาจเป็น MySQL หรือ Postgres หรือ SQLite หรืออาจเป็นบาง dbms อื่น ๆ ) รหัสในการใช้คำตอบOUTER APPLYและTOPดังนั้นมันก็จะทำงานใน SQL Server เท่านั้น (และ Sybase) LIMITซึ่งไม่ได้มี
ypercubeᵀᴹ

อันนี้ใช้ได้กับเซิร์ฟเวอร์ sql สำหรับฐานข้อมูลอื่นเท่านั้นที่เราสามารถใช้การสืบค้นภายในภายในคำสั่ง select
Santos APPANA

ใน Postgres ไม่มีOUTER APPLYแต่มีLATERALซึ่งควรจะเทียบเท่า ตัวอย่างการใช้มัน: stackoverflow.com/a/47926042/4850646
Lucas Basquerotto

2

แรงบันดาลใจจากคำตอบของ dezso /dba//a/222471/127433 ฉันแก้ปัญหาใน PostgreSQL โดยใช้อาร์เรย์ดังนี้:

SELECT 
   *, 
   p.name AS name, 
   p.image, 
   p.price,
   ( 
       SELECT ARRAY[ps.price, ps.date]
       FROM product_special ps 
       WHERE p.id = ps.id
         AND ps.date < NOW() 
       ORDER BY ps.priority ASC, LIMIT 1
   ) AS special_price_and_date
FROM product p LEFT JOIN product_special ps ON (p.id = ps.id)

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


1

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

คุณสามารถใช้สิ่งที่ชอบ:

SELECT (col1 || col2) as col3 

(ด้วยตัวคั่นหรือการจัดรูปแบบ col1 และ col2 ตามความยาวที่กำหนด) และหลังจากนั้นดึงข้อมูลของคุณโดยใช้สตริงย่อย

ฉันหวังว่าบางคนจะพบว่ามีประโยชน์


0

ใน DB2 for z / OS ให้ใช้packและunpackฟังก์ชั่นเพื่อส่งกลับหลายคอลัมน์ในการเลือกย่อย

SELECT 
   *, 
   p.name AS name, 
   p.image, 
   p.price,
    unpack((select PACK (CCSID 1028,
               ps.price,
               ps.date)
         FROM product_special ps 
       WHERE p.id = ps.id
         AND ps.date < NOW() 
       ORDER BY ps.priority ASC, LIMIT 1)) .* AS (SPECIAL_PRICE double, DATE date)
FROM product p LEFT JOIN product_special ps ON (p.id = ps.id);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.