从选择子查询中获取多个列


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唯一。

Answers:


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

您的查询语言是否具有FIRST()聚合函数?不知道是否可以将product_special的PK设置为id和优先级(均为ASC排序)之间的组合,并将ORDER子句更改为GROUP BY id, psi.priority

您可能可以完全删除ORDER BY子句并使用 HAVING MIN(psi.priority)


2

请注意,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 joinleft join lateral
a_horse_with_no_name 2016年

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
请您在回答中添加更多信息
Ahmad Abuhasna '16

有问题的代码使用LIMITDBMS且未使用DBMS进行标记(因此它可能是MySQL或Postgres或SQLite或其他一些dbms)。答案中的代码使用OUTER APPLYTOP因此它将仅在没有SQL Server(和Sybase)中运行LIMIT
ypercubeᵀᴹ

这仅适用于sql server,仅适用于其他数据库,我们可以在select语句中使用内部查询。
SANTOSH 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 z / OS版中,使用packunpack函数可在子选择中返回多个列。

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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.