我有一个来自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”。
答案 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
这可以显示是否存在这样的重复,但是即使不存在-您也必须检查元数据(方案)以查看表constraints
或indices
是否允许这样做。
问题是,当您使用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:请注意,此处的coalesce
和first(1)
的使用如何代替了Mark原始答案中的case
和exists
的使用。无论您使用单数(并且可能为空)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 来自订单