Как удалить дубликаты из хранимой процедуры без использования DISTINCT - PullRequest
0 голосов
/ 09 января 2019

Была написана хранимая процедура, которая включает дубликаты. ROW_NUMBER был опробован, но не работал. DISTINCT сработало, но не смог получить большое количество требуемых записей (около 700 000). Есть ли другой способ использования RANK или GROUP BY для удаления дубликатов?

Я использовал DISTINCT, и это не позволяет получить достаточно записей. Я не успешно использовал GROUP BY.

Я пытался использовать ROW NUMBER, но это тоже не сработало (вы можете увидеть, где это закомментировано).

CREATE PROCEDURE [report].[get_foodDetails] 
    @foodgroup_id INT, 
    @shop_id INT = 0, 
    @product_id INT = 0, 
    @maxrows INT = 600, 
    @expiry INT = 1, 
    @productactive INT = 1, 
    @expiryPeriod DATETIME = '9999-12-31 23:59:59' 
AS 
    IF (@expiryPeriod >= '9999-12-31') 
    BEGIN 
        SET @expiryPeriod = GETDATE() 
    END 

    SELECT  
        -- dp.RowNumber 
        ISNULL([FoodType], '') AS [Foodtype],
        ISNULL([FoodColour], '') AS [FoodColour],
        ISNULL([FoodBarcode], '') AS [FoodBarcode],
        ISNULL([FoodArticleNum], 0) AS [FoodArticleNum],
        ISNULL([FoodShelfLife, '9999-21-31') AS [FoodShelfLIFe]
    INTO 
        #devfood 
    FROM 
        report.[GetOrderList] (@foodgroup_id, @product_id, @productactive, @expiry, @expiryPeriod, @shop_id, @maxrows ) dp 
    INNER JOIN 
        food_group fg ON fg.food_group_id = it.item_FK_item_group_id 

    SELECT TOP(@maxrows) * 
    FROM #devfood 
    ORDER BY [device_packet_created_date]  
 END 

Получено около 700 000 записей. В настоящее время это достигается, хотя есть дубликаты. При использовании DISTINCT извлекается только 20 000 (без дубликатов).

Ответы [ 2 ]

0 голосов
/ 10 января 2019

Я знаю, что вы сказали, что пытались ROW_NUMBER, но пробовали ли вы один из этих способов?

Сначала CTE. CTE - это только ваш существующий запрос, но с прикрепленной оконной функцией ROW_NUMBER. Для каждой повторяющейся итерации записи она добавляет одну к RowNumber. Со следующей уникальной группой записей RowNumber сбрасывается до 1.

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

WITH NoDupes AS
  (
    SELECT
      ROW_NUMBER() OVER (PARTITION BY
                           ISNULL(FoodType, '')
                          ,ISNULL(FoodColour, '')
                          ,ISNULL(FoodBarcode, '')
                          ,ISNULL(FoodArticleNum, '')
                          ,ISNULL(FoodShelfLife, '9999-21-31')
                         ORDER BY
                           (
                             SELECT
                               0
                           )
                        ) AS RowNumber
     ,ISNULL(FoodType, '') AS Foodtype
     ,ISNULL(FoodColour, '') AS FoodColour
     ,ISNULL(FoodBarcode, '') AS FoodBarcode
     ,ISNULL(FoodArticleNum, 0) AS FoodArticleNum
     ,ISNULL(FoodShelfLife, '9999-21-31') AS FoodShelfLIFe
    FROM
      report.GetOrderList(@foodgroup_id, @product_id, @productactive, @expiry, @expiryPeriod, @shop_id, @maxrows) AS dp
    INNER JOIN
      food_group AS fg
        ON
        fg.food_group_id = it.item_FK_item_group_id
  )
SELECT
  nd.Foodtype
 ,nd.FoodColour
 ,nd.FoodBarcode
 ,nd.FoodArticleNum
 ,nd.FoodShelfLIFe
INTO
  #devfood
FROM
  NoDupes AS nd
WHERE
  NoDupes.RowNumber = 1;

В качестве альтернативы (и короче) вы можете попробовать SELECT TOP (1) WITH TIES, используя ту же самую функцию ROW_NUMBER для упорядочения набора записей. Часть TOP (1) WITH TIES функционально делает то же самое, что и CTE, возвращая только первую запись каждого набора дубликатов.

SELECT
  TOP (1) WITH TIES
  ISNULL(FoodType, '') AS Foodtype
 ,ISNULL(FoodColour, '') AS FoodColour
 ,ISNULL(FoodBarcode, '') AS FoodBarcode
 ,ISNULL(FoodArticleNum, 0) AS FoodArticleNum
 ,ISNULL(FoodShelfLife, '9999-21-31') AS FoodShelfLIFe
INTO
  #devfood
FROM
  report.GetOrderList(@foodgroup_id, @product_id, @productactive, @expiry, @expiryPeriod, @shop_id, @maxrows) AS dp
INNER JOIN
  food_group AS fg
    ON
    fg.food_group_id = it.item_FK_item_group_id
ORDER BY
  ROW_NUMBER() OVER (PARTITION BY
                       ISNULL(FoodType, '')
                      ,ISNULL(FoodColour, '')
                      ,ISNULL(FoodBarcode, '')
                      ,ISNULL(FoodArticleNum, '')
                      ,ISNULL(FoodShelfLife, '9999-21-31')
                     ORDER BY
                       (
                         SELECT
                           0
                       )
                    );

Возможно, CTE немного яснее для следующего человека, который смотрит на код, но TOP может работать немного лучше.

0 голосов
/ 09 января 2019

Пример кода ниже взят из презентации, которую я использовал для демонстрации CTE. Это общий механизм удаления дубликатов, и он очень быстрый. В этом случае дубликаты удаляются прямо из таблицы. Если это не ваша цель, вы можете использовать временную таблицу или ранее прикованный CTE. Обратите внимание, что важно то, по каким столбцам вы делите. Если в этом примере вы разделены только [name], вы не увидите ни красной розы, ни белой розы.

-------------------------------------------------
if object_id(N'[flower].[order]', N'U') is not null
  drop table [flower].[order];

go

create table [flower].[order]
  (
     [id]       int identity(1, 1) not null constraint [flower.order.id.clustered_primary_key] primary key clustered
     , [flower] nvarchar(128)
     , [color]  nvarchar(128)
     , [count]  int
  );

go

insert into [flower].[order]
            ([flower]
             , [color]
             , [count])
values      (N'rose',N'red',5),
            (N'rose',N'red',3),
            (N'rose',N'white',2),
            (N'rose',N'red',1),
            (N'rose',N'red',9),
            (N'marigold',N'yellow',2),
            (N'marigold',N'yellow',9),
            (N'marigold',N'yellow',4),
            (N'chamomile',N'amber',9),
            (N'chamomile',N'amber',4),
            (N'lily',N'white',12);

go

select [flower]
       , [color]
from   [flower].[order];

go

--
-------------------------------------------------
with [duplicate_finder]([name], [color], [sequence])
     as (select [flower]
                , [color]
                , row_number()
                    over (
                      partition by [flower], [color]
                      order by [flower] desc) as [sequence]
         from   [flower].[order])
delete from [duplicate_finder]
where  [sequence] > 1;

--
-- no duplicates
-------------------------------------------------
select [flower]
       , [color]
from   [flower].[order]; 
...