#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)
.