поиск уникальной записи с использованием min и max в одном запросе по нескольким критериям - PullRequest
0 голосов
/ 26 августа 2018

У меня есть общее табличное выражение со следующими полями

product.identifier, ingredient.identifier, ingredient.cost,
ingredient.isActive, ingredient.isPrimary

Я пытаюсь найти запись на основе следующих критериев среди нескольких записей

  1. , если isActive = 1 и isPrimary = 1, выберите эту запись
  2. , если запись с isPrimary = 1, но isActive = 0, выберите запись с максимальной / максимальной стоимостью, где isPrimary = 0 и isActive = 1
  3. если все записи из шага 2 имеют одинаковую стоимость, выберите самую старую / мин запись на основе ingredient.Identifier

, логика найти их самостоятельно проста, но объединение логики в одно предложение неработает как положено.вот ожидаемый вывод, который я пытаюсь сопоставить с неверным SQL

product ingredient cost  isActive isPrimary   isChosenRecord

-- isActive and isPrimary example                            
1       10         1.00  1        1           yes
1       11         1.10  1        0           no
2       20         2.00  1        1           yes
2       22         2.15  1        0           no

-- primary record is inactive, choose max cost record
3       30         3.00  0        1           no
3       31         3.10  1        0           no
3       32         3.20  1        0           yes
4       40         4.00  0        1           no
4       41         4.10  1        0           no
4       42         4.20  1        0           yes

-- primary record is inactive, all records have same cost, choose oldest record
5       50         5.00  0        1           no
5       51         5.00  1        0           yes
5       52         5.00  1        0           no
6       60         6.00  0        1           no
6       61         6.00  1        0           yes
6       62         6.00  1        0           no

; with [ActiveRecordsCTE] as
(
    select
        ProductIdentifier = p.Identifier,
        IngredientIdentifier = i.Identifier,
        i.Cost, i.isActive, i.isPrimary
    from Product p
    inner join Ingredient i on i.Identifier = p.Identifier
    where i.isActive = 1

),

[CalculatedPrimaryRecords] AS 
(
    SELECT
        r.ProductIdentifier,
        r.IngredientIdentifier
    FROM ActiveRecordsCTE r
    WHERE r.IsPrimary = 1

    UNION

    -- get the oldest records
    SELECT
        r.ProductIdentifier,
        IngredientIdentifier = min(r.IngredientIdentifier)
    FROM
    (
        -- get most expensive record by cost
        SELECT
            r.ProductIdentifier,
            r.IngredientIdentifier
        FROM ActiveRecordsCTE a
        CROSS APPLY
        (
            -- get most expensive record per product
            SELECT
                r.ProductIdentifier
                ,MaxAssetValue = MAX(r.Cost)
            FROM ActiveRecordsCTE b
            WHERE b.IsPrimary = a.IsPrimary
                AND a.ProductIdentifier = b.ProductIdentifier
                AND a.IngredientIdentifier = b.IngredientIdentifier
            GROUP BY b.ProductIdentifier
        ) ca
        WHERE a.IsPrimary = 0
            -- exclude records that are included in the statement above
            AND a.ProductIdentifier NOT IN
            (
                SELECT ProductIdentifier
                FROM ActiveRecordsCTE
                WHERE IsPrimary = 1
            )
    ) sub
    GROUP BY sub.ProductIdentifier
)

select * from [CalculatedPrimaryRecords]

1 Ответ

0 голосов
/ 26 августа 2018

Используйте row_number() для этого типа приоритизации:

with cte as ( . . . )
select t.*
from (select cte.*,
             row_number() over (partition by product
                                order by (case when isActive = 1 and isPrimary = 1 then 1
                                               when isActive = 0 and isPrimary = 1 then 2
                                               else 3
                                          end),
                                         cost desc, 
                                         identifier asc
                               ) as seqnum
      from cte
     ) t
where seqnum = 1;

Это делает некоторые предположения, которые кажутся соответствующими вопросу:

  • isActive и isPrimary толькопримите значения 0 и 1.
  • Если ни одна запись не имеет isPrimary = 1, то вы все еще хотите запись.(Если нет, их можно легко отфильтровать.)
  • identifier не определено в данных образца.

РЕДАКТИРОВАТЬ:

Если вы хотитеесли хотите, вы можете использовать top (1) with ties:

select top (1) with ties cte.*
from cte
order by row_number() over (partition by product
                            order by (case when isActive = 1 and isPrimary = 1 then 1
                                           when isActive = 0 and isPrimary = 1 then 2
                                           else 3
                                      end),
                                     cost desc, 
                                     identifier asc
                          );

Я на самом деле предпочитаю решение row_number(), потому что я не уверен, что делать в случае, если isPrimary = 0 и проще добавить логикудля этого решения отфильтровать эти записи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...