简体   繁体   中英

Count if date in date column is between start and end date [ Oracle SQL ]

This is my first post, so I hope I've posted this one correctly.

My problem:

I want to count the number of active customers per day, the last 30 days.

What I have so far:

In the first column I want to print today, and the last 29 days. This I have done with

select distinct trunc(sysdate-dayincrement, 'DD') AS DATES
from   (
  select level as dayincrement
  from   dual
  connect by level <= 30
)

I've picked it up here at stackoverflow, and it works perfectly. I can even extend the number of days returned to ex. 365 days. Perfect!

I also have a table that looks like this

|Cust# | Start date | End date  |
| 1000 | 01.01.2015 | 31.12.2015|
| 1001 | 02.01.2015 | 31.12.2016|
| 1002 | 02.01.2015 | 31.03.2015|
| 1003 | 03.01.2015 | 31.08.2015|

This is where I feel the problem starts

I would like to get this result:

| Dates    | # of cust |
|04.01.2015|     4     |
|03.01.2015|     4     |
|02.01.2015|     3     |
|01.01.2015|     1     |

Here the query would count 1 if:

  • Start date <= DATES
  • End date >= DATES

Else count 0.

I just don't know how to structure the query.

I tried this, but it didn't work.

count(
  IF ENDDATE <= DATES THEN
    IF STARTDATE >= DATES THEN 1 ELSE 0 END IF
  ELSE
    0
  END IF
) AS CUST

Any ideas?

You could write a CASE expression equivalent to your IF-ELSE construct.

For example,

SQL> SELECT COUNT(
  2    CASE
  3      WHEN hiredate <= sysdate
  4      THEN 1
  5      ELSE 0
  6    END ) AS CUST
  7  FROM emp;

      CUST
----------
        14

SQL>

However, looking at your desired output, it seems, you just need to use COUNT and GROUP BY . The date conditions should be in the filter predicate .

For example,

SELECT dates, COUNT(*) 
FROM   table_name
WHERE  dates BETWEEN start_date AND end_date
GROUP BY dates;

The following produces the results you're looking for. I had change the date generator to start on 04-JAN- 2015 instead of SYSDATE (which is, of course, in the year 2016 ), and to use LEVEL-1 to include 'current' day:

WITH CUSTS AS (SELECT 1000 AS CUST_NO, TO_DATE('01-JAN-2015', 'DD-MON-YYYY') AS START_DATE, TO_DATE('31-DEC-2015', 'DD-MON-YYYY') AS END_DATE FROM DUAL UNION ALL
               SELECT 1001 AS CUST_NO, TO_DATE('02-JAN-2015', 'DD-MON-YYYY') AS START_DATE, TO_DATE('31-DEC-2016', 'DD-MON-YYYY') AS END_DATE FROM DUAL UNION ALL
               SELECT 1002 AS CUST_NO, TO_DATE('02-JAN-2015', 'DD-MON-YYYY') AS START_DATE, TO_DATE('31-MAR-2015', 'DD-MON-YYYY') AS END_DATE FROM DUAL UNION ALL
               SELECT 1003 AS CUST_NO, TO_DATE('03-JAN-2015', 'DD-MON-YYYY') AS START_DATE, TO_DATE('31-AUG-2015', 'DD-MON-YYYY') AS END_DATE FROM DUAL ),
     DATES AS (SELECT DISTINCT TRUNC(TO_DATE('04-JAN-2015', 'DD-MON-YYYY') - DAYINCREMENT, 'DD') AS DT
                FROM (SELECT LEVEL-1 AS DAYINCREMENT
                        FROM DUAL
                        CONNECT BY LEVEL <= 30))
SELECT d.DT, COUNT(*)
  FROM CUSTS c
  CROSS JOIN DATES d
  WHERE d.DT BETWEEN c.START_DATE AND c.END_DATE
  GROUP BY d.DT
  ORDER BY DT DESC

Best of luck.

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