I have a table like below:
And the delete condition is:
delete from Employee only if the date is smaller than a specific date and no record is larger than that date
e.g.
if the date is 3/8/2014, then only record with EmployeeID 3 will be removed as the record with EmployeeID 4 has date larger than 3/8/2014, and EmployeeID 5 won't be removed as the date is 3/9/2014
if the date is 3/9/2014, then record with EmployeeID 3 and 5 will be removed, as the record with EmployeeID 4 has date larger than 3/9/2014
At first, I tried
delete from Employee where Date > #Date
But the above SQL would delete all records wherever the date is smaller than the #Date
What amendments should be made to the above SQL?
Try this:
DELETE FROM TableName
WHERE EmployeeID IN
(SELECT EmployeeID FROM TableName
GROUP BY EmployeeID
HAVING MAX(DATE)<=#Date)
Tested and verified.
See an example in SQL Fiddle.
Try this:
delete from Employee
where EmployeeID in
(select EmployeeID
from Employee
group by Employeeid
having max(Date) < #Date)
Here it is,
Declare #d date ='3/8/2014'
delete from myEmp where empID in
(
select empID from myEmp
group by empID
having MAX(empDate) <=#d
)
Link for Demo,
DEMO
create table #t (EmployeeID int, Date datetime)
insert #t values
(3, '20140304'),
(3, '20140305'),
(3, '20140306'),
(4, '20140307'),
(4, '20140308'),
(4, '20140310'),
(5, '20140309')
declare #date date = '20140308'
;with x as (
select t.*
from #t t
where Date <= #date and not exists (
select * from #t t2 where t.EmployeeId = t2.EmployeeID and Date > #date)
)
delete x;
Related
I have a lengthy query written in SQL that uses CTEs and multiple variables to produce a report of about 1500 customer records with many columns based on a particular date, #ToDate. Some of the tables are ordered CTEs so I only get the latest value based on the #ToDate.
I've omitted specifics but the structure is as follows:
Declare #ToDate date .....
Declare #Category varchar ....;
with cte1 as (select * from table1 where table1.start_date <= #ToDate and (table1.end_date > #ToDate or table1.end_date is null))
,cte2 as (select * from table2 where table2.start_date <= #ToDate and (table2.end_date > #ToDate or table2.end_date is null))
select * from cte1
left join cte2 on cte2.id = cte1.id
where .....
which gives me the following results
|RunDate |CustomerID|DOB |Category|Col5 |Col6 |
|----------|----------|----------|--------|------|------|
|2021-08-30|11111 |2000-01-01|Cat1 | | |
|2021-08-30|22222 |2000-02-02|Cat2 | | |
I'd like to run the same script multiple times but with a different date. So run with #ToDate = '2021-08-30' which gives me one set of results and then every past Monday n number of times which would give me results like this...
|RunDate |CustomerID|DOB |Category|Col5 |Col6
|----------|----------|----------|--------|------|------|
|2021-08-30|11111 |2000-01-01|Cat1 | | |
|2021-08-30|22222 |2000-02-02|Cat2 | | |
|2021-08-23|11111 |2000-01-01|Cat1 | | |
|2021-08-23|22222 |2000-02-02|Cat2 | | |
|2021-08-23|33333 |2000-03-03|Cat9 | | |
I do have a calendar table available so I can easily identify the past n Mondays (or other day I like).
The only variable to change is the #ToDate as this is the Run Date, or As At Date if you will. Essentially I want to run it multiple times for the past few Mondays so I can get what the results were like at 30-08, 23-08, 16-08 etc...
I've never used loops and research suggests I should maybe avoid them or use them as a last resort. I'm not sure on the best approach and if I do use loops, how I wrap it around my query.
Thanks in advance
The question really needs a bit more elaboration but I have give a guess at what you are trying to do with this example.
I have create a Customers and Orders table and then display the results for the date range
I don't think you need to loop with cursors and such as you can get the loop effect by just using the #DateRanges and join on that. it being a CTE or not.
Please let me know if this is not what you meant and I will remove the answer
-- Setup a temp table to hold the dates I want to look for
IF EXISTS (SELECT * FROM tempdb.dbo.sysobjects O WHERE O.xtype in ('U') AND O.id = object_id(N'tempdb..#DateRanges'))
BEGIN
PRINT 'Removing temp table #DateRanges'
DROP TABLE #DateRanges;
END
CREATE TABLE #DateRanges (
[Date] DATE
)
-- Add some dates
INSERT INTO #DateRanges ([Date])
VALUES ('2021-08-30'),
('2021-08-23'),
('2021-08-16')
-- Setup some customers
IF EXISTS (SELECT * FROM tempdb.dbo.sysobjects O WHERE O.xtype in ('U') AND O.id = object_id(N'tempdb..#Customers'))
BEGIN
PRINT 'Removing temp table #Customers'
DROP TABLE #Customers;
END
CREATE TABLE #Customers (
CustomerId BIGINT IDENTITY(1,1) NOT NULL,
[Name] NVARCHAR(50),
DOB DATE NOT NULL,
CONSTRAINT PK_CustomerId PRIMARY KEY (CustomerId)
)
INSERT INTO #Customers ([Name], DOB)
VALUES('Bob', '1989-01-01'),
('Robert', '1994-01-01'),
('Andrew', '1992-01-01');
-- Setup some orders
IF EXISTS (SELECT * FROM tempdb.dbo.sysobjects O WHERE O.xtype in ('U') AND O.id = object_id(N'tempdb..#Order'))
BEGIN
PRINT 'Removing temp table #Order'
DROP TABLE #Order;
END
CREATE TABLE #Order (
OrderId BIGINT IDENTITY(1,1) NOT NULL,
CustomerId BIGINT NOT NULL,
CreatedDate DATE NOT NULL,
Category NVARCHAR(50) NOT NULL,
CONSTRAINT PK_OrderId PRIMARY KEY (OrderId)
)
INSERT INTO #Order(CustomerId, CreatedDate, Category)
VALUES
(1, '2021-08-30', 'Cat1'),
(1, '2021-08-23', 'Cat2'),
(2, '2021-08-30', 'Cat1'),
(2, '2021-08-23', 'Cat2'),
(2, '2021-08-16', 'Cat3'),
(3, '2021-08-30', 'Cat1'),
(3, '2021-08-16', 'Cat2')
-- Using the #DateRanged temp table we can the use this to ge the data we need so no need for a loop
SELECT *
FROM #DateRanges AS DR
LEFT JOIN #Order AS O ON O.
CreatedDate <= DR.[Date] AND O.CreatedDate >= DATEADD(D, -6, DR.[Date])
I want to find max. and min. days between several records in my table. For example, in the following table I would like to have max. and min. days due to DATE field for each ID.
I'm using MS-SQL 2013
I know that there is dateiff to finding days between two dates but now, I want to find maximum and minimum days between several dates.
ID DATE
10 2016/01/13
10 2016/01/10
10 2016/11/01
10 2015/12/28
11 2015/12/11
11 2016/02/01
11 2015/01/01
Now, how can I find max. and min. days between DATEs for each ID?
Can you please help me to have the query in SQL?
This solution is a bit ugly (using two subqueries) but should get you started:
CREATE TABLE #DataTable (id INT, [date] DATETIME)
INSERT INTO #DataTable (id, [date])
VALUES (10, '20160113')
,(10, '20160110')
,(10, '20161101')
,(10, '20151211')
,(11, '20151211')
,(11, '20160201')
,(11, '20150101')
SELECT
id
, MIN([days]) AS mindays
, MAX([days]) AS maxdays
FROM (
SELECT
id
, DATEDIFF(DAY, [date], (SELECT MIN([date]) FROM #DataTable AS D1 WHERE D1.id = #DataTable.id AND D1.[date] > #DataTable.[date])) AS [days]
FROM #DataTable
) AS t
GROUP BY id
ORDER BY id
Easiest way to understand is by starting at the middle query (which can be run on its own). It delivers for each row the id, and the number of days between the date in the row and the next higher one from the same id.
The outer query is then a simple MIN MAX GROUP BY.
Ok, I've re-read your answer, are you looking for something like below;
Creating temp table and inserting data;
CREATE TABLE #DataTable (ID int, DATE DateTime)
INSERT INTO #DataTable (ID, DATE)
VALUES
(10, '2016-01-13')
,(10, '2016-01-10')
,(10, '2016-11-01')
,(10, '2015-12-11')
,(11, '2015-12-11')
,(11, '2016-02-01')
,(11, '2015-01-01')
Select statement to retrieve data;
DECLARE #StartDate DateTime; SET #StartDate = '2015-12-01'
DECLARE #EndDate DateTime; SET #EndDate = '2016-12-01'
SELECT
a.ID
,MIN(DATE) FirstDate
,MAX(DATE) LastDate
,DATEDIFF(day, MIN(DATE), MAX(DATE)) DayDiff
FROM #DataTable a
WHERE a.DATE BETWEEN #StartDate AND #EndDate
GROUP BY a.ID
You can remove the fields FirstDate and LastDate, this is just to show the dates that are being compared. You'll return a 0 value if there is only one date between your variable dates so you may have to account for this. Also probably a good idea to put something to check for a NULL.
Have you tried this:
Select ID, Min(Date), Max(Date) From MyTable Group By ID
I'm trying to retrieve the latest set of rows from a source table containing a foreign key, a date and other fields present. A sample set of data could be:
create table #tmp (primaryId int, foreignKeyId int, startDate datetime,
otherfield varchar(50))
insert into #tmp values (1, 1, '1 jan 2010', 'test 1')
insert into #tmp values (2, 1, '1 jan 2011', 'test 2')
insert into #tmp values (3, 2, '1 jan 2013', 'test 3')
insert into #tmp values (4, 2, '1 jan 2012', 'test 4')
The form of data that I'm hoping to retrieve is:
foreignKeyId maxStartDate otherfield
------------ ----------------------- -------------------------------------------
1 2011-01-01 00:00:00.000 test 2
2 2013-01-01 00:00:00.000 test 3
That is, just one row per foreignKeyId showing the latest start date and associated other fields - the primaryId is irrelevant.
I've managed to come up with:
select t.foreignKeyId, t.startDate, t.otherField from #tmp t
inner join (
select foreignKeyId, max(startDate) as maxStartDate
from #tmp
group by foreignKeyId
) s
on t.foreignKeyId = s.foreignKeyId and s.maxStartDate = t.startDate
but (a) this uses inner queries, which I suspect may lead to performance issues, and (b) it gives repeated rows if two rows in the original table have the same foreignKeyId and startDate.
Is there a query that will return just the first match for each foreign key and start date?
Depending on your sql server version, try the following:
select *
from (
select *, rnum = ROW_NUMBER() over (
partition by #tmp.foreignKeyId
order by #tmp.startDate desc)
from #tmp
) t
where t.rnum = 1
If you wanted to fix your attempt as opposed to re-engineering it then
select t.foreignKeyId, t.startDate, t.otherField from #tmp t
inner join (
select foreignKeyId, max(startDate) as maxStartDate, max(PrimaryId) as Latest
from #tmp
group by foreignKeyId
) s
on t.primaryId = s.latest
would have done the job, assuming PrimaryID increases over time.
Qualms about inner query would have been laid to rest as well assuming some indexes.
EDITED:
I'm working in Sql Server 2005 and I'm trying to get a year over year (YOY) count of distinct users for the current fiscal year (say Jun 1-May 30) and the past 3 years. I'm able to do what I need by running a select statement four times, but I can't seem to find a better way at this point. I'm able to get a distinct count for each year in one query, but I need it to a cumulative distinct count. Below is a mockup of what I have so far:
SELECT [Year], COUNT(DISTINCT UserID)
FROM
(
SELECT u.uID AS UserID,
CASE
WHEN dd.ddEnd BETWEEN #yearOneStart AND #yearOneEnd THEN 'Year1'
WHEN dd.ddEnd BETWEEN #yearTwoStart AND #yearTwoEnd THEN 'Year2'
WHEN dd.ddEnd BETWEEN #yearThreeStart AND #yearThreeEnd THEN 'Year3'
WHEN dd.ddEnd BETWEEN #yearFourStart AND #yearFourEnd THEN 'Year4'
ELSE 'Other'
END AS [Year]
FROM Users AS u
INNER JOIN UserDataIDMatch AS udim
ON u.uID = udim.udim_FK_uID
INNER JOIN DataDump AS dd
ON udim.udimUserSystemID = dd.ddSystemID
) AS Data
WHERE LOWER([Year]) 'other'
GROUP BY
[Year]
I get something like:
Year1 1
Year2 1
Year3 1
Year4 1
But I really need:
Year1 1
Year2 2
Year3 3
Year4 4
Below is a rough schema and set of values (updated for simplicity). I tried to create a SQL Fiddle, but I'm getting a disk space error when I attempt to build the schema.
CREATE TABLE Users
(
uID int identity primary key,
uFirstName varchar(75),
uLastName varchar(75)
);
INSERT INTO Users (uFirstName, uLastName)
VALUES
('User1', 'User1'),
('User2', 'User2')
('User3', 'User3')
('User4', 'User4');
CREATE TABLE UserDataIDMatch
(
udimID int indentity primary key,
udim.udim_FK_uID int foreign key references Users(uID),
udimUserSystemID varchar(75)
);
INSERT INTO UserDataIDMatch (udim_FK_uID, udimUserSystemID)
VALUES
(1, 'SystemID1'),
(2, 'SystemID2'),
(3, 'SystemID3'),
(4, 'SystemID4');
CREATE TABLE DataDump
(
ddID int identity primary key,
ddSystemID varchar(75),
ddEnd datetime
);
INSERT INTO DataDump (ddSystemID, ddEnd)
VALUES
('SystemID1', '10-01-2013'),
('SystemID2', '10-01-2014'),
('SystemID3', '10-01-2015'),
('SystemID4', '10-01-2016');
Unless I'm missing something, you just want to know how many records there are where the date is less than or equal to the current fiscal year.
DECLARE #YearOneStart DATETIME, #YearOneEnd DATETIME,
#YearTwoStart DATETIME, #YearTwoEnd DATETIME,
#YearThreeStart DATETIME, #YearThreeEnd DATETIME,
#YearFourStart DATETIME, #YearFourEnd DATETIME
SELECT #YearOneStart = '06/01/2013', #YearOneEnd = '05/31/2014',
#YearTwoStart = '06/01/2014', #YearTwoEnd = '05/31/2015',
#YearThreeStart = '06/01/2015', #YearThreeEnd = '05/31/2016',
#YearFourStart = '06/01/2016', #YearFourEnd = '05/31/2017'
;WITH cte AS
(
SELECT u.uID AS UserID,
CASE
WHEN dd.ddEnd BETWEEN #yearOneStart AND #yearOneEnd THEN 'Year1'
WHEN dd.ddEnd BETWEEN #yearTwoStart AND #yearTwoEnd THEN 'Year2'
WHEN dd.ddEnd BETWEEN #yearThreeStart AND #yearThreeEnd THEN 'Year3'
WHEN dd.ddEnd BETWEEN #yearFourStart AND #yearFourEnd THEN 'Year4'
ELSE 'Other'
END AS [Year]
FROM Users AS u
INNER JOIN UserDataIDMatch AS udim
ON u.uID = udim.udim_FK_uID
INNER JOIN DataDump AS dd
ON udim.udimUserSystemID = dd.ddSystemID
)
SELECT
DISTINCT [Year],
(SELECT COUNT(*) FROM cte cteInner WHERE cteInner.[Year] <= cteMain.[Year] )
FROM cte cteMain
Concept using an existing query
I have done something similar for finding out the number of distinct customers who bought something in between years, I modified it to use your concept of year, the variables you add would be that start day and start month of the year and the start year and end year.
Technically there is a way to avoid using a loop but this is very clear and you can't go past year 9999 so don't feel like putting clever code to avoid a loop makes sense
Tips for speeding up the query
Also when matching dates make sure you are comparing dates, and not comparing a function evaluation of the column as that would mean running the function on every record set and would make indices useless if they existed on dates (which they should). Use date add on
zero to initiate your target dates subtracting 1900 from the year, one from the month and one from the target date.
Then self join on the table where the dates create a valid range (i.e. yearlessthan to yearmorethan) and use a subquery to create a sum based on that range. Since you want accumulative from the first year to the last limit the results to starting at the first year.
At the end you will be missing the first year as by our definition it does not qualify as a range, to fix this just do a union all on the temp table you created to add the missing year and the number of distinct values in it.
DECLARE #yearStartMonth INT = 6, #yearStartDay INT = 1
DECLARE #yearStart INT = 2008, #yearEnd INT = 2012
DECLARE #firstYearStart DATE =
DATEADD(day,#yearStartDay-1,
DATEADD(month, #yearStartMonth-1,
DATEADD(year, #yearStart- 1900,0)))
DECLARE #lastYearEnd DATE =
DATEADD(day, #yearStartDay-2,
DATEADD(month, #yearStartMonth-1,
DATEADD(year, #yearEnd -1900,0)))
DECLARE #firstdayofcurrentyear DATE = #firstYearStart
DECLARE #lastdayofcurrentyear DATE = DATEADD(day,-1,DATEADD(year,1,#firstdayofcurrentyear))
DECLARE #yearnumber INT = YEAR(#firstdayofcurrentyear)
DECLARE #tempTableYearBounds TABLE
(
startDate DATE NOT NULL,
endDate DATE NOT NULL,
YearNumber INT NOT NULL
)
WHILE #firstdayofcurrentyear < #lastYearEnd
BEGIN
INSERT INTO #tempTableYearBounds
VALUES(#firstdayofcurrentyear,#lastdayofcurrentyear,#yearNumber)
SET #firstdayofcurrentyear = DATEADD(year,1,#firstdayofcurrentyear)
SET #lastdayofcurrentyear = DATEADD(year,1,#lastdayofcurrentyear)
SET #yearNumber = #yearNumber + 1
END
DECLARE #tempTableCustomerCount TABLE
(
[Year] INT NOT NULL,
[CustomerCount] INT NOT NULL
)
INSERT INTO #tempTableCustomerCount
SELECT
YearNumber as [Year],
COUNT(DISTINCT CustomerNumber) as CutomerCount
FROM Ticket
JOIN #tempTableYearBounds ON
TicketDate >= startDate AND TicketDate <=endDate
GROUP BY YearNumber
SELECT * FROM(
SELECT t2.Year as [Year],
(SELECT
SUM(CustomerCount)
FROM #tempTableCustomerCount
WHERE Year>=t1.Year
AND Year <=t2.Year) AS CustomerCount
FROM #tempTableCustomerCount t1 JOIN #tempTableCustomerCount t2
ON t1.Year < t2.Year
WHERE t1.Year = #yearStart
UNION
SELECT [Year], [CustomerCount]
FROM #tempTableCustomerCount
WHERE [YEAR] = #yearStart
) tt
ORDER BY tt.Year
It isn't efficient but at the end the temp table you are dealing with is so small I don't think it really matters, and adds a lot more versatility versus the method you are using.
Update: I updated the query to reflect the result you wanted with my data set, I was basically testing to see if this was faster, it was faster by 10 seconds but the dataset I am dealing with is relatively small. (from 12 seconds to 2 seconds).
Using your data
I changed the tables you gave to temp tables so it didn't effect my environment and I removed the foreign key because they are not supported for temp tables, the logic is the same as the example included but just changed for your dataset.
DECLARE #startYear INT = 2013, #endYear INT = 2016
DECLARE #yearStartMonth INT = 10 , #yearStartDay INT = 1
DECLARE #startDate DATETIME = DATEADD(day,#yearStartDay-1,
DATEADD(month, #yearStartMonth-1,
DATEADD(year,#startYear-1900,0)))
DECLARE #endDate DATETIME = DATEADD(day,#yearStartDay-1,
DATEADD(month,#yearStartMonth-1,
DATEADD(year,#endYear-1899,0)))
DECLARE #tempDateRangeTable TABLE
(
[Year] INT NOT NULL,
StartDate DATETIME NOT NULL,
EndDate DATETIME NOT NULL
)
DECLARE #currentDate DATETIME = #startDate
WHILE #currentDate < #endDate
BEGIN
DECLARE #nextDate DATETIME = DATEADD(YEAR, 1, #currentDate)
INSERT INTO #tempDateRangeTable(Year,StartDate,EndDate)
VALUES(YEAR(#currentDate),#currentDate,#nextDate)
SET #currentDate = #nextDate
END
CREATE TABLE Users
(
uID int identity primary key,
uFirstName varchar(75),
uLastName varchar(75)
);
INSERT INTO Users (uFirstName, uLastName)
VALUES
('User1', 'User1'),
('User2', 'User2'),
('User3', 'User3'),
('User4', 'User4');
CREATE TABLE UserDataIDMatch
(
udimID int indentity primary key,
udim.udim_FK_uID int foreign key references Users(uID),
udimUserSystemID varchar(75)
);
INSERT INTO UserDataIDMatch (udim_FK_uID, udimUserSystemID)
VALUES
(1, 'SystemID1'),
(2, 'SystemID2'),
(3, 'SystemID3'),
(4, 'SystemID4');
CREATE TABLE DataDump
(
ddID int identity primary key,
ddSystemID varchar(75),
ddEnd datetime
);
INSERT INTO DataDump (ddSystemID, ddEnd)
VALUES
('SystemID1', '10-01-2013'),
('SystemID2', '10-01-2014'),
('SystemID3', '10-01-2015'),
('SystemID4', '10-01-2016');
DECLARE #tempIndividCount TABLE
(
[Year] INT NOT NULL,
UserCount INT NOT NULL
)
-- no longer need to filter out other because you are using an
--inclusion statement rather than an exclusion one, this will
--also make your query faster (when using real tables not temp ones)
INSERT INTO #tempIndividCount(Year,UserCount)
SELECT tdr.Year, COUNT(DISTINCT UId) FROM
Users u JOIN UserDataIDMatch um
ON um.udim_FK_uID = u.uID
JOIN DataDump dd ON
um.udimUserSystemID = dd.ddSystemID
JOIN #tempDateRangeTable tdr ON
dd.ddEnd >= tdr.StartDate AND dd.ddEnd < tdr.EndDate
GROUP BY tdr.Year
-- will show you your result
SELECT * FROM #tempIndividCount
--add any ranges that did not have an entry but were in your range
--can easily remove this by taking this part out.
INSERT INTO #tempIndividCount
SELECT t1.Year,0 FROM
#tempDateRangeTable t1 LEFT OUTER JOIN #tempIndividCount t2
ON t1.Year = t2.Year
WHERE t2.Year IS NULL
SELECT YearNumber,UserCount FROM (
SELECT 'Year'+CAST(((t2.Year-t1.Year)+1) AS CHAR) [YearNumber] ,t2.Year,(
SELECT SUM(UserCount)
FROM #tempIndividCount
WHERE Year >= t1.Year AND Year <=t2.Year
) AS UserCount
FROM #tempIndividCount t1
JOIN #tempIndividCount t2
ON t1.Year < t2.Year
WHERE t1.Year = #startYear
UNION ALL
--add the missing first year, union it to include the value
SELECT 'Year1',Year, UserCount FROM #tempIndividCount
WHERE Year = #startYear) tt
ORDER BY tt.Year
Benefits over using a WHEN CASE based approach
More Robust
Do not need to explicitly determine the end and start dates of each year, just like in a logical year just need to know the start and end date. Can easily change what you are looking for with some simple modifications(i.e. say you want all 2 year ranges or 3 year).
Will be faster if the database is indexed properly
Since you are searching based on the same data type you can utilize the indices that should be created on the date columns in the database.
Cons
More Complicated
The query is a lot more complicated to follow, even though it is more robust there is a lot of extra logic in the actual query.
In some circumstance will not provide good boost to execution time
If the dataset is very small, or the number of dates being compared isn't significant then this could not save enough time to be worth it.
In SQL Server once you match a WHEN inside a CASE, it stop evaluating will not going on evaluating next WHEN clauses. Hence you can't accumulate that way.
if I understand you correctly, this would show your results.
;WITH cte AS
(F
SELECT dd.ddEnd [dateEnd], u.uID AS UserID
FROM Users AS u
INNER JOIN UserDataIDMatch AS udim
ON u.uID = udim.udim_FK_uID
INNER JOIN DataDump AS dd
ON udim.udimUserSystemID = dd.ddSystemID
WHERE ddEnd BETWEEN #FiscalYearStart AND #FiscalYearEnd3
)
SELECT datepart(year, #FiscalYearStart) AS [Year], COUNT(DISTINCT UserID) AS CntUserID
FROM cte
WHERE dateEnd BETWEEN #FiscalYearStart AND #FiscalYearEnd1
GROUP BY #FiscalYearStart
UNION
SELECT datepart(year, #FiscalYearEnd1) AS [Year], COUNT(DISTINCT UserID) AS CntUserID
FROM cte
WHERE dateEnd BETWEEN #FiscalYearStart AND #FiscalYearEnd2
GROUP BY #FiscalYearEnd1
UNION
SELECT datepart(year, #FiscalYearEnd3) AS [Year], COUNT(DISTINCT UserID) AS CntUserID
FROM cte
WHERE dateEnd BETWEEN #FiscalYearStart AND #FiscalYearEnd3
GROUP BY #FiscalYearEnd2
Following is the sample data. I need to make 3 copies of this data in t sql without using loop and return as one resultset. This is sample data not real.
42 South Yorkshire
43 Lancashire
44 Norfolk
Edit: I need multiple copies and I have no idea in advance that how many copies I need I have to decide this on the basis of dates. Date might be 1st jan to 3rd Jan OR 1st jan to 8th Jan.
Thanks.
Don't know about better but this is definatley more creative! you can use a CROSS JOIN.
EDIT: put some code in to generate a date range, you can change the date range, the rows in the #date are your multiplier.
declare #startdate datetime
, #enddate datetime
create table #data1 ([id] int , [name] nvarchar(100))
create table #dates ([date] datetime)
INSERT #data1 SELECT 42, 'South Yorkshire'
INSERT #data1 SELECT 43, 'Lancashire'
INSERT #data1 SELECT 44, 'Norfolk'
set #startdate = '1Jan2010'
set #enddate = '3Jan2010'
WHILE (#startdate <= #enddate)
BEGIN
INSERT #dates SELECT #startdate
set #startdate=#startdate+1
END
SELECT [id] , [name] from #data1 cross join #dates
drop table #data1
drop table #dates
You could always use a CTE to do the dirty work
Replace the WHERE Counter < 4 with the amount of duplicates you need.
CREATE TABLE City (ID INTEGER PRIMARY KEY, Name VARCHAR(32))
INSERT INTO City VALUES (42, 'South Yorkshire')
INSERT INTO City VALUES (43, 'Lancashire')
INSERT INTO City VALUES (44, 'Norfolk')
/*
The CTE duplicates every row from CTE for the amount
specified by Counter
*/
;WITH CityCTE (ID, Name, Counter) AS
(
SELECT c.ID, c.Name, 0 AS Counter
FROM City c
UNION ALL
SELECT c.ID, c.Name, Counter + 1
FROM City c
INNER JOIN CityCTE cte ON cte.ID = c.ID
WHERE Counter < 4
)
SELECT ID, Name
FROM CityCTE
ORDER BY 1, 2
DROP TABLE City
This may not be the most efficient way of doing it, but it should work.
(select ....)
union all
(select ....)
union all
(select ....)
Assume the table is named CountyPopulation:
SELECT * FROM CountyPopulation
UNION ALL
SELECT * FROM CountyPopulation
UNION ALL
SELECT * FROM CountyPopulation
Share and enjoy.
There is no need to use a cursor. The set-based approach would be to use a Calendar table. So first we make our calendar table which need only be done once and be somewhat permanent:
Create Table dbo.Calendar ( Date datetime not null Primary Key Clustered )
GO
; With Numbers As
(
Select ROW_NUMBER() OVER( ORDER BY S1.object_id ) As [Counter]
From sys.columns As s1
Cross Join sys.columns As s2
)
Insert dbo.Calendar([Date])
Select DateAdd(d, [Counter], '19000101')
From Numbers
Where [Counter] <= 100000
GO
I populated it with a 100K dates which goes into 2300. Obviously you can always expand it. Next we generate our test data:
Create Table dbo.Data(Id int not null, [Name] nvarchar(20) not null)
GO
Insert dbo.Data(Id, [Name]) Values(42,'South Yorkshire')
Insert dbo.Data(Id, [Name]) Values(43, 'Lancashire')
Insert dbo.Data(Id, [Name]) Values(44, 'Norfolk')
GO
Now the problem becomes trivial:
Declare #Start datetime
Declare #End datetime
Set #Start = '2010-01-01'
Set #End = '2010-01-03'
Select Dates.[Date], Id, [Name]
From dbo.Data
Cross Join (
Select [Date]
From dbo.Calendar
Where [Date] >= #Start
And [Date] <= #End
) As Dates
By far the best solution is CROSS JOIN. Most natural.
See my answer here: How to retrieve rows multiple times in SQL Server?
If you have a Numbers table lying around, it's even easier. You can DATEDIFF the dates to give you the filter on the Numbers table