Нужно немного tSQL Wizardry: обновление SQL на основе итогового количества - PullRequest
5 голосов
/ 04 марта 2010

У меня есть реализация для этого, которая использует супер волосатый рекурсивный CTE, за которым действительно трудно следить / поддерживать. Я надеялся, что один из мозгов SO сможет придумать более простой код подхода tSQL для выполнения следующего:

Таблица документов

DocID    SortOrder    PageCount    StartPgNum   EndPgNum
5        1            2               {1}          {2}
8        2            7               {3}          {9}
22       3            3               {10}         {12}

Для приведенной выше таблицы мне нужен запрос для заполнения StartPgNum и EndPgNum (Примеры значений, включенных в пример в {}, чтобы сделать намерения более понятными для того, что мне нужно.

Предположения:
* DocID, SortOrder и PageCount предварительно заполнены.
* StartPgNum и EndgNum должны быть заполнены кодом tSQL.
* SortOrder всегда начинается с 1 и является непрерывным без пробелов.
* Документы должны иметь схему непрерывной нумерации страниц в соответствии с SortOrder

Ответы [ 6 ]

4 голосов
/ 04 марта 2010

Обновлено, чтобы быть лучше:)

DECLARE @temp TABLE (DocID INT, SortOrder INT, PageCount INT)

INSERT INTO @temp VALUES (5, 1, 2)
INSERT INTO @temp VALUES (8, 2, 7)
INSERT INTO @temp VALUES (22, 3, 3)

SELECT
    *,
    StartPgNum + PageCount-1 AS EndPgNum
FROM
(SELECT
    DocID,
    SortOrder,
    PageCount,
    ISNULL((SELECT SUM(PageCount)+1 FROM @temp WHERE SortOrder < parent.SortOrder), 1) AS StartPgNum
FROM
    @temp parent) _temp
3 голосов
/ 05 марта 2010

Я провел некоторое тестирование на всех решениях, представленных здесь в других ответах, моей оригинальной опции "Hairy Recursive CTE" и ради полноты простого подхода на основе курсора.К моему большому удивлению, опция курсора показала лучший результат с явным отрывом во всех моих тестах (1K строк, 10KRows, 50K Rows, 500K Rows)

Вот среднее время для каждого подхода для записей 10K:
Волосатая рекурсивная CTE: 3 минуты 55 секунд
ПРИМЕНЕНИЕ КРЕСТА (Бен Демпси): 21-25 секунд
СУБЪЕКТЫ (Тим Хоури): 19-21 секунда
КУРСОР: 1-2 секунды

Вот мое решение на основе курсора:

Declare @temp TABLE(
 DocID INT PRIMARY KEY NOT NULL, 
 SortOrder INT NOT NULL, 
 PageCount INT NOT NULL,
 BegPg int,
 EndPg int
)

Insert into @temp (DocID,SortOrder,PageCount) 
SELECT top 50000 docid, ROW_NUMBER() OVER (ORDER BY DOCID),Pages FROM tblDocuments

DECLARE @PC int
SET @PC=1
DECLARE @FetchPageCount int
DECLARE @FetchDocID int

DECLARE myCursor CURSOR FOR 
SELECT DocID, PageCount FROM @temp ORDER BY SortOrder

OPEN myCursor
FETCH NEXT FROM myCursor INTO @FetchDocID,@FetchPageCount

WHILE @@FETCH_STATUS = 0
BEGIN

  UPDATE @temp SET BegPg=@PC, EndPg=@PC+ @FetchPageCount-1   
  WHERE (Docid=@fetchDocid)

     SET @PC = @PC + @FetchPageCount

    FETCH NEXT FROM myCursor INTO @FetchDocID,@FetchPageCount
END 
CLOSE myCursor
DEALLOCATE myCursor

SELECT * FROM @temp

Кто бы могдогадался?Возможно, курсоры не всегда являются злом.

Слово предупреждения: чтобы не поддаваться искушению заменить обновление на синтаксис "WHERE CURRENT OF myCursor", оно выполнялось намного медленнее, чем при использовании текущей версии с предложением where., хотя все еще быстрее, чем большинство других подходов.

1 голос
/ 04 марта 2010

SQL 2008 с использованием перекрестного применения (итоговое значение)

/*
DocID    SortOrder    PageCount    StartPgNum   EndPgNum
5        1            2               {1}          {2}
8        2            7               {3}          {9}
22       3            3               {10}        {12}
*/

Declare @MyTable TABLE(
DocID int,
SortOrder int,
PageCount int
)

Insert into @MyTable(DocID,SortOrder,PageCount)
values (5,1,2), (8,2,7), (22,3,3)

select 
    T1.docID, 
    T1.Sortorder, 
    T1.Pagecount, 
    (T.RunningTotal - T1.Pagecount) + 1 StartPgNum , 
    T.RunningTotal EndPgNum

FROM    @MyTable T1
        CROSS APPLY ( Select SUM(PageCount) RunningTotal FROM @MyTable where SortOrder <= T1.SortOrder) T
order by T1.sortorder
1 голос
/ 04 марта 2010

Может быть, может помочь одно из этих трех решений, так как это своего рода проблема "промежуточного итога": http://www.sqlteam.com/article/calculating-running-totals

1 голос
/ 04 марта 2010

Самый быстрый способ сделать это - Quirky Update . Это зависит от того, попадаете ли вы в «Microsoft явно не говорит, что это работает, поэтому я буду избегать» этого или нет ...

В противном случае вы находитесь на волосатой рекурсивной территории CTE (как вы уже обнаружили) или на треугольном соединении (которое может стать кошмаром для большого набора данных).

0 голосов
/ 04 марта 2010

Я решил решить эти две проблемы, создав функции: одна для первой страницы, вторая для последней.Вот функции и запрос, который будет работать.

CREATE FUNCTION dbo.GetFirstPage(@SortOrder int) 
RETURNS int
as
BEGIN
DECLARE @FirstPage int
SET @FirstPage = 1
IF(@SortOrder > 1)
BEGIN
SELECT @FirstPage = SUM(PageCount) + 1
FROM Documents
WHERE SortOrder < @SortOrder
END
RETURN @FirstPage
END

CREATE FUNCTION dbo.GetLastPage(@FirstPage int, @PageCount int)
RETURNS int 
AS
BEGIN
RETURN (@FirstPage + @PageCount -1)
END

И, наконец, запрос.

SELECT * ,  
        dbo.GetFirstPage(SortOrder) AS FirstPage,
        dbo.GetLastPage(dbo.GetFirstPage(SortOrder),Pagecount) AS LastPage 
FROM Documents
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...