简体   繁体   中英

[SQL][DB2] Returns 0 when no records

The following query returns the number of transactions per month. I would like the query to return 0 in the month when there were no transactions in the TV_TRANSACTION table (no records).

WITH
msc (num) AS
(VALUES (TIMESTAMP('2019-10-01 00:00:00.001'))
UNION ALL
SELECT num + 1 MONTH
FROM msc
WHERE num < current timestamp - 1 MONTH
)
SELECT SUBSTR(CHAR(num),1,7), 
count(case when t.STATUS = 44 and t.TRANSACTION_TYPE = 0 then 1 else 0 end) FROM TV_TRANSACTION t
join TB_INSTALLATION i on t.INSTALLATION_ID = i.INSTALLATION_ID
right join msc mc on SUBSTR(CHAR(t.REGISTRATION_DATE),1,7) = SUBSTR(CHAR(mc.num),1,7)
where i.IB24_VERSION = 2
and date(t.REGISTRATION_DATE) >= '2019-10-01'
and t.STATUS = 44
and t.TRANSACTION_TYPE = 0
and SUBSTR(CHAR(t.REGISTRATION_DATE),1,7) >= SUBSTR(CHAR(i.IB24_VERSION_UPDATE_DATE),1,7)
group by SUBSTR(CHAR(mc.num),1,7)

At the moment the query returns the result

2020-02 | 1552
2020-03 | 6981
2020-04 | 16439
2020-05 | 5

How to modify a query to get something like this?:

2019-10 | 0
2019-11 | 0
2019-12 | 0
2020-01 | 0    
2020-02 | 1552
2020-03 | 6981
2020-04 | 16439
2020-05 | 5

Start the joins from the CTE and use LEFT joins for the other tables, including all the conditions in the ON clauses and not the WHERE clause:

WITH msc(num) AS (
  VALUES (TIMESTAMP('2019-10-01 00:00:00.001'))
  UNION ALL
  SELECT num + 1 MONTH
  FROM msc
  WHERE num < current timestamp - 1 MONTH
)
SELECT 
  SUBSTR(CHAR(num),1,7), 
  COUNT(CASE WHEN t.STATUS = 44 AND t.TRANSACTION_TYPE = 0 THEN 1 END) 
FROM msc mc
LEFT JOIN TV_TRANSACTION t 
ON    SUBSTR(CHAR(t.REGISTRATION_DATE),1,7) = SUBSTR(CHAR(mc.num),1,7)
  AND date(t.REGISTRATION_DATE) >= '2019-10-01'
  AND t.STATUS = 44
  AND t.TRANSACTION_TYPE = 0
LEFT JOIN TB_INSTALLATION i 
ON    t.INSTALLATION_ID = i.INSTALLATION_ID
  AND i.IB24_VERSION = 2
  AND SUBSTR(CHAR(t.REGISTRATION_DATE),1,7) >= SUBSTR(CHAR(i.IB24_VERSION_UPDATE_DATE),1,7)
GROUP BY SUBSTR(CHAR(mc.num),1,7)

Also I removed the ELSE part from the CASE expression of COUNT() .

It has been commented already that you need to put the conditions in the join rather than in the where clause. Also, the logic is much simpler if you start from the dates table, and then bring the additional tables with left join s.

As far as I see, other optimizations of fixes are possible:

  • you can use dates rather than timestamps in the cte

  • filtering using half-open intervals is more efficient than using string functions (this makes these predicates SARGeable, meaning that they can take advantage of an index)

  • there is no need for the conditional logic in the count() , since it matches the join conditions already

  • count() takes in account 0 (it ignores null values only)- presumably, you just want to count how many records are available in both tables for each date, so counting anything from the second left join is enough

  • I renamed the cte to something that makes more sense to me

This should be (close enough to) what you want:

with all_dates (dt) as
    (values (date('2019-10-01''))
    union all
    select num + 1 month from msc where num < current timestamp 
)
select 
    d.dt,
    count(i.installation_id) no_matches 
from  all_dates d
left join tv_transaction t
    on  t.registration_date >= d.dt
    and t.registration_date < d.dt + 1 day
    and t.status = 44
    and t.transaction_type = 0
left join tb_installation i 
    on  i.installation_id = t.installation_id
    and i.ib24_version = 2
    and i.ib24_version_update_date <= d.dt
group by d.dt

Thank you guys. I've modified the query below and it looks like it works

WITH
msc (num) AS
(VALUES (TIMESTAMP('2018-10-01 00:00:00.001'))
UNION ALL
SELECT num + 1 MONTH
FROM msc
WHERE num < current timestamp - 1 MONTH
)
SELECT substr(char(mc.num),1,7), 
count(case when t.STATUS = 44 and t.TRANSACTION_TYPE = 0 then 1 end) FROM TV_TRANSACTION t
join TB_INSTALLATION i on t.INSTALLATION_ID = i.INSTALLATION_ID and i.IB24_VERSION = 2
right join msc mc on SUBSTR(CHAR(t.REGISTRATION_DATE),1,7) = substr(char(mc.num),1,7) and date(t.REGISTRATION_DATE) >= '2019-10-01' and t.STATUS = 44 and t.TRANSACTION_TYPE = 0
and SUBSTR(CHAR(t.REGISTRATION_DATE),1,7) >= SUBSTR(CHAR(i.IB24_VERSION_UPDATE_DATE),1,7)
group by substr(char(mc.num),1,7)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM