【问题标题】:Combining Rows in SQL Viia Recursive Query在 SQL Viia 递归查询中组合行
【发布时间】:2016-09-19 14:14:50
【问题描述】:

我有下表。

Animal  Vaccine_Date    Vaccine
Cat     2/1/2016        y
Cat     2/1/2016        z
Dog     2/1/2016        z
Dog     1/1/2016        x
Dog     2/1/2016        y

我希望得到如下所示的结果。

Animal  Vaccine_Date    Vaccine
Dog     1/1/2016        x
Dog     2/1/2016        y,z
Cat     2/1/2016        y,z

我有以下代码,它是通过我在“Combine(concatenate) rows based on dates via SQL”的其他帖子提供的

WITH RECURSIVE recCTE AS
(
    SELECT 
        animal, 
        vaccine_date,
        CAST(min(vaccine) as VARCHAR(50)) as vaccine, --big enough to hold concatenated list 
        cast (1 as int) as depth --used to determine the largest/last group_concate (the full group) in the final select
    FROM TableOne

    GROUP BY 1,2


    UNION ALL

    SELECT 
        recCTE.animal,
        recCTE.vaccine_date,
        trim(trim(recCTE.vaccine)|| ',' ||trim(TableOne.vaccine)) as vaccine,
        recCTE.depth + cast(1 as int) as depth
    FROM recCTE 
        INNER JOIN TableOne ON
            recCTE.animal = TableOne.animal AND
            recCTE.vaccine_date =  TableOne.vaccine_date and
           TableOne.vaccine > recCTE.vaccine

           WHERE recCTE.depth < 5


)

--Now select the result with the largest depth for each animal/vaccine_date combo
SELECT * FROM recCTE
QUALIFY ROW_NUMBER() OVER (PARTITION BY animal,vaccine_date ORDER BY depth desc) =1

但这会导致以下结果。

Animal  Vaccine_Date    vaccine     depth
Cat     2/1/2016        y,z,z,z,z   5
Dog     1/1/2016        x           1
Dog     2/1/2016        y,z,z,z,z   5

“z”不断重复。这是因为代码中的任何内容都超过了最低限度的疫苗。为了解决这个问题,代码更改为以下内容。

WITH RECURSIVE recCTE AS
(
    SELECT 
        animal, 
        vaccine_date,
        CAST(min(vaccine) as VARCHAR(50)) as vaccine, --big enough to hold concatenated list 
        cast (1 as int) as depth, --used to determine the largest/last group_concate (the full group) in the final select
        vaccine as vaccine_check
    FROM TableOne

    GROUP BY 1,2,5


    UNION ALL

    SELECT 
        recCTE.animal,
        recCTE.vaccine_date,
        trim(trim(recCTE.vaccine)|| ',' ||trim(TableOne.vaccine)) as vaccine,
        recCTE.depth + cast(1 as int) as depth,
        TableOne.vaccine as vaccine_check
    FROM recCTE 
        INNER JOIN TableOne ON
            recCTE.animal = TableOne.animal AND
            recCTE.vaccine_date =  TableOne.vaccine_date and
           TableOne.vaccine > recCTE.vaccine and
           vaccine_check <> recCTE.vaccine_check 

           WHERE recCTE.depth < 5


)

--Now select the result with the largest depth for each animal/vaccine_date combo
SELECT * FROM recCTE
QUALIFY ROW_NUMBER() OVER (PARTITION BY animal,vaccine_date ORDER BY depth desc) =1

但是,结果如下。

Animal  Vaccine_Date    vaccine depth   vaccine_check
Cat     2/1/2016        y       1       y
Dog     1/1/2016        x       1       x
Dog     2/1/2016        y       1       y

代码中缺少什么以获得以下所需的结果。

Animal  Vaccine_Date    Vaccine
Dog     1/1/2016        x
Dog     2/1/2016        y,z
Cat     2/1/2016        y,z

【问题讨论】:

  • 这很接近。将 WHERE 上的最后两个条件更改为:TableOne.vaccine_check &gt; recCTE.vaccine_check,因为您希望疫苗检查不等于彼此,并且由于我们从 min(vaccine) 开始,我们希望它在迭代时抓取任何更大的东西。你是... 差不多了...
  • 您使用的是哪个数据库?
  • 当我更改 TableOne.vaccine &gt; recCTE.vaccine and vaccine_check &gt; recCTE.vaccine_check 时,结果只返回每只动物每天的最低疫苗量。结果与我的 OP 中倒数第二个结果表相同。此外,数据库是 Teradata。
  • 您的 Teradata 版本是什么?

标签: sql concatenation teradata recursive-query


【解决方案1】:

嗯。我手头没有 Teradata,但这是该项目的一个主要缺点(在我看来)。我认为这对你有用,但可能需要一些调整:

with tt as (
      select t.*,
             row_number() over (partition by animal, vaccine_date order by animal) as seqnum
             count(*) over (partition by animal, vaccine_date) as cnt
     ),
     recursive cte as (
      select animal, vaccine_date, vaccine as vaccines, seqnum, cnt
      from tt
      where seqnum = 1
      union all
      select cte.animal, cte.dte, cte.vaccines || ',' || t.vaccine, tt.seqnum, tt.cnt
      from cte join
           tt
           on tt.animal = cte.animal and
              tt.vaccine_date = cte.vaccine_date and
              tt.seqnum = cte.seqnum + 1
     )
select cte.*
from cte
where seqnum = cnt;

【讨论】:

    【解决方案2】:

    如果您的 Teradata Database 版本为 14.10 或更高版本,则它支持 XML 数据类型。这也意味着支持 XMLAGG 函数,这对您的情况很有用,并且可以让您避免递归。

    检查XMLAGG 函数是否存在,该函数作为 UDF 随 XML Services 一起安装:

    SELECT * FROM dbc.FunctionsV WHERE FunctionName = 'XMLAGG'
    

    如果是这样,那么查询将如下所示:

    SELECT
      animal,
      vaccine_date
      TRIM(TRAILING ',' FROM CAST(XMLAGG(vaccine || ',' ORDER BY vaccine) AS VARCHAR(10000)))
    FROM
      tableone
    GROUP BY 1,2
    

    我无法测试此 atm,但我相信这应该可以进行细微调整。

    【讨论】:

    • 很遗憾,我使用的版本不支持 XMLAGG。
    【解决方案3】:

    我能够使用以下 SQL 获得所需的结果。这似乎根本不是很有效,也不是动态的。但是,我可以根据需要添加额外的子查询,以按日期按动物组合更多疫苗。

    select 
    qrya.animal
    ,qrya.vaccine_date
    ,case when qrya.vac1 is not null then qrya.vac1 else null end ||','||case when qrya.animal=qryb.animal and qrya.vaccine_date=qryb.vaccine_date then qryb.Vac2 else 'End' end as  vaccine_List
    from
    (
    select
    
    qry1.Animal
    ,qry1.Vaccine_Date
    
    ,case when qry1.Vaccine_Rank = 1  then qry1.vaccine end as Vac1
    
    
    from
    (
    select 
    animal
    ,vaccine_date
    ,vaccine
    ,row_number() over (partition by animal,vaccine_date order by vaccine) as Vaccine_Rank
    from TableOne
    ) as qry1
    
    where vac1 is not null
    
    group by qry1.Animal,
    qry1.Vaccine_Date
    ,case when qry1.Vaccine_Rank = 1  then qry1.vaccine end 
    ) as qrya
    join
    (
    select
    
    qry1.Animal
    ,qry1.Vaccine_Date
    
    ,case when qry1.Vaccine_Rank = 2  then qry1.vaccine end as Vac2
    
    
    from
    (
    select 
    animal
    ,vaccine_date
    ,vaccine
    ,row_number() over (partition by animal,vaccine_date order by vaccine) as Vaccine_Rank
    from TableOne
    ) as qry1
    
    where vac2 is not null
    
    group by qry1.Animal,
    qry1.Vaccine_Date
    ,case when qry1.Vaccine_Rank = 2  then qry1.vaccine end 
    ) as qryb
    on qrya.Animal=qryb.Animal
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-21
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 2020-09-28
      • 1970-01-01
      • 2020-11-27
      • 2015-07-13
      相关资源
      最近更新 更多