如何使用条件连接选择重复项的表
Posted
技术标签:
【中文标题】如何使用条件连接选择重复项的表【英文标题】:How to join tables selecting duplicates with condition 【发布时间】:2021-12-28 18:26:31 【问题描述】:我有两张桌子
TicketHeaders TH
ticketID | Amount |
---|---|
1 | 600 |
2 | 900 |
3 | 400 |
TicketBody TB
ticketID | SellerName | SellerType |
---|---|---|
1 | Karen | Manager |
1 | James | Trainee |
2 | John | Manager |
3 | James | Trainee |
我需要的是一张桌子 TicketID - Amount - SellerName,但如果我有一张有 2 个卖家的票,我只需要为该票选择经理。
输出表应该是:
ticketID | Amount | SellerName |
---|---|---|
1 | 600 | Karen |
2 | 900 | John |
3 | 400 | James |
如果我使用左连接,我会得到票 1 的重复金额
SELECT TH.ticketID, TH.Amount, TB.SellerName
FROM TH
LEFT JOIN TB ON TH.ticketID = TB.ticketID
【问题讨论】:
【参考方案1】:SELECT TH.ticketID, TH.Amount, COALESCE(TB_M.SellerName, TB_T.SellerName)
FROM TH
LEFT JOIN TB TB_M ON TH.ticketID = TB_M.ticketID AND TB_M.SellerType = 'Manager'
LEFT JOIN TB TB_T ON TH.ticketID = TB_T.ticketID AND TB_T.SellerType <> 'Manager'
【讨论】:
【参考方案2】:根据声明的 2.5 版,您似乎无法使用 row_number()
解决方案。您可以使用单个内部连接来解决此问题。我不知道避免额外加入 Firebird 是否有任何好处。
select ticketID, min(Amount) as Amount,
case min(case SellerType when 'Manager' then 1 else 2 end) when 1
then min(case SellerType = 'Manager' then SellerName end)
else min(case SellerType <> 'Manager' then SellerName end)
end SellerName
from TH th inner join TB tb on tb.ticketID = th.ticketID
group by ticketID
另一个好处是,这将适用于不同卖家的更大层次结构(通过添加新案例)。但如果在一个级别有多个卖家,它就行不通了。
【讨论】:
【参考方案3】:在这种情况下我会使用 row_number():
with _cte as (
SELECT TH.ticketID,
TH.Amount,
TB.SellerName,
row_number() over (partition by TH.ticketID order by case when SellerType = 'Manager' then 0 else 1 end) as rn
FROM TH
LEFT JOIN TB ON TH.ticketID = TB.ticketID
)
select ticketID, Amount, SellerName
from _cte
where rn = 1
【讨论】:
row_number()
在 Firebird 3.0 中引入,OP 使用的是 Firebird 2.5。【参考方案4】:
A.S.我认为您的问题尚未明确定义:
我只需要为该特定工单选择经理
如果有很多行,但有两个或更多经理怎么办?零经理?如果没有考虑清楚,就像保证 SQL Server 永远不会允许插入此类数据一样,这是一个等待发生的错误。
我还认为 SellerType
最好是一个整数字段,是一些 Seller_types
字典表的外键 - 既因为整数字段更容易 index
ed 和比较 join
ing 表,因为那会允许您以后随心所欲地重命名“功能角色”(或按照您的职责),而无需更改任何内容:您的 Seller_types
可以有额外的列,如整数 role_priority
甚至像 max_persons_of_type_in_one_ticket
之类的东西(你老板例如,可能决定一张票可以有两个经理,或者一个经理和一个副经理,然后不超过四名学员)。
但回到这个问题,还有另一种方法可以做到这一点。事实上,它会为TicketHeaders
表中的每一行运行一个correlated sub-query
,因此如果您对数千行进行“长”选择,它可能会更慢。特别是如果您保留 Firebird 的默认小内存缓存(请参阅 ib-aid.com 上有关配置 Firebird 和宽松配置的文章)。
另一方面,它需要“做一次就忘记”,使您的查询更简单,从而减少您将来出错的机会。对于短(100 行或更少的行)查询,速度损失可能并不明显。而且,如果您根本不查询该新列(您不会在生产中使用 select *
查询,对吗?),也不会受到任何惩罚。
所以,代码,最后:dbfiddle here
create table Ticket_Headers ( ticket_id integer primary key, amount integer not null )
create table Ticket_Body ( ticket_id integer REFERENCES Ticket_Headers(ticket_id) ON DELETE CASCADE ON UPDATE CASCADE, Seller_Name varchar(20) not null, Seller_Type varchar(20) not null, CONSTRAINT TicketBody_PK PRIMARY KEY (ticket_id, Seller_Name) )
create index idx_TicketBody_Type on Ticket_Body(Seller_Type)
3 行受影响insert into Ticket_Headers select 1, 600 from rdb$database union all select 2, 900 from rdb$database union all select 3, 400 from rdb$database
4 行受影响insert into Ticket_body select 1, 'Karen', 'Manager' from rdb$database union all select 1, 'James', 'Trainee' from rdb$database union all select 2, 'John', 'Manager' from rdb$database union all select 3, 'James', 'Trainee' from rdb$database
TICKET_ID |数量 --------: | -----: 1 | 600 2 | 900 3 | 400select * from Ticket_Headers
TICKET_ID | SELLER_NAME | SELLER_TYPE --------: | :------------ | :---------- 1 |凯伦 |经理 1 |詹姆斯 |实习生 2 |约翰 |经理 3 |詹姆斯 |实习生select * from Ticket_Body
TICKET_ID |数量 | TICKET_ID | SELLER_NAME | SELLER_TYPE --------: | -----: | --------: | :------------ | :---------- 1 | 600 | 1 |凯伦 |经理 1 | 600 | 1 |詹姆斯 |实习生 2 | 900 | 2 |约翰 |经理 3 | 400 | 3 |詹姆斯 |实习生select * from Ticket_Headers TH, Ticket_Body TB where TH.ticket_id = TB.ticket_ID
alter table Ticket_Headers add Seller_Top computed by ( -- this parenthesis is required by COMPUTED BY SQL syntax ( -- this parenthesis is required to coerce SELECT from query to expression select First(1) TB.Seller_Name from Ticket_Body TB where TB.ticket_id = Ticket_Headers.ticket_id order by TB.Seller_type /* Descending - if other order to be needed */ ) )
TICKET_ID |数量 | SELLER_TOP --------: | -----: | :--------- 1 | 600 |凯伦 2 | 900 |约翰 3 | 400 |詹姆士select * from Ticket_Headers
前面提到的Seller_types.role_priority
会更加灵活,因此对于这样的order-by
来说是面向未来的方法。
【讨论】:
以上是关于如何使用条件连接选择重复项的表的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 SQL 和 Python 连接两个具有日期条件的表?