Создание «наборов» в одной таблице на основе многоколоночных критериев

#sql-server

#sql-server

Вопрос:

Для каждой уникальной комбинации BoxId и ревизии с одним идентификатором UnitTypeId равным 1 и одним идентификатором UnitTypeId равным 2, оба из которых имеют нулевой setNumber, назначьте setNumber равным 1.

Настройка таблицы и данных:

 IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[UnitTypes]') AND type in (N'U'))
Drop Table dbo.UnitTypes
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Tracking]') AND type in (N'U'))
DROP TABLE [dbo].[Tracking]
GO
CREATE TABLE dbo.UnitTypes
    (
    Id int NOT NULL,
    Notes varchar(80)
    )
GO
CREATE TABLE dbo.Tracking
    (
    Id int NOT NULL IDENTITY (1, 1),
    BoxId int NOT NULL,
    Revision int NOT NULL,
    UnitValue int NULL,
    UnitTypeId int NULL,
    SetNumber int NULL
    )
GO
ALTER TABLE dbo.Tracking ADD CONSTRAINT
    PK_Tracking PRIMARY KEY CLUSTERED 
    (
    Id
    )
GO  
Insert Into dbo.UnitTypes (Id, Notes) Values (1, 'X Coord'),
                                             (2, 'Y Coord'),
                                             (3, 'Weight'),
                                             (4, 'Length')
Go
Insert Into dbo.Tracking (BoxId, Revision, UnitValue, UnitTypeId, SetNumber)
              Values (1165, 1, 150, 1, NULL),
                     (1165, 1, 1477, 2, NULL),
                     (1165, 1, 31, 4, NULL),
                     (1166, 1, 425, 1, 1),
                     (1166, 1, 1146, 2, 1),
                     (1166, 1, 438, 1, NULL),
                     (1166, 1, 1163, 2, NULL),
                     (1167, 1, 560, 1, NULL),
                     (1167, 1, 909, 2, NULL),
                     (1167, 1, 12763, 3, NULL),
                     (1168, 1, 21, 1, NULL),
                     (1168, 1, 13109, 3, NULL)
  

Идеальными результатами были бы:

 Id  BoxId   Revision    UnitValue   UnitTypeId  SetNumber
 1   1165          1          150            1          1
 2   1165          1         1477            2          1
 3   1165          1           31            4          1
 4   1166          1          425            1          1
 5   1166          1         1146            2          1
 6   1166          1          438            1       NULL <--NULL Because there is already an existing Set
 7   1166          1         1163            2       NULL <--NULL Because there is already an existing Set
 8   1167          1          560            1          1
 9   1167          1          909            2          1
 10  1167          1        12763            3          1
 11  1168          1           21            1       NULL <--NULL Because there is not exactly one UnitTypeId of 1 and exactly one UnitTypeId of 2 for this BoxIdRevision combination.
 12  1168          1        13109            3       NULL <--NULL Because there is not exactly one UnitTypeId of 1 and exactly one UnitTypeId of 2 for this BoxIdRevision combination.
  

Редактировать:
Вопрос в том, как я могу обновить setNumber, учитывая приведенные выше ограничения, используя чистый TSQL?

Комментарии:

1. Вопрос в том, как это сделать с помощью sql. Я пробовал много вещей, но всегда получал временные таблицы. Я ищу наиболее эффективный способ выполнить это обновление.

2. Как и на уроке математики в школе, лучше всего показывать свою работу . Если кажется, что вы ничего не сделали и просто пришли в SO, чтобы опубликовать список требований и попросить кого-нибудь сделать все за вас, у вас меньше шансов получить помощь от более опытных людей.

3. Отличная точка зрения. Хотя я весь день бил себя по этой проблеме и от отчаяния обратился к SO, надеясь, что у кого-то есть умное решение.

Ответ №1:

Если я правильно понимаю ваш вопрос, вы могли бы сделать это с помощью подзапроса, который требует выполнения всех условий:

 update  t1
set     SetNumber = 1
from    dbo.Tracking t1
where   SetNumber is null
        and 1 = 
        (
        select  case 
                when count(case when t2.UnitTypeId = 1 then 1 end) <> 1 then 0
                when count(case when t2.UnitTypeId = 2 then 1 end) <> 1 then 0
                when count(t2.SetNumber) <> 0 then 0
                else 1 
                end
        from    dbo.Tracking t2
        where   t1.BoxId = t2.BoxId
                and t1.Revision = t2.Revision
        )
  

Это count(t2.SetNumber) немного сложно: это будет учитывать только строки, которых SetNumber нет null . Таким образом, это соответствует критерию, согласно которому не существует другого набора с таким же (BoxId, Revision) .

Комментарии:

1. Мне нравится простота этого. Я настолько запрограммирован против подзапросов, что мой мозг отфильтровывает их как возможное решение. Я имею дело с небольшими наборами данных, поэтому производительность не является проблемой. Я ценю вашу помощь.

Ответ №2:

Попробуйте это, он возвращает те же результаты, которые вы дали. WITH Оператор устанавливает CTE для запроса. Функция ROW_NUMBER() — это функция разделения, которая делает то, что вы хотите:

 ;WITH BoxSets AS (
SELECT
    ID
    ,BoxId
    ,Revision
    ,UnitValue
    ,UnitTypeId
    ,CASE WHEN UnitTypeId IN (1,2) THEN 1 ELSE 0 END ValidUnit
    ,ROW_NUMBER() OVER (PARTITION BY BoxID,UnitTypeID ORDER BY BoxID,UnitTypeID,UnitValue ) SetNumber
FROM Tracking
)

SELECT 
    b.ID
    ,b.BoxId
    ,b.Revision
    ,b.UnitValue
    ,b.UnitTypeId
    ,CASE ISNULL(b1.ValidUnits,0) WHEN 0 THEN NULL ELSE CASE b.SetNumber WHEN 1 THEN b.SetNumber ELSE NULL END END
FROM BoxSets AS b
LEFT JOIN (SELECT
             BoxID
             ,SUM(ValidUnit) AS ValidUnits
             FROM BoxSets
             GROUP BY BoxId
             HAVING SUM(ValidUnit) > 1) AS b1 ON b.BoxId = b1.BoxId
  

Комментарии:

1. OP просит отметить, когда есть ровно один 1 и один 2 . Этот ответ будет принимать группы с двумя или даже тремя UnitTypeId = 1 ?

2. Простое исправление, просто изменило условие в CASE инструкции.

3. Это очень близко, но не исключает серию BoxId 1166.

4. @MichaelMinton, на самом деле на основе выборочных данных (и данных, которые я вставил) это так. Здесь не удается правильно отформатировать результаты, но я вижу именно то, что вы опубликовали как идеальное.