根据列中的值将单行转换为多行

时间:2017-09-14 11:11:43

标签: sql-server unpivot

我有一张表,其中每条记录代表一个人,并且有许多列用于指示他们参加的活动:

CREATE TABLE EventAttendees
(
    Person VARCHAR(100),
    [Event A] VARCHAR(1),
    [Event B] VARCHAR(1),
    [Event C] VARCHAR(1)
)

INSERT INTO EventAttendees
SELECT 'John Smith','x',NULL,NULL
UNION
SELECT 'Jane Doe',NULL,'x','x'
UNION
SELECT 'Phil White','x',NULL,'x'
UNION
SELECT 'Sarah Jenkins','x','x','x'

例如:

SELECT * FROM Event Attendees

/---------------|---------|---------|---------\
| Person        | Event A | Event B | Event C |
|---------------|---------|---------|---------|
| John Smith    |    x    |   NULL  |   NULL  |
| Jane Doe      |   NULL  |    x    |    x    |
| Phil White    |    x    |   NULL  |    x    |
| Sarah Jenkins |    x    |    x    |    x    |
\---------------|---------|---------|---------/

我想生成一个列出谁参加了哪些事件的列表,所以我想要的输出是:

/---------------|---------|
| Person        | Event   |
|---------------|---------|
| John Smith    | Event A |
| Jane Doe      | Event B |
| Jane Doe      | Event C |
| Phil White    | Event A |
| Phil White    | Event C |
| Sarah Jenkins | Event A |
| Sarah Jenkins | Event B |
| Sarah Jenkins | Event C |
\---------------|---------/

实际上我有超过3个事件,但上面是为了便于解释(这是一个家庭作业问题btw)。由于事件可能会在未来发生变化而且我无法控制我传递的数据,因此我真的需要一个可以处理任意数量的事件列的动态解决方案。

我假设我可以对UNPIVOT做点什么,但我无法弄清楚,或者在SO或其他地方找到一个好的例子来工作 - 有人可以帮忙吗? / p>

4 个答案:

答案 0 :(得分:1)

我使用super.awakeFromNib执行此操作:

outer apply

答案 1 :(得分:1)

尝试类似

的内容
    DECLARE @name varchar(30)
    DECLARE @sql varchar(1000) = 'SELECT * FROM (';
    DECLARE NameCursor CURSOR
        FOR select name from sys.all_columns where object_id = (select object_id from sys.tables where name='EventAttendees') and name!='Person'
    OPEN NameCursor
    FETCH NEXT FROM NameCursor INTO @name

    WHILE @@FETCH_STATUS = 0
    BEGIN

        SET @sql += 'SELECT Person, CASE WHEN [' + @name+'] = ''x'' THEN ''' + @name +''' END AS [Event] FROM EventAttendees'   
        FETCH NEXT FROM NameCursor INTO @name

        IF(@@FETCH_STATUS = 0)
        BEGIN
            SET @sql += ' UNION ';
        END

    END;
    CLOSE NameCursor;  
    DEALLOCATE NameCursor;
    SET @sql += ') AS EventAttendees
            WHERE Event is not null
            order by Person';

    execute (@sql);

对于动态sql,您可以尝试这样的事情:

base R

答案 2 :(得分:1)

如你所说,你可以用unpivot来做,你只需要确保你告诉它它是什么事件,否则你只是得到一个X:

CREATE TABLE #tmpEventAttendees
(
    Person VARCHAR(100),
    [Event A] VARCHAR(1),
    [Event B] VARCHAR(1),
    [Event C] VARCHAR(1)
)
INSERT INTO #tmpEventAttendees
SELECT 'John Smith','x',NULL,NULL
UNION
SELECT 'Jane Doe',NULL,'x','x'
UNION
SELECT 'Phil White','x',NULL,'x'
UNION
SELECT 'Sarah Jenkins','x','x','x'

SELECT Person, [Event]
FROM
(
  SELECT    Person                                                                                  , 
            CASE WHEN [Event A] IS NOT NULL THEN 'Event A' END AS [Event A]                             , 
            CASE WHEN [Event B] IS NOT NULL THEN 'Event B' END AS [Event B]                             ,
            CASE WHEN [Event C] IS NOT NULL THEN 'Event C' END AS [Event C]
  FROM #tmpEventAttendees
) AS cp
UNPIVOT 
(
  [Event] FOR [Events] IN ([Event A], [Event B], [Event C])
) AS up;

DROP TABLE #tmpEventAttendees

答案 3 :(得分:0)

找出我想到的解决方案,但是,它确实需要动态SQL来获取相关的列名以提供给UNPIVOT

declare @sql varchar(max)
set @sql = 
    'select Person, EventName
    from EventAttendees
    unpivot
    (
        Attended for EventName in (' + (select
                                        stuff((
                                            select ',' + QUOTENAME(c.[name])
                                            from sys.columns c
                                            join sys.objects o on c.object_id = o.object_id
                                            where o.[name] = 'EventAttendees'
                                            and c.column_id > 1
                                            order by c.[name]
                                            for xml path('')
                                        ),1,1,'') as colList) + ')
    ) unpiv
    where unpiv.Attended = ''x''
    order by Person, EventName'

exec (@sql)

在这个例子中,我假设Event列来自表中的第二列,但显然我可以在子查询中使用一些不同的逻辑来在必要时识别相关的列。

在我的示例数据中,这会得到所需的结果:

/---------------------------\
| Person        | EventName |
|---------------|-----------|
| Jane Doe      | Event B   |
| Jane Doe      | Event C   |
| John Smith    | Event A   |
| Phil White    | Event A   |
| Phil White    | Event C   |
| Sarah Jenkins | Event A   |
| Sarah Jenkins | Event B   |
| Sarah Jenkins | Event C   |
\---------------------------/

我认为我更喜欢使用光标,虽然我实际上并没有确认两种动态方法之间存在什么性能差异(如果有的话)。

感谢大家对这个问题的帮助和建议,一如既往地非常感谢!