2016-09-19 5 views
0

次の表があります。SQL Viia再帰クエリの行の結合

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 
+0

これは近いです。 'TableOne.vaccine_check> recCTE.vaccine_check'のように、WHEREの最後の2つの条件を変更して、vaccine_checkを互いに等しくしないようにします。そして、min(ワクチン)から開始するので、それは反復する。あなたは... <サングラスを置く> AlmostThere ... – JNevill

+0

どのデータベースを使用していますか? – scaisEdge

+0

私は 'TableOne.vaccine> recCTE.vaccineと vaccine_check> recCTE.vaccine_check'を変更すると、結果は毎日の各動物の最小ワクチンを返します。結果は私のOPの2番目から最後の結果テーブルと同じです。また、dbはTeradataです。 – AlmostThere

答えて

0

Hmmm。私は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; 
0

Teradata Databaseのバージョンが14.10以降の場合は、XMLデータ型をサポートしています。これはまた、XMLAGG関数がサポートされていることを意味します。これは、あなたのケースで便利であり、再帰を避けることができます。 UDFとしてXMLサービスと一緒にインストールされてXMLAGG関数が存在する場合

チェック、:

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 

私は方法はありませんこの気圧をテストしますが、これは微妙な調整の可能性があると考えています。

+0

残念ながら、私が使用しているバージョンはXMLAGGをサポートしていません。 – AlmostThere

0

次の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