Простой SQL: как вычислить уникальные, непрерывные числа для дубликатов в наборе?

#sql #tsql #sql-server-2000

#sql #tsql #sql-server-2000

Вопрос:

Допустим, я создаю таблицу с int Page, int Section и полем идентификатора int ID, где поле страницы варьируется от 1 до 8, а поле раздела — от 1 до 30 для каждой страницы. Теперь предположим, что две записи имеют дублирующиеся страницу и раздел. Как я мог бы перенумеровать эти две записи, чтобы последовательность нумерации страниц и разделов была непрерывной?

 select page, section
from #fun
group by page, section having count(*) > 1
  

показывает дубликаты:

 page 1 section 3
page 2 section 3
  

страница 1, раздел 4 и страница 2, раздел 4, отсутствуют. Есть ли способ без использования курсора найти и перенумеровать позиции в SQL 2000, который не поддерживает Row_Number()?

Приведенное ниже число строк, конечно, выдает точно такое же число, как в разделе:

 select page, section,
    (select count(*)   1 
     from #fun b 
     where b.page = a.page and b.section < a.section) as rownum
from #fun a
  

Я мог бы создать сводную таблицу со значениями от 1 до 100, но к чему бы я присоединился?

То, что я хочу сделать, это что-то вроде этого:

 update p set section = (expression that gets 4)
from #fun p
where (expression that identifies duplicate sections by page)
  

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

1. Всегда ли дубликаты имеют самый высокий номер раздела на странице? Можем ли мы быть уверены, например, что в вашем наборе данных уже нет (страница = 1, раздел = 4)? Нужно ли увеличивать «страницу», если повторяющийся «раздел» равен 30?

2. 1, заинтригован узнать, есть ли способ сделать это без курсора и без ROW_NUMBER

Ответ №1:

У меня нет сервера 2000, на котором можно это протестировать, но я думаю, что это должно сработать.

Создание тестовых таблиц / данных:

 CREATE TABLE #fun
(Id INT IDENTITY(100,1)
,page INT NOT NULL
,section INT NOT NULL
)


INSERT #fun (page, section)
SELECT 1,1
UNION ALL SELECT 1,3    UNION ALL SELECT 1,2
UNION ALL SELECT 1,3    UNION ALL SELECT 1,5
UNION ALL SELECT 2,1    UNION ALL SELECT 2,2
UNION ALL SELECT 2,3    UNION ALL SELECT 2,5
UNION ALL SELECT 2,3
  

Теперь обработка:

 -- create a worktable
CREATE TABLE #fun2
(Id INT IDENTITY(1,1)
,funId INT
,page INT NOT NULL
,section INT NOT NULL
)

-- insert data into the second temp table ordered by the relevant columns
-- the identity column will form the basis of the revised section number
INSERT  #fun2 (funId, page, section)
SELECT  Id,page,section
FROM    #fun
ORDER BY page,section,Id

-- write the calculated section value back where it is different
UPDATE  p
SET     section = y.calc_section
FROM    #fun AS p 
JOIN
        (
            SELECT  f2.funId, f2.id - x.adjust calc_section
            FROM    #fun2 AS f2
            JOIN    (
                        -- this subquery is used to calculate an offset like
                        -- PARTITION BY in a 2005  ROWNUMBER function
                        SELECT MIN(Id) - 1 adjust, page
                        FROM #fun2
                        GROUP BY page
                    ) AS x
            ON      f2.page = x.page
        ) AS y
ON      p.Id = y.funId
WHERE   p.section <> y.calc_section


SELECT * FROM #fun order by page, section
  

Ответ №2:

Отказ от ответственности: у меня нет SQL Server для тестирования.

Если я вас правильно понял, если бы вы знали ROW_NUMBER ваших #fun записей, разделенных на (page, section) дубликаты, вы могли бы использовать это относительное ранжирование для увеличения «раздела»:

     UPDATE p
       SET section = section   (rownumber - 1)
      FROM #fun AS p
INNER JOIN ( -- SELECT id, ROW_NUMBER() OVER (PARTITION BY page, section) ...
            SELECT id, COUNT(1) AS rownumber
              FROM #fun a
         LEFT JOIN #fun b
                   ON a.page = b.page AND a.section = b.section AND a.id <= b.id
          GROUP BY a.id, a.page, a.section) d
            ON p.id = d.id
      WHERE rownumber > 1
  

Это не будет обрабатывать случай, когда количество дубликатов превышает верхний предел в 30. Это также может создать новые дубликаты, где, если разделы с более высоким номером на странице уже существуют — то есть один экземпляр (pg 1, sec 3) become (pg 1, sec 4) , который уже существовал, — но вы можете запускать ОБНОВЛЕНИЕ повторно, пока дубликатов не останется.

А затем добавьте уникальный индекс на (page, section) .