Как разработать запрос для создания динамических столбцов из строк - PullRequest
8 голосов
/ 15 сентября 2010

У меня есть данные

Table1

ID     Name
-----------
1      n1
2      n2
3      n4

Table2

FID   YearS    Val
----------------------
1     2008     Up
1     2009     Down
1     2010     Up
2     2000     Up
2     2001     Down
2     2002     Up
2     2003     Up
3     2009     Down
3     2010     Up

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

ID  Yr1  Val1    Yr2   Val2  Yr3   Val3    Yr4  Val4
--------------------------------------------------------
1   2008 Up      2009  Down  2010  Up      NULL Null
2   2000 Up      2001  Down  2002  Up      2003 Up
3   2009 Down    2010  Up    NULL  NULL    NULL Null

На основе максимального количества столбцов для идентификатора я хочу создать имена столбцов, а затем преобразовать строки в столбцы. Можно ли это сделать с помощью SQL-запроса?

Ответы [ 3 ]

5 голосов
/ 16 сентября 2010

Я создал таблицу с именем «Table2», содержащую данные, которые вы показали выше под заголовком таблицы 2.

Вот SQL, который я использовал в SQL Server 2008.

WITH RankedValues AS
(
    SELECT
        FID AS ID,
        YearS,
        ROW_NUMBER() OVER(PARTITION BY FID ORDER BY YearS) AS YearSRank,
        Val
    FROM
        Table2
)
SELECT
    ID,
    MAX((CASE WHEN YearSRank = 1 THEN YearS ELSE 0 END)) AS Yr1,
    MAX((CASE WHEN YearSRank = 1 THEN Val ELSE '' END)) AS Val1,
    MAX((CASE WHEN YearSRank = 2 THEN YearS ELSE 0 END)) AS Yr2,
    MAX((CASE WHEN YearSRank = 2 THEN Val ELSE '' END)) AS Val2,
    MAX((CASE WHEN YearSRank = 3 THEN YearS ELSE 0 END)) AS Yr3,
    MAX((CASE WHEN YearSRank = 3 THEN Val ELSE '' END)) AS Val3,
    MAX((CASE WHEN YearSRank = 4 THEN YearS ELSE 0 END)) AS Yr4,
    MAX((CASE WHEN YearSRank = 4 THEN Val ELSE '' END)) AS Val4
FROM
    RankedValues
GROUP BY
    ID

Приведенный выше SQL приведет к следующему:

ID   Yr1     Val1  Yr2     Val2    Yr3     Val3  Yr4     Val4
---------------------------------------------------------------------
1    2008    Up    2009    Down    2010    Up    0    
2    2000    Up    2001    Down    2002    Up    2003    Up
3    2009    Down  2010    Up      0             0    

Причина, по которой вы не видите значения NULL, связана с ELSE в каждом операторе CASE.Если у вас есть NULL значения, просто удалите ELSE 0 и ELSE '', как требуется.

В настоящее время я не знаю, возможно ли сделать этот универсальный, например: обработать неизвестное количестворазличных FID, поскольку это также будет означать генерацию имен столбцов (Yr1, al1, Yr2 и т. д.) в общем.

Вы могли бы достичь этого с помощью динамического SQL, но, как я не большой поклонникдинамический SQL Я бы попробовал найти другой способ решения этой проблемы.

- Правка (добавлен сводный подход для полноты) -

Я посмотрел ссылку, опубликованную Джо Стефанелли, и добавилSQL ниже для вашего требования.Хотя мне не нравится идея динамического SQL, я не смог найти другой способ в этом конкретном случае.

DECLARE @query VARCHAR(4000)
DECLARE @years VARCHAR(2000)

SELECT @years = STUFF((
    SELECT DISTINCT
        '],[' + ltrim(str(YearS))
    FROM Table2
    ORDER BY '],[' + ltrim(str(YearS))
    FOR XML PATH('')), 1, 2, '') + ']'

SET @query =
    'SELECT * FROM
    (
        SELECT FID AS ID,YearS,Val
        FROM Table2
    ) AS t
    PIVOT (MAX(Val) FOR YearS IN (' + @years + ')) AS pvt'

EXECUTE (@query)

Это приведет к следующему:

ID  2000    2001    2002    2003    2008    2009    2010
---------------------------------------------------------
1   NULL    NULL    NULL    NULL    Up      Down    Up
2   Up      Down    Up      Up      NULL    NULL    NULL
3   NULL    NULL    NULL    NULL    NULL    Down    Up

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

3 голосов
1 голос
/ 15 сентября 2010

Этот запрос должен помочь

;WITH cte AS
    (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY FID ORDER BY FID, YearS) AS NUMBER
    FROM Table2
    )

SELECT t.ID, MAX(CASE WHEN cte.number = 1 THEN cte.YearS END) as yr1, MAX(CASE WHEN cte.number = 1 THEN cte.Val END) as val1,
MAX(CASE WHEN cte.number = 2 THEN cte.YearS END) as yr2, MAX(CASE WHEN cte.number = 2 THEN cte.Val END) as val2,
MAX(CASE WHEN cte.number = 3 THEN cte.YearS END) as yr3, MAX(CASE WHEN cte.number = 3 THEN cte.Val END) as val3,
MAX(CASE WHEN cte.number = 4 THEN cte.YearS END) as yr4, MAX(CASE WHEN cte.number = 4 THEN cte.Val END) as val4
FROM Table1 T
JOIN cte ON t.ID = cte.FID
GROUP BY t.ID
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...