Combining Rows in SQL Viia Recursive Query

泪湿孤枕 提交于 2019-12-12 02:36:53

问题


I have the following table.

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

I would like to get the results to be as shown below.

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

I have the following code which was supplied via my other post at "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

But this results in the following.

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

The "z" keeps repeating. This is because the code is saying anything greater than the minimum vaccine. To account for this, the code was changed to the following.

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

However, this resulted in the following.

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

What is missing in the code to get the desired results of the following.

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

回答1:


Hmmm. I don't have Teradata on hand but this is a major shortcoming in the project (in my opinion). I think this will work for you, but it might need some tweaking:

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:


If your Teradata Database version is 14.10 or higher it supports XML data type. This also means that XMLAGG function is supported which would be useful for your case and would let you avoid recursion.

Check if XMLAGG function exists, which is installed with XML Services as an UDF:

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

If it does, then the query would look like:

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

I have no way of testing this atm, but I believe this should work with possibility of minor tweaks.




回答3:


I was able to get the desired results with the following SQL. This doesn't seem very efficient at all and is not dynamic. However, I can add extra sub querys as needed to combine more vaccines by animal by date.

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


来源:https://stackoverflow.com/questions/39575359/combining-rows-in-sql-viia-recursive-query

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!