ПРИСОЕДИНЯЙТЕСЬ к столбцу varchar с подзапросом - PullRequest
2 голосов
/ 06 декабря 2011

я знаю, что это не рекомендуемый способ join таблиц.Но это относится только к одному редко используемому отчету для одного человека, и я не хочу менять для него свою модель данных.

У меня есть две таблицы Model и SparePart, которые не связаны напрямую друг с другомчерез внешние ключи.

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 ]

2 голосов
/ 06 декабря 2011

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, который будет эмулировать левое соединение в подзапросе.Документация здесь .

1 голос
/ 06 декабря 2011

Попробуйте функцию 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
1 голос
/ 06 декабря 2011

Сначала ваше условие соединения может быть несколько упрощено, а затем вы можете использовать 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. Это может иметь преимущество в производительности, вам придется попробовать и посмотреть. CROSS APPLY со встроенными функциями обычно обычно более производительный, чем коррелированные подзапросы, поэтому я не могу предсказать , что быстрее.

...