I want to get the first row of each status for each id.
There can be multiple rows for each status. So I want to get the first occurrence of each status based on the previous status.
e.g. info_required first occurs at row 2, then it changes to another status pending at row 4, and then info_required again at row 6.
Likewise, status pending first at row 4, then at row 8 since the status changed after row4, it needs to be in the resultset.
Hence below I want to get the row number 1, 2, 4, 6, and 8.
WITH t1 AS (
SELECT 1 AS row, 'A' AS id, 'created' AS status, '2021-05-18 18:30:00'::timestamp AS created_at UNION ALL
SELECT 2 AS row, 'A' AS id, 'info_required' AS status, '2021-05-19 11:30:00'::timestamp AS created_at UNION ALL
SELECT 3 AS row, 'A' AS id, 'info_required' AS status, '2021-05-19 12:00:00'::timestamp AS created_at UNION ALL
SELECT 4 AS row, 'A' AS id, 'pending' AS status, '2021-05-19 12:30:00'::timestamp AS created_at UNION ALL
SELECT 5 AS row, 'A' AS id, 'pending' AS status, '2021-05-20 13:30:00'::timestamp AS created_at UNION ALL
SELECT 6 AS row, 'A' AS id, 'info_required' AS status, '2021-05-20 14:30:00'::timestamp AS created_at UNION ALL
SELECT 7 AS row, 'A' AS id, 'info_required' AS status, '2021-05-20 15:30:00'::timestamp AS created_at UNION ALL
SELECT 8 AS row, 'A' AS id, 'pending' AS status, '2021-05-20 16:30:00'::timestamp AS created_at
)
SELECT *
FROM t1
Using CONDITIONAL_CHANGE_EVENT
WITH cte AS (
SELECT *, CONDITIONAL_CHANGE_EVENT(status) over (partition by id
order by created_at) AS cce
FROM t1
)
SELECT *
FROM cte
QUALIFY ROW_NUMBER() OVER(PARTITION BY id, cce ORDER BY created_at) = 1;
Data preparation:
CREATE TABLE t1 AS
WITH t1 AS (
SELECT 1 AS row_, 'A' AS id, 'created' AS status, '2021-05-18 18:30:00'::timestamp AS created_at UNION ALL
SELECT 2 AS row_, 'A' AS id, 'info_required' AS status, '2021-05-19 11:30:00'::timestamp AS created_at UNION ALL
SELECT 3 AS row_, 'A' AS id, 'info_required' AS status, '2021-05-19 12:00:00'::timestamp AS created_at UNION ALL
SELECT 4 AS row_, 'A' AS id, 'pending' AS status, '2021-05-19 12:30:00'::timestamp AS created_at UNION ALL
SELECT 5 AS row_, 'A' AS id, 'pending' AS status, '2021-05-20 13:30:00'::timestamp AS created_at UNION ALL
SELECT 6 AS row_, 'A' AS id, 'info_required' AS status, '2021-05-20 14:30:00'::timestamp AS created_at UNION ALL
SELECT 7 AS row_, 'A' AS id, 'info_required' AS status, '2021-05-20 15:30:00'::timestamp AS created_at UNION ALL
SELECT 8 AS row_, 'A' AS id, 'pending' AS status, '2021-05-20 16:30:00'::timestamp AS created_at
)
SELECT *
FROM t1;
Cte part:
SELECT *, CONDITIONAL_CHANGE_EVENT(status) over (partition by id
order by created_at) AS cce
FROM t1;
You can use lag() and qualify():
select t.*
from t
qualify lag(status) over (partition by id order by created_at) is distinct from status;
Related
Here We have a duplicate ID, Need to get distinct of ID and SUM, So it should be 13 as Amount
I know it is possible to do this by first getting the distinct query and then on top of the query getting the SUM.
Is there a way to achieve this in one single query
with data as
(
select 1 as ID ,'ABC' as Name, 'Paid' as Status, 10 as Amount
union all
select 1 as ID ,'ABC' as Name, 'Paid' as Status, 10 as Amount
union all
select 2 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
union all
select 3 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
union all
select 4 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
)
select * , sum(amount) over (partition by name ,status) from data
There might be a better way to do, but here's my take:
with data as
(
select 1 as ID ,'ABC' as Name, 'Paid' as Status, 10 as Amount
union all
select 1 as ID ,'ABC' as Name, 'Paid' as Status, 10 as Amount
union all
select 2 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
union all
select 3 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
union all
select 4 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
)
select sum(Amount) from (select distinct * from data) a
Here is my take,
with data as
(
select 1 as ID ,'ABC' as Name, 'Paid' as Status, 10 as Amount
union all
select 1 as ID ,'ABC' as Name, 'Paid' as Status, 10 as Amount
union all
select 2 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
union all
select 3 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
union all
select 4 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
)
select sum(Amount) from data d where not exists(select 'x' from data d2 where d2.amount = d.amount and d2.id > d.id)
I'd leverage Snowflake's QUALIFY function to do something along these lines:
with data as
(
select 1 as ID ,'ABC' as Name, 'Paid' as Status, 10 as Amount
union all
select 1 as ID ,'ABC' as Name, 'Paid' as Status, 10 as Amount
union all
select 2 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
union all
select 3 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
union all
select 4 as ID ,'ABC' as Name, 'Paid' as Status, 1 as Amount
),
filter as (
select *
from data
qualify row_number() over (partition by id order by name) = 1
)
select * , sum(amount) over (partition by name ,status)
from filter;
Oracle 11g
How can I return single row of min(email_sent) for each email_type per ID?
That is, I'd like to get the first invitation date and the first confirmation date into a single row.
with mailings as (
select 1 as recipient_id, 'INVITE' as email_type, to_date('JAN-01-2020','MON-DD-YYYY') as email_sent from dual union all
select 1 as recipient_id, 'INVITE' as email_type, to_date('JAN-02-2020','MON-DD-YYYY') as email_sent from dual union all
select 1 as recipient_id, 'INVITE' as email_type, to_date('JAN-03-2020','MON-DD-YYYY') as email_sent from dual union all
select 1 as recipient_id, 'CONFIRM'as email_type, to_date('JAN-10-2020','MON-DD-YYYY') as email_sent from dual union all
select 1 as recipient_id, 'CONFIRM'as email_type, to_date('JAN-11-2020','MON-DD-YYYY') as email_sent from dual
)
select *
from (
select recipient_id,
email_type,
min(email_sent) over (partition by recipient_id, email_type) as first_invite,
min(email_sent) over (partition by recipient_id, email_type) as first_confirmation,
rank() over (partition by recipient_id, email_type order by email_sent) as email_type
from mailings
)
where email_type=1;
Desired Result: Report the dates for the first invitation and the first confirmation.
Recipient_ID FIRST_INVITE FIRST_CONFIRMATION
1 JAN-01-2020 JAN-10-2020
Here you go, as per your wording, not your result (as you first sent, but you show last sent):
with mailings as (
select 1 as recipient_id, 'INVITE' as email_type, to_date('JAN-01-2020','MON-DD-YYYY') as email_sent from dual union all
select 1 , 'INVITE' , to_date('JAN-02-2020','MON-DD-YYYY') from dual union all
select 1 , 'INVITE' , to_date('JAN-03-2020','MON-DD-YYYY') from dual union all
select 1 , 'CONFIRM', to_date('JAN-10-2020','MON-DD-YYYY') from dual union all
select 1 , 'CONFIRM', to_date('JAN-11-2020','MON-DD-YYYY') from dual
)
select recipient_id,
min(case when email_type='INVITE' then email_sent else null end) sent,
min(case when email_type='CONFIRM' then email_sent else null end) confirmed
from mailings
group by recipient_id;
RECIPIENT_ID SENT CONFIRMED
1 01-JAN-20 10-JAN-20
My Data is given below
In the below sample latest record has T and last occurrence of T was updated on 3-Apr-17 so that row needs to be displayed
EMP EFFDT STATUS
11367 15-Apr-15 A
11367 14-Jun-15 A
11367 10-Aug-15 T
11367 2-Apr-17 A
11367 3-Apr-17 T *
11367 10-Apr-17 T
In the below sample latest record has T and last occurrence of T was updated on 23-Feb-18 so that row needs to be displayed
EMP EFFDT STATUS
20612 4-Sep-16 A
20612 23-Feb-18 T *
20612 20-Jul-18 T
In the below sample latest record has T and that is the only occurrence so display it
EMP EFFDT STATUS
20644 12-Jul-15 A
20644 8-Aug-16 A
20644 6-Oct-16 T*
In the below sample latest record does not has T so no need to display
EMP EFFDT STATUS
21155 18-May-17 T
21155 21-Jun-17 A
21155 13-Mar-18 T
21155 15-Aug-18 A
My Desired Output should be (* marked records)
EMP EFFDT STATUS
11367 3-Apr-17 T
20612 23-Feb-18 T
20644 6-Oct-16 T
This is an island and gap problem.
In the cte you try to found out what island have T as last update (t=0)
SQL DEMO
WITH cte as (
SELECT "EMP",
"EFFDT",
SUM(CASE WHEN "STATUS" <> 'T'
THEN 1
ELSE 0
END) OVER (partition by "EMP" ORDER BY "EFFDT" DESC) as t
FROM Table1
)
SELECT "EMP", MIN("EFFDT") as "EFFDT", MAX('T') as "STATUS"
FROM cte
WHERE t = 0
GROUP BY "EMP"
OUTPUT
| EMP | EFFDT | STATUS |
|-------|-----------------------|--------|
| 11367 | 2017-04-03 00:00:00.0 | T |
| 20612 | 2018-02-23 00:00:00.0 | T |
| 20644 | 2016-10-06 00:00:00.0 | T |
For debug you can try
SELECT *
FROM cte
to see how t values are created
WITH cte1
AS (
SELECT A.*
,lag(STATUS, 1, 0) OVER (
PARTITION BY EMP ORDER BY EFFDT
) AS PRIOR_STATUS
FROM Table1 A
)
SELECT EMP
,STATUS
,MAX(EFFDT) AS EFFDT
FROM cte1 A
WHERE A.STATUS = 'T'
AND A.PRIOR_STATUS <> 'T'
GROUP BY EMP
,STATUS
SQL Fiddle here: http://sqlfiddle.com/#!4/458733/18
alter session set nls_date_format = 'dd-Mon-rr';
Solution (including simulated data in with clause):
with
simulated_data (EMP, EFFDT, STATUS) as (
select 11367, to_date('15-Apr-15'), 'A' from dual union all
select 11367, to_date('14-Jun-15'), 'A' from dual union all
select 11367, to_date('10-Aug-15'), 'T' from dual union all
select 11367, to_date( '2-Apr-17'), 'A' from dual union all
select 11367, to_date( '3-Apr-17'), 'T' from dual union all
select 11367, to_date('10-Apr-17'), 'T' from dual union all
select 20612, to_date( '4-Sep-16'), 'A' from dual union all
select 20612, to_date('23-Feb-18'), 'T' from dual union all
select 20612, to_date('20-Jul-18'), 'T' from dual union all
select 20644, to_date('12-Jul-15'), 'A' from dual union all
select 20644, to_date( '8-Aug-16'), 'A' from dual union all
select 20644, to_date( '6-Oct-16'), 'T' from dual union all
select 21155, to_date('18-May-17'), 'T' from dual union all
select 21155, to_date('21-Jun-17'), 'A' from dual union all
select 21155, to_date('13-Mar-18'), 'T' from dual union all
select 21155, to_date('15-Aug-18'), 'A' from dual
)
-- End of simulated data (for testing only).
-- SQL query (solution) begins BELOW THIS LINE.
select emp, min(effdt) as eff_dt, 'T' as status
from (
select emp, effdt, status,
row_number() over (partition by emp, status
order by effdt desc) as rn,
min(status) keep (dense_rank last order by effdt)
over (partition by emp) as last_status
from simulated_data
)
where last_status = 'T' and status = 'T' and rn <= 2
group by emp
;
Output:
EMP EFF_DT STATUS
---------- --------- ------
11367 03-Apr-17 T
20612 23-Feb-18 T
20644 06-Oct-16 T
Explanation:
In the subquery, we add two columns to the input data. Column RN gives a rank within each partition by EMPNO and STATUS, in descending order by EFFDT. LAST_STATUS used the analytic version of the LAST() function to assign either T or A as the last status for each EMP (and it attaches this value to EVERY row for the EMP, regardless of each row's own STATUS).
In the outer query, we are only interested to retain the EMP where the last status was T. For those rows, we only want to retain the rows where the actual status of the row is in fact T (we know this will always include the last row for that EMP, by the way, and it will have RN = 1). Moreover, we are only interested in those rows where RN is 1 or possibly 2 (if there are at least two rows with status T for that EMP). Of these either one or two rows with status T for a given EMP, we want to get the EARLIEST date. That will be the ONLY date if there is no row with RN = 2 for that partition; otherwise, it will be the date from the earlier row, with RN = 2.
In the outer SELECT we select the EMP, the earliest date, and the status we already know, it is T (so we don't need any work for this - actually it is not clear why the third column is even needed, since it is known beforehand it will be T in all rows).
Assuming that A and T are the only statuses, this should work.
WITH cte1
AS (
SELECT A.EMP, A.EFFDT, A.STATUS
,min(STATUS) OVER (
PARTITION BY EMP ORDER BY EFFDT RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
) AS MIN_STATUS
FROM Table1 A
)
SELECT
cte1.EMP
,MIN(cte1.EFFDT) AS EFFDT
,MIN(cte1.STATUS) as STATUS
FROM cte1
WHERE cte1.MIN_STATUS = 'T'
GROUP BY EMP
EDIT: well, if you have another statues, let's make it more robust. Actually, it's almost the same as juan-carlos-oropeza proposed, but he missed "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" part.
Ooops, it IS the same solution: juan-carlos-oropeza used order by DESC istead of unbounded following.
with emp_status_log (EMP, EFFDT, STATUS) as
(
select 11367, to_date('15-Apr-15', 'dd-Mon-yy'), 'A' from dual union all
select 11367, to_date('14-Jun-15', 'dd-Mon-yy'), 'A' from dual union all
select 11367, to_date('10-Aug-15', 'dd-Mon-yy'), 'T' from dual union all
select 11367, to_date( '2-Apr-17', 'dd-Mon-yy'), 'A' from dual union all
select 11367, to_date( '3-Apr-17', 'dd-Mon-yy'), 'T' from dual union all
select 11367, to_date('10-Apr-17', 'dd-Mon-yy'), 'T' from dual union all
select 20612, to_date( '4-Sep-16', 'dd-Mon-yy'), 'A' from dual union all
select 20612, to_date('23-Feb-18', 'dd-Mon-yy'), 'T' from dual union all
select 20612, to_date('20-Jul-18', 'dd-Mon-yy'), 'T' from dual union all
select 20644, to_date('12-Jul-15', 'dd-Mon-yy'), 'A' from dual union all
select 20644, to_date( '8-Aug-16', 'dd-Mon-yy'), 'A' from dual union all
select 20644, to_date( '6-Oct-16', 'dd-Mon-yy'), 'T' from dual union all
select 21155, to_date('18-May-17', 'dd-Mon-yy'), 'T' from dual union all
select 21155, to_date('21-Jun-17', 'dd-Mon-yy'), 'A' from dual union all
select 21155, to_date('13-Mar-18', 'dd-Mon-yy'), 'T' from dual union all
select 21155, to_date('15-Aug-18', 'dd-Mon-yy'), 'A' from dual
)
,
-- End of simulated data (for testing only).
/* SQL query (solution) begins BELOW THIS LINE.
with--*/
cte1 as
(
select sl.*
,sum(decode(sl.STATUS, 'T', 0, 1)) OVER (
PARTITION BY sl.EMP ORDER BY sl.EFFDT RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
) AS non_t_count
from emp_status_log sl
)
select
cte1.emp
, min(cte1.effdt) as effdt
, min(cte1.status) as status
from cte1
where cte1.non_t_count = 0
group by cte1.emp
is there a way to get a total count of rows per {id, date} and the count > 1 per {id, date, columnX} in the same query?
For example, having such a table:
id date columnX
1 2017-04-20 a
1 2017-04-20 a
1 2017-04-18 b
1 2017-04-17 c
2 2017-04-20 a
2 2017-04-20 a
2 2017-04-20 c
2 2017-04-19 b
2 2017-04-19 b
2 2017-04-19 b
2 2017-04-19 b
2 2017-04-19 c
As the result, I wanna get the following table:
id date columnX count>1 count_total
1 2017-04-20 a 2 2
2 2017-04-20 a 2 3
2 2017-04-19 b 4 5
I tried to do it with partition by but receive weird results. I've heard Rollup function might be used but it seems like it's applicable only in legacy SQL, which is not the option for me.
If I understand correctly, you can use window functions:
select id, date, columnx, cnt,
(case when cnt > 1 then cnt else 0 end) as cnt_gt_1,
total_cnt
from (select id, date, columnx, count(*) as cnt
sum(count(*)) over (partition by id, date) as total_cnt
from t
group by id, date, columnx
) x
where cnt > 1;
Another possibility:
SELECT
id,
date,
data.columnX columnX,
data.count_ count_bigger_1,
count_total
FROM(
SELECT
id,
date,
ARRAY_AGG(columnX) data,
COUNT(1) count_total
FROM
`your_table_name`
GROUP BY
id, date
),
UNNEST(ARRAY(SELECT AS STRUCT columnX, count(1) count_ FROM UNNEST(data) columnX GROUP BY columnX HAVING count(1) > 1)) data
You can test it with simulated data:
WITH data AS(
SELECT 1 AS id, '2017-04-20' AS date, 'a' AS columnX UNION ALL
SELECT 1 AS id, '2017-04-20' AS date, 'a' AS columnX UNION ALL
SELECT 1 AS id, '2017-04-18' AS date, 'b' AS columnX UNION ALL
SELECT 1 AS id, '2017-04-17' AS date, 'c' AS columnX UNION ALL
SELECT 2 AS id, '2017-04-20' AS date, 'a' AS columnX UNION ALL
SELECT 2 AS id, '2017-04-20' AS date, 'a' AS columnX UNION ALL
SELECT 2 AS id, '2017-04-20' AS date, 'c' AS columnX UNION ALL
SELECT 2 AS id, '2017-04-19' AS date, 'b' AS columnX UNION ALL
SELECT 2 AS id, '2017-04-19' AS date, 'b' AS columnX UNION ALL
SELECT 2 AS id, '2017-04-19' AS date, 'b' AS columnX UNION ALL
SELECT 2 AS id, '2017-04-19' AS date, 'b' AS columnX UNION ALL
SELECT 2 AS id, '2017-04-19' AS date, 'c' AS columnX
)
SELECT
id,
date,
data.columnX columnX,
data.count_ count_bigger_1,
count_total
FROM(
SELECT
id,
date,
ARRAY_AGG(columnX) data,
COUNT(1) count_total
FROM
data
GROUP BY
id, date
),
UNNEST(ARRAY(SELECT AS STRUCT columnX, count(1) count_ FROM UNNEST(data) columnX GROUP BY columnX HAVING count(1) > 1)) data
This solution avoids the analytical function (which can be quite expensive depending on the input) and scales well to large volumes of data.
I recommend you to add into your example two more below rows
1 2017-04-20 x
1 2017-04-20 x
and check what solutions in two previous answers will give you:
It will be something like below:
id date columnX count>1 count_total
1 2017-04-20 a 2 4
1 2017-04-20 x 2 4
2 2017-04-20 a 2 3
2 2017-04-19 b 4 5
Notice two rows for id=1 and date=2017-04-20 and both having count_total=4
I am not sure if this is what you want - even though you might not even considered this scenario in your question
Anyway, I feel that to support more generic case like above your expectation of output should of be like below
Row id date x.columnX x.countX count_total
1 1 2017-04-20 x 2 4
a 2
2 2 2017-04-20 a 2 3
3 2 2017-04-19 b 4 5
where x is repeated field and each value represents respective columnX with its count
Below query does exactly this
#standardSQL
SELECT id, date,
ARRAY(SELECT x FROM UNNEST(x) AS x WHERE countX > 1) AS x,
count_total
FROM (
SELECT id, date, SUM(countX) AS count_total,
ARRAY_AGG(STRUCT<columnX STRING, countX INT64>(columnX, countX) ORDER BY countX DESC) AS X
FROM (
SELECT id, date,
columnX, COUNT(1) countX
FROM `yourTable`
GROUP BY id, date, columnX
)
GROUP BY id, date
HAVING count_total > 1
)
you can play/test it with dummy data from your question
#standardSQL
WITH `yourTable` AS(
SELECT 1 AS id, '2017-04-20' AS date, 'a' AS columnX UNION ALL
SELECT 1, '2017-04-20', 'a' UNION ALL
SELECT 1, '2017-04-20', 'x' UNION ALL
SELECT 1, '2017-04-20', 'x' UNION ALL
SELECT 1, '2017-04-18', 'b' UNION ALL
SELECT 1, '2017-04-17', 'c' UNION ALL
SELECT 2, '2017-04-20', 'a' UNION ALL
SELECT 2, '2017-04-20', 'a' UNION ALL
SELECT 2, '2017-04-20', 'c' UNION ALL
SELECT 2, '2017-04-19', 'b' UNION ALL
SELECT 2, '2017-04-19', 'b' UNION ALL
SELECT 2, '2017-04-19', 'b' UNION ALL
SELECT 2, '2017-04-19', 'b' UNION ALL
SELECT 2, '2017-04-19', 'c'
)
SELECT id, date,
ARRAY(SELECT x FROM UNNEST(x) AS x WHERE countX > 1) AS x,
count_total
FROM (
SELECT id, date, SUM(countX) AS count_total,
ARRAY_AGG(STRUCT<columnX STRING, countX INT64>(columnX, countX) ORDER BY countX DESC) AS X
FROM (
SELECT id, date,
columnX, COUNT(1) countX
FROM `yourTable`
GROUP BY id, date, columnX
)
GROUP BY id, date
HAVING count_total > 1
)
I have a table with the below data,
id cd_used
1 trl
1 upf
2 upf
3 trl
3 trl
I have to apply flatten feed logic and derive the below output.
id cd_used
1 trlupf
2 upfonly
3 trlonly
One of the method was filter the table using cd_used and for for two subqueries, and the result of intersect can be added to trlupf, any other methods for implementing this?
WITH tab_temp
AS (SELECT 1 AS id, 'trl' AS cd_used FROM DUAL
UNION ALL
SELECT 1 AS id, 'upf' AS cd_used FROM DUAL
UNION ALL
SELECT 2 AS id, 'upf' AS cd_used FROM DUAL
UNION ALL
SELECT 3 AS id, 'trl' AS cd_used FROM DUAL
UNION ALL
SELECT 3 AS id, 'trl' AS cd_used FROM DUAL)
SELECT t.id,
LISTAGG (t.cd_used, '') WITHIN GROUP (ORDER BY t.cd_used DESC)
"cd_used"
FROM (SELECT DISTINCT
id,
CASE
WHEN COUNT (DISTINCT cd_used) OVER (PARTITION BY id) = 1
THEN
cd_used || 'Only'
ELSE
cd_used
END
cd_used
FROM tab_temp) t
GROUP BY t.id;
WITH tab_temp
AS (SELECT 1 AS id, 'trl' AS cd_used FROM DUAL
UNION ALL
SELECT 1 AS id, 'upf' AS cd_used FROM DUAL
UNION ALL
SELECT 2 AS id, 'upf' AS cd_used FROM DUAL
UNION ALL
SELECT 3 AS id, 'trl' AS cd_used FROM DUAL
UNION ALL
SELECT 3 AS id, 'trl' AS cd_used FROM DUAL)
, aggage as
(
select id, listagg (cd_used) within group (order by cd_used) as new_cd
from (select distinct id, cd_used from tab_temp)
group by id
)
select id, case
when new_cd in (select cd_used from tab_temp) then new_cd||'only'
else new_cd
end as cd_used
from aggage