每个用户的Teradata SQL堆栈行

时间:2016-03-07 21:24:33

标签: sql teradata

有没有办法按用户堆叠/分组字符串/文本?

我有数据

    USER    STATES
     1      CA
     1      AR
     1      IN
     2      CA
     3      CA
     3      NY
     4      CA
     4      AL
     4      SD
     4      TX

我需要的是

    USER    STATES
     1      CA / AR / IN
     2      CA
     3      CA / NY
     4      CA / AL / SD / TX

我尝试了交叉连接,然后尝试了另一个交叉连接但数据假脱机。谢谢!

3 个答案:

答案 0 :(得分:2)

如果安装了Teradata的XML服务,那么会有一个名为XMLAGG的函数,它会返回类似的结果:CA, AR, IN

SELECT user, 
   TRIM(TRAILING ',' FROM (XMLAGG(TRIM(states)|| ',' /* optionally ORDER BY ...*/) (VARCHAR(10000))))
FROM tab
GROUP BY 1

顺便说一句,使用递归会导致使用大量的假脱机,因为在返回最后一行之前,所有中间行都保留在假脱机中。

答案 1 :(得分:1)

不幸的是Teradata中没有GROUP_CONCAT或任何字符串聚合函数(至少没有我知道的),所以实现结果的一种方法是使用递归,因为你不知道每个用户的最大状态值。

对于递归,您应该使用易失性表,因为递归部分中不允许OLAP函数。这是一个未经测试的代码(遗憾的是我无法对其进行测试),因此可能存在一些错误,但是应该给出概念并进行一些故障排除(如果需要)给出预期的结果。

将Volatile Table的定义中的yourtable替换为您的真实表名。

CREATE VOLATILE TABLE vt AS (
SELECT
    user
    , states
    , ROW_NUMBER() OVER (PARTITION BY user ORDER BY states) AS rn
    , COUNT(*) OVER (PARTITION BY user) AS cnt
FROM yourtable
) WITH DATA 
UNIQUE PRIMARY INDEX(user, rn)
ON COMMIT PRESERVE ROWS;

WITH RECURSIVE cte (user, list, rn) AS (
SELECT
    user
    , CAST(states AS VARCHAR(1000)) -- maximum size based on maximum number of rows * length of states
    , rn
FROM vt
WHERE rn = cnt -- start with last states row

UNION ALL

SELECT
    vt.user
    , cte.list || ',' || vt.states
    , vt.rn
FROM vt
JOIN cte ON vt.user = cte.user AND vt.rn = cte.rn - 1 -- append a row that is rn-1 of your rows for a given user
)
SELECT user, list
FROM cte
WHERE rn = 1; -- going from last to first, in this condition there should be entire list

此解决方案并不完美 - 它会强制引擎在查询处理期间将临时结果存储在临时区域中。您可能会遇到No more spool space错误。

答案 2 :(得分:1)

我不是专家,但这应该有用。您可能需要根据您的具体要求对其进行一些修改。希望这有帮助!

    CREATE VOLATILE TABLE temp AS (                                     
     SELECT                                     
       USER                                                         
       ,STATES                                                                              
       ,ROW_NUMBER() OVER (PARTITION BY USER ORDER BY STATES) AS rn                                     
     FROM yourtable                                     
    ) WITH DATA PRIMARY INDEX(USER) ON COMMIT PRESERVE ROWS;                                        

    WITH RECURSIVE rec_test(US,ST, LVL)                                     
       AS                                       
       (                                        
        SELECT USER,STATES (VARCHAR(10)),1                                      
        FROM temp                                       
        WHERE rn = 1                                        
        UNION ALL                                       
        SELECT  USER, TRIM(STATES) || ', ' || ST,LVL+1                                      
        FROM temp INNER JOIN rec_test                                       
        ON USER = US                                        
       AND temp.rn = rec_test.lvl+1                                     
       )                                        
       SELECT US,ST, LVL                                        
       FROM rec_test                                        
       QUALIFY RANK() OVER(PARTITION BY US ORDER BY LVL DESC) = 1;