使用子查询在varchar列上进行JOIN

时间:2011-12-06 15:19:44

标签: sql sql-server-2005 join

我知道这不是join表的推荐方式。但它只与一个人很少使用的报告相关,我不想改变我的数据模型。

我有两个表ModelSparePart,它们没有通过外键直接相互链接。

Model            SparePart
idModel          idSparePart
ModelName        SparePartDescription
                 Price

在特殊情况下,模型也是备件(交换单元)。然后我需要从SparePart表格通过其SparePartDescription列获得此模型的价格。

例如:

ModelName = C510
SparePartDescription = C510/Exchange Unit/Exch unit/Red

所以我尝试加入两个表来获得跟随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

但我得到一个模型的多个记录:

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

如果有多个备件与SparePartDescription匹配,我只想选择一条记录。

3 个答案:

答案 0 :(得分:2)

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

它将'modModel'表连接到子查询'只有最匹配的tabSparePart'。

您还可以使用OUTER APPLY来模拟子查询上的LEFT JOIN。文档为here

答案 1 :(得分:1)

首先,您的连接条件可以简化一些,然后您可以使用ROW_NUMBER()为结果指定某种顺序,从而允许选择第一个结果(每个模型)。如果没有匹配,我也将其更改为LEFT 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的答案就在我打字的时候出现了。这里的一个区别是选择标准应用于整个集合,而不是在CROSS APPLY中单独应用。 可能具有性能优势,您必须尝试查看。使用内联函数的交叉应用通常与子查询相关的性能更高,因此我无法预测哪个更快。

答案 2 :(得分:1)

尝试使用ROW_NUMBER函数。它保证您只能获得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