Iterative Union ALL's - sql

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.

Related

Duplicate SQL table to creat large dummy table

I have an SQL table already, the format of which I just want duplicating for testing purposes. I tried the following:
DECLARE #Counter AS INT(1)
WHILE ( #Counter <= 10)
BEGIN
CREATE TABLE my_new_big_table
AS (SELECT * FROM my_table)
SET #Counter = #Counter +1
END;
However I encounter an error with 'DECLARE'
ParseException:
[PARSE_SYNTAX_ERROR] Syntax error at or near 'DECLARE'(line 1, pos 0)
Is this the best approach to duplicate an existing table?
Your approach seems wrong. With a CREATE TABLE statement in the loop, you'd try to create the same table ten times.
Here is a solution in pure SQL that I hope will work for you. I take the original table and cross join it with 10 generated rows, so as to get the original rows tenfold.
CREATE TABLE my_new_big_table AS
SELECT t.*
FROM my_table t
CROSS JOIN VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10) v(i);

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.

How would I set the value of a variable to the results of a query in SQL?

I have a stored procedure that has two variables. They are #DateLeft and #DateRight. #DateLeft is the variable that I would like to set equal to the results of a query.#DateRight = GETDATE(). I have the following data:
EDIT 2: Added 'Distance' column
Date Code Distance **The values in the distance column are arbitrary and they are there for example's sake
11/23/2015 A 456
12/28/2015 B 2163
1/25/2016 C 203
4/30/2015 D 8921
My stored procedure is supposed to use #DateLeft and #DateRight to determine a date range that controls another calculation. These dates are the most recent dates that a particular event occurred. I am supposed to use the dates in the Date column as the #DateLeft value for each different code in the Code column. For example, the procedure should run and get 11/23/2015 as the value for #DateLeft for code A. It will then do its calculation and move on and get 12/28/2015 for code B, and so on. I don't know how to perform this calculation so that it doesn't have to run the whole procedure again to do its calculations with a different date for #DateLeft. My query for the end result looks something like this:
SELECT SUM(TraveledDistance) AS Distance, Code
FROM MyTable
WHERE
Time BETWEEN #DateLeft AND #DateRight
I would like to do something like this maybe (I'm open to suggestions, of course):
SET #DateLeft = Select Date From SampleTable
Group By Code --I know this doesn't work
Any help is appreciated.
EDIT: Description of procedure itself
The procedure takes the #DateLeft value (which, as it stands, is not a parameter) and uses it as a filter to calculate the distance traveled between that date and today's date (#DateRight). You can make up the calculation part if you want. I'm more concerned with the approach that I should take to set #DateLeft to each of the different dates for each of the codes, without having to repeat the procedure several times.
DECLARE #DateLeft DATE;
SELECT #DateLeft = [Date] From SampleTable Group By Code
You can set the variable value using this syntax.
Update based on OP's update:
The OP's updated request is not abundantly clear, but I believe a loop may do the trick.
DECLARE #DateLeft DATE;
DECLARE #Level INT = 0;
WHILE #Level < (SELECT COUNT(*) FROM SampleTable)
BEGIN
SELECT #DateLeft = [Date] From SampleTable Group By Code;
--do something with #DateLeft
SET #Level = #Level + 1;
END
If you need to do it like a loop you might need to look at SQL Server Cursor.
If you don't need a loop you can always select value to a variable like this:
declare #DateLeft as datetime;
SELECT #DateLeft = Date
From SampleTable
Group By Code;
Perhaps something like this is what you're looking for?:
select
(
/* calculation goes here */
select sum(m.TraveledDistance) as Distance
from MyTable m
/* correlate the subquery on t.Code and max t.Date */
where m.Code = t.Code and m.Time between max(t.Date) and getdate()
) as Calculation,
t.Code
from SampleTable t
group by t.Code
Your approach works as follows: SET #DateLeft = (Select Date From Sample Table Group By Code)

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.

I am trying to run a query based on the results from a stored procedure

First, here is the code for sp_GetWorkQByUserName:
ALTER PROCEDURE [dbo].[sp_GetWorkQByUserName]
( #UserName varchar(50),
#StartDate datetime,
#EndDate datetime )
AS
BEGIN
SET NOCOUNT ON;
SELECT DISTINCT SpotId FROM tblSpotCount WHERE StoreNum = EXECUTE sp_GetUserLocationCodes(#UserName)
ORDER BY SpotDt ASC
END
I know my SELECT DISTINCT statement is wrong, but I wrote it like that to help show what I'm trying to do. I want to run this stored procedure based on the results from the sp_GetUserLocationCodes with a parameter of #UserName.
From what I can tell, my problem lies in how I'm calling sp_GetUserLocationCodes.
Question: how can I run a SELECT DISTINCT query on tblSpotCount.SpotId based on the results from the sp_GetUserLocationCodes stored procedure?
You cannot use a stored procedure directly in a query. You can, however, insert the results of a stored procedure into a temporary table and use that in your query:
CREATE TABLE #storeLocations
(
-- appropriate column names and data types go here
)
INSERT INTO #storeLocations (put column list here)
EXECUTE sp_GetUserLocationCodes(#UserName)
SELECT DISTINCT SpotId
FROM tblSpotCount
WHERE EXISTS (SELECT 1
FROM #storeLocations
WHERE #storeLocations.StoreNum = tblSpotCount.StoreNum)
ORDER BY SpotDt ASC
DROP TABLE #storeLocations