Select rows with no date range overlap - sql
Imagine the following Loans table:
BorrowerID StartDate DueDate
=============================================
1 2012-09-02 2012-10-01
2 2012-10-05 2012-10-21
3 2012-11-07 2012-11-09
4 2012-12-01 2013-01-01
4 2012-12-01 2013-01-14
1 2012-12-20 2013-01-06
3 2013-01-07 2013-01-22
3 2013-01-15 2013-01-18
1 2013-02-20 2013-02-24
How would I go about selecting the distinct BorrowerIDs of those who have only ever taken out a single loan at a time? This includes borrowers who have only ever taken out a single loan, as well as those who have taken out more than one, provided if you were to draw a time line of their loans, none of them would overlap. For example, in the table above, it should find borrowers 1 and 2 only.
I've tried experimenting with joining the table to itself, but haven't really managed to get anywhere. Any pointers much appreciated!
Solution for dbo.Loan with PRIMARY KEY
To solve this you need a two step approach as detailed in the following SQL Fiddle. I did add a LoanId column to your example data and the query requires that such a unique id exists. If you don't have that, you need to adjust the join clause to make sure that a loan does not get matched to itself.
MS SQL Server 2008 Schema Setup:
CREATE TABLE dbo.Loans
(LoanID INT, [BorrowerID] int, [StartDate] datetime, [DueDate] datetime)
GO
INSERT INTO dbo.Loans
(LoanID, [BorrowerID], [StartDate], [DueDate])
VALUES
(1, 1, '2012-09-02 00:00:00', '2012-10-01 00:00:00'),
(2, 2, '2012-10-05 00:00:00', '2012-10-21 00:00:00'),
(3, 3, '2012-11-07 00:00:00', '2012-11-09 00:00:00'),
(4, 4, '2012-12-01 00:00:00', '2013-01-01 00:00:00'),
(5, 4, '2012-12-01 00:00:00', '2013-01-14 00:00:00'),
(6, 1, '2012-12-20 00:00:00', '2013-01-06 00:00:00'),
(7, 3, '2013-01-07 00:00:00', '2013-01-22 00:00:00'),
(8, 3, '2013-01-15 00:00:00', '2013-01-18 00:00:00'),
(9, 1, '2013-02-20 00:00:00', '2013-02-24 00:00:00')
GO
First you need to find out which loans overlap with another loan. The query uses <= to compare the start and due dates. That counts loans where the second one starts the same day the first one ends as overlapping. If you need those to not be overlapping use < instead in both places.
Query 1:
SELECT
*,
CASE WHEN EXISTS(SELECT 1 FROM dbo.Loans L2
WHERE L2.BorrowerID = L1.BorrowerID
AND L2.LoanID <> L1.LoanID
AND L1.StartDate <= L2.DueDate
AND L2.StartDate <= l1.DueDate)
THEN 1
ELSE 0
END AS HasOverlappingLoan
FROM dbo.Loans L1;
Results:
| LOANID | BORROWERID | STARTDATE | DUEDATE | HASOVERLAPPINGLOAN |
|--------|------------|----------------------------------|---------------------------------|--------------------|
| 1 | 1 | September, 02 2012 00:00:00+0000 | October, 01 2012 00:00:00+0000 | 0 |
| 2 | 2 | October, 05 2012 00:00:00+0000 | October, 21 2012 00:00:00+0000 | 0 |
| 3 | 3 | November, 07 2012 00:00:00+0000 | November, 09 2012 00:00:00+0000 | 0 |
| 4 | 4 | December, 01 2012 00:00:00+0000 | January, 01 2013 00:00:00+0000 | 1 |
| 5 | 4 | December, 01 2012 00:00:00+0000 | January, 14 2013 00:00:00+0000 | 1 |
| 6 | 1 | December, 20 2012 00:00:00+0000 | January, 06 2013 00:00:00+0000 | 0 |
| 7 | 3 | January, 07 2013 00:00:00+0000 | January, 22 2013 00:00:00+0000 | 1 |
| 8 | 3 | January, 15 2013 00:00:00+0000 | January, 18 2013 00:00:00+0000 | 1 |
| 9 | 1 | February, 20 2013 00:00:00+0000 | February, 24 2013 00:00:00+0000 | 0 |
Now, with that information you can determine the borrowers that have no overlapping loans with this query:
Query 2:
WITH OverlappingLoans AS (
SELECT
*,
CASE WHEN EXISTS(SELECT 1 FROM dbo.Loans L2
WHERE L2.BorrowerID = L1.BorrowerID
AND L2.LoanID <> L1.LoanID
AND L1.StartDate <= L2.DueDate
AND L2.StartDate <= l1.DueDate)
THEN 1
ELSE 0
END AS HasOverlappingLoan
FROM dbo.Loans L1
),
OverlappingBorrower AS (
SELECT BorrowerID, MAX(HasOverlappingLoan) HasOverlappingLoan
FROM OverlappingLoans
GROUP BY BorrowerID
)
SELECT *
FROM OverlappingBorrower
WHERE hasOverlappingLoan = 0;
Or you could even get more information by counting the loans as well as counting the number of loans that have overlapping other loans for each borrower in the database. (Note, if loan A and loan B overlap, both will be counted as overlapping loan by this query)
Results:
| BORROWERID | HASOVERLAPPINGLOAN |
|------------|--------------------|
| 1 | 0 |
| 2 | 0 |
Query 3:
WITH OverlappingLoans AS (
SELECT
*,
CASE WHEN EXISTS(SELECT 1 FROM dbo.Loans L2
WHERE L2.BorrowerID = L1.BorrowerID
AND L2.LoanID <> L1.LoanID
AND L1.StartDate <= L2.DueDate
AND L2.StartDate <= l1.DueDate)
THEN 1
ELSE 0
END AS HasOverlappingLoan
FROM dbo.Loans L1
)
SELECT BorrowerID,COUNT(1) LoanCount, SUM(hasOverlappingLoan) OverlappingCount
FROM OverlappingLoans
GROUP BY BorrowerID;
Results:
| BORROWERID | LOANCOUNT | OVERLAPPINGCOUNT |
|------------|-----------|------------------|
| 1 | 3 | 0 |
| 2 | 1 | 0 |
| 3 | 3 | 2 |
| 4 | 2 | 2 |
Solution for dbo.Loan without PRIMARY KEY
UPDATE: As the requirement actually calls for a solution that does not rely on a unique identifier for each loan, I made the following changes:
1) I added a borrower that has two loans with the same start and due dates
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE dbo.Loans
([BorrowerID] int, [StartDate] datetime, [DueDate] datetime)
GO
INSERT INTO dbo.Loans
([BorrowerID], [StartDate], [DueDate])
VALUES
( 1, '2012-09-02 00:00:00', '2012-10-01 00:00:00'),
( 2, '2012-10-05 00:00:00', '2012-10-21 00:00:00'),
( 3, '2012-11-07 00:00:00', '2012-11-09 00:00:00'),
( 4, '2012-12-01 00:00:00', '2013-01-01 00:00:00'),
( 4, '2012-12-01 00:00:00', '2013-01-14 00:00:00'),
( 1, '2012-12-20 00:00:00', '2013-01-06 00:00:00'),
( 3, '2013-01-07 00:00:00', '2013-01-22 00:00:00'),
( 3, '2013-01-15 00:00:00', '2013-01-18 00:00:00'),
( 1, '2013-02-20 00:00:00', '2013-02-24 00:00:00'),
( 5, '2013-02-20 00:00:00', '2013-02-24 00:00:00'),
( 5, '2013-02-20 00:00:00', '2013-02-24 00:00:00')
GO
2) Those "equal date" loans require an additional step:
Query 1:
SELECT BorrowerID, StartDate, DueDate, COUNT(1) LoanCount
FROM dbo.Loans
GROUP BY BorrowerID, StartDate, DueDate;
Results:
| BORROWERID | STARTDATE | DUEDATE | LOANCOUNT |
|------------|----------------------------------|---------------------------------|-----------|
| 1 | September, 02 2012 00:00:00+0000 | October, 01 2012 00:00:00+0000 | 1 |
| 1 | December, 20 2012 00:00:00+0000 | January, 06 2013 00:00:00+0000 | 1 |
| 1 | February, 20 2013 00:00:00+0000 | February, 24 2013 00:00:00+0000 | 1 |
| 2 | October, 05 2012 00:00:00+0000 | October, 21 2012 00:00:00+0000 | 1 |
| 3 | November, 07 2012 00:00:00+0000 | November, 09 2012 00:00:00+0000 | 1 |
| 3 | January, 07 2013 00:00:00+0000 | January, 22 2013 00:00:00+0000 | 1 |
| 3 | January, 15 2013 00:00:00+0000 | January, 18 2013 00:00:00+0000 | 1 |
| 4 | December, 01 2012 00:00:00+0000 | January, 01 2013 00:00:00+0000 | 1 |
| 4 | December, 01 2012 00:00:00+0000 | January, 14 2013 00:00:00+0000 | 1 |
| 5 | February, 20 2013 00:00:00+0000 | February, 24 2013 00:00:00+0000 | 2 |
3) Now, with each loan range unique, we can use the old technique again. However, we also need to account for those "equal date" loans. (L1.StartDate <> L2.StartDate OR L1.DueDate <> L2.DueDate) prevents a loan getting matched with itself. OR LoanCount > 1 accounts for "equal date" loans.
Query 2:
WITH NormalizedLoans AS (
SELECT BorrowerID, StartDate, DueDate, COUNT(1) LoanCount
FROM dbo.Loans
GROUP BY BorrowerID, StartDate, DueDate
)
SELECT
*,
CASE WHEN EXISTS(SELECT 1 FROM dbo.Loans L2
WHERE L2.BorrowerID = L1.BorrowerID
AND L1.StartDate <= L2.DueDate
AND L2.StartDate <= l1.DueDate
AND (L1.StartDate <> L2.StartDate
OR L1.DueDate <> L2.DueDate)
)
OR LoanCount > 1
THEN 1
ELSE 0
END AS HasOverlappingLoan
FROM NormalizedLoans L1;
Results:
| BORROWERID | STARTDATE | DUEDATE | LOANCOUNT | HASOVERLAPPINGLOAN |
|------------|----------------------------------|---------------------------------|-----------|--------------------|
| 1 | September, 02 2012 00:00:00+0000 | October, 01 2012 00:00:00+0000 | 1 | 0 |
| 1 | December, 20 2012 00:00:00+0000 | January, 06 2013 00:00:00+0000 | 1 | 0 |
| 1 | February, 20 2013 00:00:00+0000 | February, 24 2013 00:00:00+0000 | 1 | 0 |
| 2 | October, 05 2012 00:00:00+0000 | October, 21 2012 00:00:00+0000 | 1 | 0 |
| 3 | November, 07 2012 00:00:00+0000 | November, 09 2012 00:00:00+0000 | 1 | 0 |
| 3 | January, 07 2013 00:00:00+0000 | January, 22 2013 00:00:00+0000 | 1 | 1 |
| 3 | January, 15 2013 00:00:00+0000 | January, 18 2013 00:00:00+0000 | 1 | 1 |
| 4 | December, 01 2012 00:00:00+0000 | January, 01 2013 00:00:00+0000 | 1 | 1 |
| 4 | December, 01 2012 00:00:00+0000 | January, 14 2013 00:00:00+0000 | 1 | 1 |
| 5 | February, 20 2013 00:00:00+0000 | February, 24 2013 00:00:00+0000 | 2 | 1 |
This query logic did not change (other than switching out the beginning).
Query 3:
WITH NormalizedLoans AS (
SELECT BorrowerID, StartDate, DueDate, COUNT(1) LoanCount
FROM dbo.Loans
GROUP BY BorrowerID, StartDate, DueDate
),
OverlappingLoans AS (
SELECT
*,
CASE WHEN EXISTS(SELECT 1 FROM dbo.Loans L2
WHERE L2.BorrowerID = L1.BorrowerID
AND L1.StartDate <= L2.DueDate
AND L2.StartDate <= l1.DueDate
AND (L1.StartDate <> L2.StartDate
OR L1.DueDate <> L2.DueDate)
)
OR LoanCount > 1
THEN 1
ELSE 0
END AS HasOverlappingLoan
FROM NormalizedLoans L1
),
OverlappingBorrower AS (
SELECT BorrowerID, MAX(HasOverlappingLoan) HasOverlappingLoan
FROM OverlappingLoans
GROUP BY BorrowerID
)
SELECT *
FROM OverlappingBorrower
WHERE hasOverlappingLoan = 0;
Results:
| BORROWERID | HASOVERLAPPINGLOAN |
|------------|--------------------|
| 1 | 0 |
| 2 | 0 |
4) In this counting query we need to incorporate the "equal date" loan counts again. For that we use SUM(LoanCount) instead of a plain COUNT. We also have to multiply hasOverlappingLoan with the LoanCount to get the correct overlapping count again.
Query 4:
WITH NormalizedLoans AS (
SELECT BorrowerID, StartDate, DueDate, COUNT(1) LoanCount
FROM dbo.Loans
GROUP BY BorrowerID, StartDate, DueDate
),
OverlappingLoans AS (
SELECT
*,
CASE WHEN EXISTS(SELECT 1 FROM dbo.Loans L2
WHERE L2.BorrowerID = L1.BorrowerID
AND L1.StartDate <= L2.DueDate
AND L2.StartDate <= l1.DueDate
AND (L1.StartDate <> L2.StartDate
OR L1.DueDate <> L2.DueDate)
)
OR LoanCount > 1
THEN 1
ELSE 0
END AS HasOverlappingLoan
FROM NormalizedLoans L1
)
SELECT BorrowerID,SUM(LoanCount) LoanCount, SUM(hasOverlappingLoan*LoanCount) OverlappingCount
FROM OverlappingLoans
GROUP BY BorrowerID;
Results:
| BORROWERID | LOANCOUNT | OVERLAPPINGCOUNT |
|------------|-----------|------------------|
| 1 | 3 | 0 |
| 2 | 1 | 0 |
| 3 | 3 | 2 |
| 4 | 2 | 2 |
| 5 | 2 | 2 |
I strongly suggest finding a way to use my first solution, as a loan table without a primary key is a, let's say "odd" design. However, if you really can't get there, use the second solution.
I got it working but in a bit convoluted way. It first gets borrowers that don't meet criteria in the inner query and returns the rest. The inner query has 2 parts:
Get all overlapping borrowings not starting on the same day.
Get all borrowings starting on the same date.
select distinct BorrowerID from borrowings
where BorrowerID NOT IN
(
select b1.BorrowerID from borrowings b1
inner join borrowings b2
on b1.BorrowerID = b2.BorrowerID
and b1.StartDate < b2.StartDate
and b1.DueDate > b2.StartDate
union
select BorrowerID from borrowings
group by BorrowerID, StartDate
having count(*) > 1
)
I had to use 2 separate inner queries as your table doesn't have a unique identifier for each record and using b1.StartDate <= b2.StartDate as I should have makes a record join to itself. It would be good to have a separate identifier for each record.
try
with cte as
(
select *,
row_number() over (partition by b order by s) r
from loans
)
select l1.b
from loans l1
except
select c1.b
from cte c1
where exists (
select 1
from cte c2
where c2.b = c1.b
and c2.r <> c1.r
and (c2.s between c1.s and c1.e
or c1.s between c2.s and c2.e)
)
If you're on SQL 2012, you can do it like this:
with cte as (
select
BorrowerID,
StartDate,
DueDate,
lag(DueDate) over (partition by borrowerid order by StartDate, DueDate) as PrevDueDate
from test
)
select
distinct BorrowerID
from cte
where BorrowerID not in
(select BorrowerID
from cte
where StartDate <= PrevDueDate)
Related
MSSQL Count Multiple Columns
Say I have a table like this in ms sql 2008: +------+--------+---------+ | year | JAN | FEB | +------+--------+---------+ | 2016 | 5K2 | 5K2 | | 2016 | 5K2 | 5K2 | | 2016 | 5K2 | 5K2 | | 2016 | 8Z | 8Z | | 2016 | R5205 | R5205 | | 2016 | 5K2 | 5K2 | | 2016 | 5K2 | 5K2 | | 2016 | NULL | NULL | | 2016 | TE | NULL | | 2016 | TE | NULL | | 2016 | 8Z | 8Z | +------+--------+---------+ And I want to get a count for each column, something like this +------+--------+---------+ | opt | JAN_cnt| FEB_cnt | +------+--------+---------+ | 5K2 | 5 | 4 | | 8Z | 2 | 2 | | R5205| 1 | 1 | | TE | 2 | 0 | | NULL | 1 | 4 | +------+--------+---------+ First, can this be done? Second, how? I have searched, but cant find exactly what I am looking for.
I think the simplest way is to use UNION ALL with conditional aggregation using CASE EXPRESSION : SELECT s.opt, COUNT(CASE WHEN s.ind_from = 1 THEN 1 END) as jan_cnt, COUNT(CASE WHEN s.ind_from = 2 THEN 1 END) as feb_cnt FROM ( SELECT t1.jan as opt,1 as ind_from FROM YourTable t1 UNION ALL SELECT t2.feb,2 FROM YourTable t2) s GROUP BY s.opt
I would advise putting the values into a different format: opt month cnt You can do this as: select opt, mon, count(*) as cnt from ((select jan as opt, 'jan' as mon from t) union all (select feb as opt, 'feb' as mon from t) ) o group by opt, mon; It is easy enough to switch this to your format: select opt, sum(jan) as jan, sum(feb) as feb from ((select jan as opt, 1 as jan, 0 as feb from t) union all (select feb as opt, 0, 1, from t) ) o group by opt; I just prefer the first format. It is easier to generalize to more columns.
SELECT COALESCE(t1.JAN, t2.FEB), t1.JAN_cnt, t2.FEB_cnt FROM ( SELECT JAN, COUNT(*) AS JAN_cnt FROM yourTable GROUP BY JAN ) t1 FULL OUTER JOIN ( SELECT FEB, COUNT(*) AS FEB_cnt FROM yourTable GROUP BY FEB ) t2 ON t1.JAN = t2.FEB
Get the Last Two Non-Null Values for a column in SQL Server 2008 R2
Right now, I have a select statement that returns this result set: | date | id | price | +--------+-----+-------+ | Jan 01 | XYZ | 5.00 | | Jan 02 | XYZ | NULL | | Jan 03 | XYZ | NULL | | Jan 06 | XYZ | NULL | | Jan 07 | XYZ | 3.00 | | Jan 08 | XYZ | NULL | The problem is that I want to get the last known price for the product with the id of XYZ in the row if the value is NULL, but only to grab this if it is within two days. So for Jan 02 & Jan 03 I'd like to see the value from Jan 01, but not for Jan 06. Here's what I mean: | date | id | price | +--------+-----+-------+ | Jan 01 | XYZ | 5.00 | | Jan 02 | XYZ | 5.00 | (Jan 01) | Jan 03 | XYZ | 5.00 | (Jan 01) | Jan 06 | XYZ | NULL | << Stays NULL | Jan 07 | XYZ | 3.00 | | Jan 08 | XYZ | 3.00 | (Jan 07) Here's the table definition: CREATE TABLE dbo.catalogue ( [date] DATE NOT NULL , [id] VARCHAR(3) NOT NULL , [price] FLOAT ) And the Sample Data: INSERT INTO dbo.catalogue ( [date], [id], [price] ) VALUES ( '2015-01-01', 'XYZ', 5.00 ), ( '2015-01-02', 'XYZ', NULL ), ( '2015-01-03', 'XYZ', NULL ), ( '2015-01-06', 'XYZ', NULL ), ( '2015-01-07', 'XYZ', 3.00 ), ( '2015-01-08', 'XYZ', NULL ) Edit: Also, I should mention that this is within a stored procedure's subquery, so performance will definitely matter. Thanks in advance.
Another solution using APPLY: SELECT c.[date], c.id, price = ISNULL(c.price, x.price) FROM dbo.catalogue c OUTER APPLY( SELECT TOP 1 price FROM dbo.catalogue WHERE DATEDIFF(DAY, [date], c.[date]) <= 2 AND c.[date] > [date] AND id = c.id ORDER BY date DESC )x
If you were using 2012+ this would be a bit easier using LAG but you can do this with a cte in 2008. Thank you for posting consumable ddl and sample data. That makes this a lot easier. Here is one way to do this. with cte as ( select * , ROW_NUMBER() over (partition by id order by date) as RowNum from catalogue ) select c.date , c.id , isnull(case when DATEDIFF(day, c2.date, c.date) <= 2 then ( select MAX(price) from cte c3 where c3.RowNum >= c2.RowNum - 1 ) end, c.price) as NewPrice from cte c left join cte c2 on c2.RowNum = c.RowNum - 1
How to generate a compounded view of data over time in Oracle SQL
Say I have a base number 10 and a table that has a value of 20 associated to November 2013, and a value of 10 associated to March 2014. I want to populate a list of all months, and their compounded value. So from May-November 2013, the value should be 10, then between Nov and Mar, the value should be 10+20 and afterwards it should be 10+20+10. So in a table I have the following MONTH VALUE Nov-2013 20 Mar-2014 10 I'd like to have a select statement that somehow returns. There's an initial value of 10, hard-coded as the base. MONTH VALUE May-2013 10 Jun-2013 10 Jul-2013 10 Aug-2013 10 Sep-2013 10 Oct-2013 10 Nov-2013 30 Dec-2013 30 Jan-2014 30 Feb-2014 30 Mar-2014 40 Is this doable?
In case I understand your requirements correctly, SQL Fiddle Oracle 11g R2 Schema Setup: CREATE TABLE months ("MON" date, "VALUE" int) ; INSERT ALL INTO months ("MON", "VALUE") VALUES (date '2013-11-01', 20) INTO months ("MON", "VALUE") VALUES (date '2014-03-01', 10) SELECT * FROM dual ; Query 1: with months_interval as ( select date '2013-05-01' interval_start, max(mon) interval_end from months ) , all_months as ( select add_months(m.interval_start,level-1) mon from months_interval m connect by level <= months_between(interval_end, interval_start) + 1 ), data_to_sum as ( select am.mon, decode(am.mon, first_value(am.mon) over(order by am.mon), 10, m.value) value from months m, all_months am where am.mon = m.mon(+) ) select mon, value, sum(value) over(order by mon) cumulative from data_to_sum order by 1 Results: | MON | VALUE | CUMULATIVE | ---------------------------------------------------------- | May, 01 2013 00:00:00+0000 | 10 | 10 | | June, 01 2013 00:00:00+0000 | (null) | 10 | | July, 01 2013 00:00:00+0000 | (null) | 10 | | August, 01 2013 00:00:00+0000 | (null) | 10 | | September, 01 2013 00:00:00+0000 | (null) | 10 | | October, 01 2013 00:00:00+0000 | (null) | 10 | | November, 01 2013 00:00:00+0000 | 20 | 30 | | December, 01 2013 00:00:00+0000 | (null) | 30 | | January, 01 2014 00:00:00+0000 | (null) | 30 | | February, 01 2014 00:00:00+0000 | (null) | 30 | | March, 01 2014 00:00:00+0000 | 10 | 40 | This one is probably slightly suboptimal performance-wise (queries months table twice etc.) and should be optimized, but the idea is like this - pregenerate a list of months (I assumed your interval start is somehow fixed), left join it to your data, use analytic sum function.
weekly aggregate with CTE not behaving as expected
I have this USERS table with users that can be of two different types (A and B). I need to show a report with the aggregate per type for each week. The query I have so far works well except some weeks are not grouping properly. In the example below, the week starting Jan 28th should have one line, not two. Week Starts |Week| Type A | Type B ------------+----+--------+------ 2013-02-04 | 14 | 2 | 26 2013-01-28 | 13 | 5 | 191 2013-01-28 | 13 | 0 | 24 2013-01-21 | 12 | 1 | 134 2013-01-21 | 12 | 0 | 20 2013-01-14 | 11 | 1 | 143 2013-01-14 | 11 | 0 | 2 2013-01-07 | 10 | 0 | 233 2013-01-07 | 10 | 0 | 23 2012-12-31 | 9 | 0 | 12 2012-12-31 | 9 | 4 | 164 2012-12-31 | 9 | 0 | 20 SQL ;with cte as ( select DATEADD(m,-3,GETDATE()) firstday, DATEADD(m,-3,GETDATE()) + 6 - DATEDIFF(day, 0, DATEADD(m,-3,GETDATE())) %7 lastday, 1 week union all select lastday + 1, case when GETDATE() < lastday + 7 then GETDATE() else lastday + 7 end, week + 1 from cte where lastday < GETDATE() ) SELECT cast(firstday as date) 'Week Starts', cte.week as 'Week', Sum(CASE WHEN USR_TYPE = 'A' THEN 1 ELSE 0 END) As 'Type A', Sum(CASE WHEN USR_TYPE = 'B' THEN 1 ELSE 0 END) As 'Type B' FROM cte left join USERS ON cte.firstday <= USERS.CREATED AND cte.lastday > USERS.CREATED GROUP BY cte.week, cte.firstday, cte.lastday, DATEPART(YEAR,USERS.CREATED), DATEPART(wk,USERS.CREATED) ORDER BY week desc What am I doing wrong?
Without seeing any data from your users table I am going to take a guess. The list of dates you are generating in the CTE includes the time. You might need to cast() your firstday and lastday values as either a date or generate the list with no time. See a SQL Fiddle Demo Sample from your CTE and the new dates cast: | CASTFIRSTDAY | CASTLASTDAY | WEEK | FIRSTDAY | LASTDAY | --------------------------------------------------------------------------------------------------------- | 2012-11-05 | 2012-11-11 | 1 | November, 05 2012 20:08:10+0000 | November, 11 2012 20:08:10+0000 | | 2012-11-12 | 2012-11-18 | 2 | November, 12 2012 20:08:10+0000 | November, 18 2012 20:08:10+0000 | | 2012-11-19 | 2012-11-25 | 3 | November, 19 2012 20:08:10+0000 | November, 25 2012 20:08:10+0000 | | 2012-11-26 | 2012-12-02 | 4 | November, 26 2012 20:08:10+0000 | December, 02 2012 20:08:10+0000 | | 2012-12-03 | 2012-12-09 | 5 | December, 03 2012 20:08:10+0000 | December, 09 2012 20:08:10+0000 | | 2012-12-10 | 2012-12-16 | 6 | December, 10 2012 20:08:10+0000 | December, 16 2012 20:08:10+0000 | You might want to edit your CTE to return the date only values: ;with cte as ( select cast(DATEADD(m,-3,GETDATE()) as date) firstday, cast(DATEADD(m,-3,GETDATE()) + 6 - DATEDIFF(day, 0, DATEADD(m,-3,GETDATE())) %7 as DATE) lastday, 1 week union all select cast(DATEADD(DAY, 1, lastday) as date), case when cast(GETDATE() as date) < cast(DATEADD(DAY, 7, lastday) as date) then cast(GETDATE() as date) else cast(DATEADD(DAY, 7, lastday) as date) end, week + 1 from cte where cast(lastday as date) < cast(GETDATE() as date) ) select * from cte See SQL Fiddle with Demo
Doubts in query conditions
I have two tables 1.Employee EMP_NAME, EMP_CODE 2.Vacations EMP_NAME, EMP_CODE, VACATION_START_DATE-->date type VACATION_END_DATE-->date type, My question is how to query to get the EMP_NAME from table1(Employee), where the today is not in between VACATION_START_DATE and VACATION_END_DATE from table2(Vacations)..
Try this please: assuming that you do not have emp_name vacations table..as well as emp_code as the relationship between the two table. So you can use joins. SQLFIDDLE DEMO select e.emp_code, e.emp_name, v.start_date, v.end_Date from emp e inner join vacation v on e.emp_code = v.emp_code where not (Now() between v.start_date and v.end_date) ; | EMP_CODE | EMP_NAME | START_DATE | END_DATE | ------------------------------------------------------------------------------------------- | 1 | john | December, 10 2012 00:00:00+0000 | December, 20 2012 00:00:00+0000 | | 2 | kate | December, 20 2012 00:00:00+0000 | December, 30 2012 00:00:00+0000 | | 3 | tim | December, 24 2012 00:00:00+0000 | January, 01 2013 00:00:00+0000 | | 1 | john | January, 01 2013 00:00:00+0000 | January, 08 2013 00:00:00+0000 |
Not sure whether you meant "today" was a variable or the system date, so the following code may need to be modified, but I think it might work: SELECT EMP_NAME FROM EMPLOYEE WHERE NOT EXISTS (SELECT * FROM VACATIONS WHERE EMP_NAME = EMPLOYEE.EMP_NAME AND VACATIONS.VACATION_START_DATE >= Today AND VACATIONS.VACATION_END_DATE <= Today)