выберите в SQL Server 2005 - PullRequest
       12

выберите в SQL Server 2005

3 голосов
/ 24 августа 2010

У меня есть таблица следования:

ID  |  first  |  end
--------------------
a   |      1  |    3
b   |      3  |    8
c   |      8  |   10

Я хочу выбрать следующее:

 ID  |  first  |  end
---------------------
a-c  |      1  |   10   

Но я не могу этого сделать. Пожалуйста! Помоги мне. Спасибо!

Ответы [ 2 ]

1 голос
/ 24 августа 2010

Это работает для меня:

SELECT MIN(t.id)+'-'+MAX(t.id) AS ID,
       MIN(t.[first]) AS first,
       MAX(t.[end]) AS [end]
  FROM dbo.YOUR_TABLE t

Но пожалуйста , не используйте зарезервированные слова, такие как "конец" для имен столбцов .

0 голосов
/ 24 августа 2010

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

WITH Ancestors AS
(
    SELECT
        InitRow.[ID] AS [Ancestor],
        InitRow.[ID],
        InitRow.[first],
        InitRow.[end],
        0 AS [level],
        '00000' + InitRow.[ID] AS [hacky_level_plus_ID]
    FROM
        YOUR_TABLE AS InitRow
    WHERE
        NOT EXISTS
        (
            SELECT * FROM YOUR_TABLE AS PrevRow
            WHERE PrevRow.[end] = InitRow.[first]
        )
    UNION ALL
    SELECT
        ParentRow.Ancestor,
        ChildRow.[ID],
        ChildRow.[first],
        ChildRow.[end],
        ParentRow.level + 1 AS [level],
        -- Avoids having to build the recursive structure more than once.
        -- We know we will not be over 5 digits since CTEs have a recursion
        -- limit of 32767.
        RIGHT('00000' + CAST(ParentRow.level + 1 AS varchar(4)), 5)
            + ChildRow.[ID] AS [hacky_level_plus_ID]
    FROM
        Ancestors AS ParentRow
        INNER JOIN YOUR_TABLE AS ChildRow
            ON ChildRow.[first] = ParentRow.[end]
)
SELECT
    Ancestors.Ancestor + '-' + SUBSTRING(MAX([hacky_level_plus_ID]),6,10) AS [IDs],
-- Without the [hacky_level_plus_ID] column, you need to do it this way:
--  Ancestors.Ancestor + '-' +
--      (SELECT TOP 1 Children.ID FROM Ancestors AS Children
--      WHERE Children.[Ancestor] = Ancestors.[Ancestor]
--      ORDER BY Children.[level] DESC) AS [IDs],
    MIN(Ancestors.[first]) AS [first],
    MAX(Ancestors.[end]) AS [end]
FROM
    Ancestors
GROUP BY
    Ancestors.Ancestor
-- If needed, add OPTION (MAXRECURSION 32767)

Краткое объяснениечто делает каждая часть:

Предложение WITH Ancestors AS (...) создает общее табличное выражение (в основном подзапрос) с именем Ancestors.Первый SELECT в этом выражении устанавливает базовую линию: все строки, у которых нет соответствующей записи до него.

Затем во второй SELECT начинается рекурсия. Так как он ссылается на Ancestorsкак часть запроса, он использует строки, которые он уже добавил в таблицу, а затем выполняет соединение с новыми из YOUR_TABLE.Это будет рекурсивно находить все больше и больше строк для добавления в конец каждой цепочки.

Последнее предложение - SELECT, которое использует эту рекурсивную таблицу, которую мы создали.Это просто GROUP BY, так как мы сохранили исходный идентификатор в столбце Ancestor, поэтому начало и конец - это простые MIN и MAX.

Сложная фигураидентификатор последней строки в цепочке.Есть два способа сделать это, оба показаны в запросе.Вы можете либо присоединиться к рекурсивной таблице, в этом случае она будет заново строить рекурсивную таблицу, либо вы можете попытаться отслеживать последний элемент по ходу работы.(Если создание рекурсивного списка связанных записей стоит дорого, вы определенно хотите минимизировать количество раз, которое вам нужно сделать.)

Способ, которым он отслеживает ход, состоит в том, чтобы отслеживать его положение вцепочка (столбец level - обратите внимание, как мы добавляем 1 каждый раз, когда мы повторяем), обнуляем ее, а затем вставляем идентификатор в конце.Затем получение элемента с максимальным значением level - это просто MAX с последующим извлечением данных level.

Если CTE придется слишком много рекурсировать, это вызовет ошибку, но яполагаю, что вы можете настроить это, используя опцию MAXRECURSION.По умолчанию установлено значение 100. Если вам нужно установить его выше этого значения, вы можете рассмотреть возможность не использовать для этого рекурсивный CTE.

Это также не очень хорошо обрабатывает искаженные данные.Если у вас есть две записи с одинаковым first или записью, в которой first == end, то это не будет работать правильно, и вам, возможно, придется настроить условия соединения внутри CTE или использовать другой подход.

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

...