[英]JOIN on varchar column with subquery
i know that this is not the recommended way to join
tables. 我知道这不是推荐的join
表的方式。 But it's only relevant for one rarely used report for one person and i don't want to change my datamodel for it. 但这仅与一个人的一份很少使用的报告有关,我不想为此更改数据模型。
I have two tables Model
and SparePart
that are not directly linked with each other via foreign keys. 我有两个表Model
和SparePart
,它们不是通过外键直接链接的。
Model SparePart
idModel idSparePart
ModelName SparePartDescription
Price
In special cases a model is also a sparepart(exchange unit). 在特殊情况下,模型也是备件(交换单元)。 Then i need the price for this model from the SparePart
table via its SparePartDescription
column. 然后,我需要通过其SparePartDescription
列从SparePart
表中获得该模型的价格。
For example: 例如:
ModelName = C510
SparePartDescription = C510/Exchange Unit/Exch unit/Red
So i try to join both tables to get the price with following SQL: 因此,我尝试通过下面的SQL加入两个表以获取价格:
SELECT m.idModel, m.ModelName, sp.Price, sp.SparePartDescription
FROM modModel AS m INNER JOIN
tabSparePart AS sp ON m.ModelName =
(SELECT TOP 1 LEFT(sp.SparePartDescription, CHARINDEX('/', sp.SparePartDescription) - 1)order by price desc)
WHERE (CHARINDEX('/', sp.SparePartDescription) > 0)
AND (sp.fiSparePartCategory = 6)
ORDER BY m.ModelName, sp.SparePartDescription
But i get multiple records for one model: 但是我得到一个模型的多个记录:
idModel ModelName Price SparePartDescription
569 C510 70,75 C510/Exchange Unit/Exch unit/Red
569 C510 70,75 C510/Exchange Unit/Latin/Generic/Black
569 C510 70,75 C510/Exchange Unit/Latin/Generic/Silver
433 C702 80,72 C702/Exchange Unit/Latin/Generic/Black
433 C702 NULL C702/Exchange Unit/Latin/Generic/Cyan
433 C702 80,72 C702/Exchange Unit/Orange Global/Black
I only want to select one record if there are multiple spareparts with matching SparePartDescription. 如果有多个备件与SparePartDescription匹配,我只想选择一个记录。
Sql Server 2005 and better introduced the 'APPLY' operator which allows you to join against a subquery... Try this. Sql Server 2005及更高版本引入了“ APPLY”运算符,该运算符使您可以对子查询进行联接...尝试此操作。
SELECT m.idModel, m.ModelName, sp.Price, sp.SparePartDescription
FROM modModel AS m
CROSS APPLY
(
SELECT TOP 1 * FROM tabSparePart
WHERE m.ModelName =
LEFT(SparePartDescription, LEN(ModelName))
ORDER BY Price DESC
) sp
WHERE (sp.fiSparePartCategory = 6)
ORDER BY m.ModelName, sp.SparePartDescription
It inner joins the 'modModel' table with the subquery 'only the top one matching tabSparePart'. 它内部将'modModel'表与子查询'仅匹配最上面的一个匹配的tabSparePart'连接在一起。
You can also use OUTER APPLY which will emulate a LEFT JOIN on the subquery. 您还可以使用OUTER APPLY来模拟子查询上的LEFT JOIN。 Documentation is here . 文档在这里 。
First, your join condition can be simplified some, and then you can use ROW_NUMBER() to specify some kind of order to your results, allowing the first result (per model) to be selected. 首先,可以将连接条件简化一些,然后可以使用ROW_NUMBER()为结果指定某种顺序,从而允许选择第一个结果(每个模型)。 I also changed it to a LEFT JOIN in case there was no match. 如果没有匹配项,我也将其更改为LEFT JOIN。 If that is not required, it's simple to change back to an INNER JOIN :) 如果不需要,可以很容易地改回INNER JOIN :)
WITH
ranked_results AS
(
SELECT
m.idModel, m.ModelName, sp.Price, sp.SparePartDescription,
ROW_NUMBER() OVER (PARTITION BY m.idModel ORDER BY sp.Price DESC) AS rank
FROM
modModel AS m
LEFT JOIN
tabSparePart AS sp
ON LEFT(sp.SparePartDescription, LEN(m.ModelName)) = m.ModelName
AND (CHARINDEX('/', sp.SparePartDescription) > 0)
AND (sp.fiSparePartCategory = 6)
)
SELECT
*
FROM
ranked_results
WHERE
rank = 1
ORDER BY
ModelName,
SparePartDescription
@MattMurrell's answer just appeared while I was typing this. 我输入此内容时,@ MattMurrell的答案才出现。 One difference here is that the selection criteria is being applied to the whole set, rather than separately in the CROSS APPLY. 此处的一个区别是选择标准将应用于整个集合,而不是在“交叉应用”中单独应用。 This may have a performance benefit, you'd have to try and see. 这可能会带来性能上的好处,您必须尝试看看。 CROSS APPLY with inline functions is normally more performant that correlated sub-queries, so I can't predict which is faster. CROSS内联函数适用通常更高性能的是相关子查询,所以我无法预测哪个更快。
Try the ROW_NUMBER function. 尝试ROW_NUMBER函数。 It guarantees that you'll only get one of each item as defined in the PARTITION BY clause. 它保证您只会得到PARTITION BY子句中定义的每一项。
SELECT a.idModel, a.ModelName, Price, SparePartDescription
FROM modModel a
LEFT JOIN
(
SELECT m.idModel, m.ModelName, sp.Price, sp.SparePartDescription
, ROW_NUMBER() OVER (PARTITION BY m.idModel, m.ModelName ORDER BY sp.price DESC) AS r
FROM modModel AS m
INNER JOIN tabSparePart AS sp
ON m.ModelName = LEFT(sp.SparePartDescription, CHARINDEX('/', sp.SparePartDescription) - 1)
WHERE (CHARINDEX('/', sp.SparePartDescription) > 0)
AND (sp.fiSparePartCategory = 6)
) b
ON a.idModel = b.idModel
AND b.r = 1
ORDER BY ModelName, SparePartDescription
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.