SQL Server 2000 (creating all dates given a daterange) - sql

I was wondering if there is a way in SQL Server 2000 to create all dates given a start and end date as a result. I know I can achieve this with T-SQL looping. I am looking for a non looping solution. Also in 2005 you can use the recursive with clause. The solution can also be using a T table that has numbers in it to join with the table. Again I am looking at a SQL Server 2000 non looping/using T tables solution. Is there any?

SELECT
DATEADD(dy, T.number, #start_date)
FROM
T
WHERE
number BETWEEN 0 AND DATEDIFF(dy, #start_date, #end_date)
A Calendar table can also be useful for these kind of queries and you can add some date-specific information to it, such as whether a day is a holiday, counts as a "business" day, etc.

try this:
create numbers table, only need to do this one time in your DB:
CREATE TABLE Numbers (Number int NOT NULL)
GO
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
GO
DECLARE #x int
set #x=0
WHILE #X<8000
BEGIN
SET #X=#X+1
INSERT INTO Numbers VALUES (#x)
END
--run your query:
DECLARE #StartDate datetime
DECLARE #EndDate datetime
set #StartDate='05/03/2009'
set #EndDate='05/12/2009'
SELECT
#StartDate+Number-1
FROM Numbers
WHERE Number<=DATEDIFF(day, #StartDate, #EndDate)+1

Related

Optimize delete SQL query with unordered table

I am attempting a mass delete of old data from a huge table with 80,000,000 rows, about 50,000,000 rows will be removed. This will be done in batches of 50k to avoid database log overflow. Also the rows of the table are not sorted chronologically. I've come up with the following script:
BEGIN
DECLARE #START_TIME DATETIME,
#END_TIME DATETIME,
#DELETE_COUNT NUMERIC(10,0),
#TOTAL_COUNT NUMERIC(10,0),
#TO_DATE DATETIME,
#FROM_DATE DATETIME,
#TABLE_SIZE INT
SELECT #START_TIME = GETDATE()
PRINT 'Delete script Execution START TIME = %1!', #START_TIME
SELECT #TABLE_SIZE = COUNT(*) FROM HUGE_TABLE
PRINT 'Number of rows in HUGE_TABLE = %1!', #TABLE_SIZE
SELECT #DELETE_COUNT = 1,
#TOTAL_COUNT = 0,
#TO_DATE = DATEADD(yy, -2, GETDATE())
CREATE TABLE #TMP_BATCH_FOR_DEL (REQUEST_DT DATETIME)
WHILE(#DELETE_COUNT > 0)
BEGIN
DELETE FROM #TMP_BATCH_FOR_DEL
INSERT INTO #TMP_BATCH_FOR_DEL (REQUEST_DT)
SELECT TOP 50000 REQUEST_DT
FROM HUGE_TABLE
WHERE REQUEST_DT < #TO_DATE
ORDER BY REQUEST_DT DESC
SELECT #FROM_DATE = MIN(REQUEST_DT), #TO_DATE = MAX(REQUEST_DT)
FROM #TMP_BATCH_FOR_DEL
PRINT 'Deleting data from %1! to %2!', #FROM_DATE, #TO_DATE
DELETE FROM HUGE_TABLE
WHERE REQUEST_DT BETWEEN #FROM_DATE AND #TO_DATE
SELECT #DELETE_COUNT = ##ROWCOUNT
SELECT #TOTAL_COUNT = #TOTAL_COUNT + #DELETE_COUNT
SELECT #TO_DATE = #FROM_DATE
COMMIT
CHECKPOINT
END
SELECT #END_TIME = GETDATE()
PRINT 'Delete script Execution END TIME = %1!', #END_TIME
PRINT 'Total Rows deleted = %1!', #TOTAL_COUNT
DROP TABLE #TMP_BATCH_FOR_DEL
END
GO
I did a practice run and found the above was deleting around 2,250,000 rows per hour. So, it would take 24+ hours of continuous runtime to delete my data.
I know it's that darn ORDER BY clause within the loop that's slowing things down, but storing the ordered table in another temp table would take up too much memory. But, I can't think of a better way to do this.
Thoughts?
It is probably not the query itself. Your code is deleting about 600+ records per second. A lot is going on in that time -- logging, locking, and so on.
A faster approach is to load the data you want into a new table, truncate the old table, and reload it:
select *
into temp_huge_table
from huge_table
where request_dt > ?; -- whatever the cutoff is
Then -- after validating the results -- truncate the huge table and reload the data:
truncate table huge_table;
insert into huge_table
select *
from temp_huge_table;
If there is an identity column you will want to disable that to allow identity insert. You might have to take other precautions if there are triggers that set values in the table. Or if there are foreign key references to rows in the table.
I would not suggest doing this directly. After you have truncated the table, you should probably partition by the table by date -- by day, week, month, whatever.
Then, in the future, you can simply drop partitions rather than deleting rows. Dropping partitions is much, much faster.
Note that loading a few tens of millions of rows into an empty table is much, much faster than deleting them, but it still takes time (you can test how much time on your system). This requires downtown for the table. However, you hopefully have a maintenance period where this is possible.
And, the downtime can be justified by partitioning the table so you won't have this issue in the future.
Maybe you can optimize your Query by Inserting the 30.000.000 Records you want to keep into antoher Table which will be your new "Huge Table". And Drop the whole old "Huge Table" all together.
Best Regards
LK

multiple where clause dates

I pass two date range to my procedure then I select last date of each and every month as below
create procedure stcheck
#From_Date date,
#To_Date date
as
begin
declare #tmpTable table (dates date)
declare #startDate date = #From_Date
declare #endDate date = #To_Date
while #startDate <= #endDate
begin
insert into #tmpTable (dates) values (#startDate)
set #startDate = DATEADD(DAY, 1, #startDate)
end
select max(dates) as [Last day] from #tmpTable as o
group by datepart(YEAR, dates), datepart(MONTH, dates)
If am getting result as follows
Last day
2017-01-31
2017-02-28
2017-03-31
2017-04-30
I need to pass some data to temporary table by using these date output
Ex :-
Insert #xyz
Select * from abcd where postdate <=’2017-01-31’
And my requirement is that these all dates should be automatically used and relevant data should pass to #table.
Hi expert . thank for your early reply.
In brief my requirement is as follows. Please help because I am new with sql.
Think,I have a table as ‘stock’ with in and out stock transaction and stock
report can be taken as follows to any as at date
SELECT item ,SUM(InQty-OutQty) FROM stock
WHERE TransDate <= #AsatDate
GROUP BY item
Having SUM(InQty-OutQty) > 0
If I need to get the each and every month end stock when I am passing the date range
from #From date= ‘2017/05/01’ to #AsatDate=‘2017/09/30’ .
The stock report result should be appear something like this.
STOCK REPORT EXAMPLE
Please help what can be done?
NB:- it should be able to pass any date range when it is required.
Your question is a little unclear, but I'm assuming you want to run the stored procedure and insert data into the temp table #xyz (assumes temp table #xyz already exists):
insert into #xyz
exec stcheck #From_Date, #To_Date
Now off on a little tangent:
I'd also look at table-valued functions if you need parameterized, deterministic tabular output like this. I agree with #John that you should avoid looping if possible. One possible way to achieve this is instead of iterating day by day you use a pre-populated calendar table (stick say 50-100 years worth of future calendar dates in a table and use this in calculations - there are tons of examples of how to do this online). Another method is to use a recursive CTE or a numbers table, both outlined in answers here.
Once your calendar dates are easily queryable, you can stick all this in a view or inline table-valued function and it will be super fast and much clearer. If you regularly use "last day of month" as a filter criteria, you could even pre-calculate these as a column in your calendar table. Here's what it could end up like:
insert into #xyz
select dbo.DatesBetween(#From_Date, #To_Date)
You could even use dbo.DatesBetween(#From_Date, #To_Date) directly in your query in place of the temp table, if it's only used once.

Iterative Union ALL's

I have a large SQL Server 2012 Database which I am querying 3 tables to create a result set of 5 fields.
I want to repeat this query in a WHILE - loop and "UNION ALL" the result sets obtained in each loop. This iteration will be on a variable: #this_date which will increment over the past 6 years and stop at today's date.
At each iteration a different results set will be obtained by the SELECT.
So I am trying to code the Stored Procedure as follows:
Declare #the_date as Date,
#to_date as Date
-- I setup the above dates, #the_date being 6 years behind #to_date
-- Want to loop for each day over the 6-year period
WHILE (#the_date <= #to_date)
BEGIN
-- the basic select query looks like this
Select Table1.Field-1, Table2.Field-2 ...
FROM Table1
Inner Join Table2 ...
On ( ..etc.. )
-- the JOIN conditions are based on table.attributes which are compared with
-- #the_date to get a different result set each time
-- now move the date up by 1
DateAdd(Day, +1, #the_date)
-- want to concatenate the result sets
UNION ALL
END
The above gives me a syntax error:
Incorrect syntax near the keyword 'Union'.
Any ideas on a solution to my problem would be welcome
- thanks.
Don't use a UNION. You can't in a loop anyway. Instead store the results of each iteration in a temp table or a table variable and select from the temp table / table variable instead.
DECLARE #the_date as Date,
#to_date as Date
CREATE TABLE #t (Col1 VARCHAR(100))
WHILE (#the_date <= #to_date)
BEGIN
INSERT #t (Col1) SELECT ... etc
DateAdd(Day, +1, #the_date)
END
SELECT Col1 FROM #t
That said, if you provide some sample data and expected results we might be able to help you with a more efficient set-based solution. You should avoid iterative looping in RDBMS whenever possible.

Avoiding while loops in SQL when a counter is required

I feel like this is a common problem, but it seems that none of the answers that I have found on SO or other sites seem to address the issue of a while loop with a counter.
Let's say that I am trying to write a stored procedure in SQL that will populate a user's timesheet by inserting a row for each day for the remainder of the month. If the #endMonth variable holds the last day of the month, then I know that I could easily write a while loop and do something along these lines:
WHILE #date <= #endMonth
BEGIN
//Do some action with the date, like an insert
SET #date = DATEADD(d, 1, #date) //increment the date by one day
END
However, looking at answers here and on other sites leads me to believe that it would be best to avoid using a while loop if at all possible.
So my question is this: is there a way I can implement a loop with a counter in SQL without using the WHILE structure? What technique would I use to go about converting a loop similar to the one I posted? Or with something like this, do I have to bite the bullet and just use a while loop?
As an aside, some of the following questions come close, but none of them seem to quite address the issue of needing a counter as a loop condition. Most of the answers seem to condemn using WHILE loops, but I can't seem to find a general purpose solution as to an alternative.
sql while loop with date counter
SQL Server 2008 Insert with WHILE LOOP (this one was close, but unfortunately for me it only works with an auto increment column)
I saw many examples of populating data.
First you create dates from starting to ending dates in cte and then you can insert it into table.
One of them is with cte:
DECLARE #StartDate DateTime = '2014-06-01'
DECLARE #EndDate DateTime = '2014-06-29'
;WITH populateDates (dates) AS (
SELECT #StartDate as dates
UNION ALL
SELECT DATEADD(d, 1, dates)
FROM populateDates
WHERE DATEADD(d, 1, dates)<=#EndDate
)
SELECT *
INTO dbo.SomeTable
FROM populateDates
You should try to look for on internet how to populate date in sql table
As a general case, you can increment values without using cursors by assigning values and incrementing the variable in the same select, like this:
DECLARE #i INT = 0
DECLARE #table TABLE
(
ID INT ,
testfield VARCHAR(5)
)
INSERT INTO #table
( testfield )
VALUES ( 'abcd'),
( 'efgh' ),
( 'ijkl' ),
( 'mnop' )
UPDATE #table
SET #I = ID = #i + 1
SELECT *
FROM #table
I used a sequence - create temporarily.
I needed to do my updates outside of script context, with plain SQL, sequence was the only "counter" I could come up with.

Using while loop in T-SQL function

Non-database programmer here. It happens so, that I need to create a function in T-SQL which returns workdays count between given dates. I believe that the easiest how it's done is with while loop. Problem is, that as soon as I write something like
while #date < #endDate
begin
end
the statement won't execute, claiming "incorrect syntax near the keyword 'return'" (not very helpful). Where's the problem?
P.S. Full code:
ALTER FUNCTION [dbo].[GetNormalWorkdaysCount] (
#startDate DATETIME,
#endDate DATETIME
)
RETURNS INT
AS
BEGIN
declare #Count INT,
#CurrDate DATETIME
set #CurrDate = #startDate
while (#CurrDate < #endDate)
begin
end
return #Count
END
GO
Unlike some languages, the BEGIN/END pair in SQL Server cannot be empty - they must contain at least one statement.
As to your actual problem - you've said you're not a DB programmer. Most beginners to SQL tend to go down the same route - trying to write procedural code to solve the problem.
Whereas, SQL is a set-based language - it's usually better to find a set-based solution, rather than using loops.
In this instance, a calendar table would be a real help. Such a table contains one row for each date, and additional columns indicating useful information for your business (e.g. what you consider to be a working day). It then makes your query for working days look like:
SELECT COUNT(*) from Calendar
where BaseDate >= #StartDate and BaseDate < #EndDate and IsWorkingDay = 1
Populating the Calendar table becomes a one off exercise, and you can populate it with e.g. 30 years worth of dates easily.
Using any loop within SQL server is never a good idea :)
There are few better solutions, referring to one presented on StackOverflow already.