Nhận nhiều cột từ một truy vấn con chọn


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)

Như bạn có thể thấy tôi đang lặp lại cùng một truy vấn con chỉ để lấy một cột khác ra. Tôi tự hỏi có cách nào tốt hơn để làm điều này?

id là khóa chính trong cả hai bảng. Tôi không gặp vấn đề gì khi làm cho sản phẩm_special.p Warriority trở nên độc đáo nếu điều đó có thể giúp ích.

Câu trả lời:


11

Giả sử kết hợp product_special.id, product_special.prioritylà duy nhất

 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

trừ khi bạn có ý định trả lại các trường dưới dạng Special_price.price và date.date tại sao không đặt bí danh cho các tên trong truy vấn con? ví dụ

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

Ngôn ngữ truy vấn của bạn có hàm tổng hợp FIRST () không? Không chắc chắn nếu bạn có thể làm cho PK của sản phẩm_đặc biệt là kết hợp giữa id và mức độ ưu tiên (cả hai loại ASC) và thay đổi mệnh đề ORDER thànhGROUP BY id, psi.priority

bạn MIGHT có thể xóa hoàn toàn mệnh đề ORDER BY và sử dụng HAVING MIN(psi.priority)


2

Lưu ý rằng cơ chế "áp dụng chéo" từ SQL Server sẽ giải quyết vấn đề này, nhưng nó không có sẵn trong PostgreQuery. Về cơ bản, đó là giải pháp của họ về cách truyền tham số (có xu hướng tham chiếu đến các cột bên ngoài biểu thức bảng hiện tại) cho các hàm được gọi là biểu thức bảng trong mệnh đề TỪ. Nhưng hóa ra nó hữu ích cho tất cả các loại tình huống mà bạn muốn tránh một cấp độ khác của việc truy vấn con hoặc chuyển mọi thứ từ mệnh đề TỪ sang mệnh đề SELECT. PostgreSQL đã có thể thực hiện điều này bằng cách tạo ra một ngoại lệ - bạn có thể truyền các tham số như thế nếu biểu thức là một lệnh gọi hàm đơn giản nhưng không nói đúng một CHỌN nhúng. Vì thế

left join highestPriorityProductSpecial(p.id) on true

vẫn ổn, nhưng không

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

mặc dù định nghĩa của hàm là chính xác.

Vì vậy, trên thực tế đó là một giải pháp hữu ích (ít nhất là 9.1): tạo một hàm để trích xuất hàng ưu tiên cao nhất của bạn bằng cách thực hiện giới hạn bên trong hàm.

Nhưng các hàm có nhược điểm là kế hoạch truy vấn sẽ không hiển thị những gì đang diễn ra bên trong chúng và tôi tin rằng nó sẽ luôn chọn một phép nối vòng lặp lồng nhau, ngay cả khi điều đó có thể không tốt nhất.


6
cross apply có sẵn trong Postgres bắt đầu với 9,3 (phát hành năm 2013) nhưng họ đã chọn để tuân thủ các tiêu chuẩn SQL và sử dụng các tiêu chuẩn lateralvận hành. Trong truy vấn thứ hai của bạn thay thế left joinbằngleft join lateral
a_horse_with_no_name

2

Hãy thử lệnh SQL sau:

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
bạn có thể vui lòng thêm thông tin vào câu trả lời của bạn không
Ahmad Abuhasna

Mã trong câu hỏi sử dụng LIMITvà không được gắn thẻ với DBMS (vì vậy nó có thể là MySQL hoặc Postgres hoặc SQLite hoặc có thể một số dbms khác). Mã trong câu trả lời sử dụng OUTER APPLYTOPdo đó, nó sẽ chỉ hoạt động trong SQL Server (và Sybase) không có LIMIT.
ypercubeᵀᴹ

Điều này chỉ áp dụng cho máy chủ sql cho các cơ sở dữ liệu khác mà chúng ta có thể sử dụng truy vấn bên trong trong câu lệnh chọn.
SANTOSH APPANA

Trong Postgres thì không OUTER APPLY, nhưng có LATITH , tương đương. Một ví dụ sử dụng nó: stackoverflow.com/a/47926042/4850646
Lucas Basquerotto

2

Lấy cảm hứng từ câu trả lời của dezso /dba//a/222471/127433 Tôi đang giải quyết vấn đề trong PostgreQuery bằng cách sử dụng mảng, như thế này:

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)

Phải thừa nhận rằng nó vẫn chỉ là một cột, nhưng trong mã của tôi, tôi có thể dễ dàng truy cập hai giá trị. Hy vọng nó làm việc cho bạn là tốt.


1

Tôi chỉ muốn đặt cái này ở đây cho lần cuối cùng, cho tất cả những người sử dụng công cụ cơ sở dữ liệu không hỗ trợ một hoặc nhiều câu trả lời khác ...

Bạn có thể sử dụng một cái gì đó như:

SELECT (col1 || col2) as col3 

(Với dấu phân cách hoặc định dạng col1 và col2 theo chiều dài cụ thể.) Và sau đó vẽ dữ liệu của bạn bằng chuỗi phụ.

Tôi hy vọng ai đó thấy nó hữu ích.


0

Trong DB2 cho z / OS, sử dụng packvà các unpackhàm để trả về nhiều cột trong một mục con.

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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.