有条件时添加列以选择语句

时间:2019-04-24 17:08:52

标签: sql firebird

我有一个来自order_details的数据的SQL语句,该表包含许多列,其中包括产品名称,代码等。我如何在select语句中添加一列,以便每当订单具有特定产品时(我需要的product_code称为“ Pap”),它会写一个标记“ pap”,这样我就可以直观地知道该产品有哪些订单?

我尝试了以下代码:

select distinct order_id, customer_id,
  (select distinct order_id from order_details 
   group by 1 having sum (case when product_code='pap' 
   then 1 else 0 end)=1
  ) as pap from orders 
left join order_details 
  on order_details.order_id=orders.order_id
group by 1,2,3

我正在尝试的代码给我一个错误“ [Firebird]单例中有多行; HY000”。

4 个答案:

答案 0 :(得分:1)

您猜测要显示具有一个或多个product_code为“ pap”的order_details的订单的“ pap”,在这种情况下,您可以使用:

select order_id, customer_id,
  (select max(order_details.product_code)
      from order_details 
      where order_details.order_id = orders.order_id 
      and order_details.product_code = 'pap') as pap
from orders 

或更通用的解决方案(不依赖product_code来显示值):

select order_id, customer_id,
  case 
    when exists(
      select 1 
      from order_details 
      where order_details.order_id = orders.order_id 
      and order_details.product_code = 'pap') 
      then 'pap' 
  end as pap
from orders 

答案 1 :(得分:1)

让我们尝试逐步构建您的查询。从简单到复杂,以过时的从上到下的方式:-)

我建议您运行每个查询以查看结果,并逐步了解数据的改进方式,并尽早检查您的假设是否成立。

第一个未知数是order_details-一个订单可以使用同一商品有多行吗?是否可以订购2 + 3 Paps或仅一个摘要5 Paps的订单? (order_id,product_code是该表的unique constraint还是primary key

Select Count(1), order_id, product_code
From order_details
Group by 2,3
Order by 1 DESC

这可以显示是否存在这样的重复,但是即使不存在-您也必须检查元数据(方案)以查看表constraintsindices是否允许这样做。

问题是,当您使用JOIN表时,它们的匹配行将成倍增加(以设定的理论术语而言)。因此,如果您可以以一个顺序包含几行有关Paps的内容-那么我们必须对此进行特殊处理。除非我们找到免费的方式,否则这会增加服务器的额外负担。

我们可以轻松地检查一个特定的订单以购买该产品。

select 'pap' from order_details where order_id = :parameter_id and product_code='pap' 

然后,如果没有受到约束的限制,我们可以以标准方式(但需要额外的排序)或特定于Firebird(但免费)的方式取消重复。

select DISTINCT 'pap' from order_details where order_id = :parameter_id and product_code='pap' 

select FIRST(1) 'pap' from order_details where order_id = :parameter_id and product_code='pap' 

但是,这些可以通过相关子查询满足Mark的答案:

select o.order_id, o.customer_id, 
  coalesce(
    ( select first(1) 'pap' /* the flag */ from order_details d
        where o.order_id = d.order_id and d.product_code = 'pap' )
    , '' /* just avoiding NULL */ 
    ) as pap
from orders o

Lifehack:请注意,此处的coalescefirst(1)的使用如何代替了Mark原始答案中的caseexists的使用。无论您使用单数(并且可能为空)1列查询作为表达式的地方,都可以在Firebird中使用此技巧。


为避免多个子查询并切换到外部联接,我们需要进行一次查询以使所有订单ID都带有Paps,但只有一次。

select distinct order_id from order_details where product_code='pap' 

应该做到这一点。但是可能是以额外的排序为代价来抑制可能的重复(再次,但是有可能吗?)

select order_id, count(order_id) 
from order_details 
where product_code='pap' 
group by 1 order by 2 desc

如果重复已经存在,将显示为重复。只是为了解释我的意思。并查看是否可以对已经存在的数据执行SQL constraints,如果您没有这些数据,可以选择加强数据库结构。

通过这种方式,我们只需要与之进行外部联接,并使用CASE(或其一些简写形式)就可以完成过滤外部联接的NULL行的典型技巧。

select o.order_id, o.customer_id,
   iif( d.order_id is null, '', 'pap') as pap 
from orders o
left join (
    select distinct order_id
    from order_details
    where product_code = 'pap'
      and product_quantity > 0 ) d
  on o.order_id=d.order_id

正如某人所说的那样,这看起来很丑陋,还有另一种“现代”方式可以精确地编写该查询,也许看起来会更好:-D

with d as (
  select distinct order_id
    from order_details
    where product_code = 'pap'
      and product_quantity > 0 )

select o.order_id, o.customer_id,
   iif( d.order_id is null, '', 'pap') as pap 
from orders o left join d on o.order_id=d.order_id

在单个order_id内不能(注意,不能,但不能)重复'pap'的情况下,查询将变得更加简单和快捷:

select o.order_id, o.customer_id,
   iif( d.order+id is null, '', 'pap') as pap 
from orders o
left join order_details d
  on o.order_id=d.order_id
 and d.product_code='pap' 
 and d.product_quantity>0

请注意以下关键细节:d.product_code='pap'被设置为联接(之前)的内部条件。您可以在连接后将其放入外部WHERE子句中吗-它不起作用!


现在,要比较JOIN和相关子查询这两种方法,您必须查看查询统计信息,两次将生成多少次提取和缓存的提取。在中型表上以及OS磁盘缓存和Firebird缓存预热的情况下,您可能看不到时间上的差异。但是您至少要关闭并重新启动Firebird服务并改善整个计算机的状况吗-清理所说的缓存-然后将这些查询返回到最后一行(通过在数据库IDE中发出“全部提取”或“滚动到最后一行”) ,或将我和Mark的查询包装到 select count(1) from ( /* measured query here */)您可能也会开始看到时间变化。

答案 2 :(得分:0)

SELECT
    ...
    <foreign_table>.<your_desired_extra_column>
FROM
    <current_table>
LEFT JOIN
    <foreign_table> ON <foreign_table>.id = <current_table>.id
    AND 
    <current_table>.<condition_field> = <condition_value>

如果不满足条件,则多余的列将为NULL。

答案 3 :(得分:0)

选择order_id,customer_id,   (选择max(order_details.product_code)       来自order_details       其中order_details.order_id = orders.order_id       和order_details.product_code ='pap')如pap 来自订单