SQL Loop to build Case based on Variables - sql

I am trying to build a case query based on variables
The idea being is when the variables are populated the case statement would alter accordingly.
My Current query takes Values from a table and groups them together into a sort of Bucket.
This works fine providing its always going to be the set ranges and number of ranges, I want to make this configurable by passing variables
From my original query all i wanted was to configure the Number of Buckets and the value of From and Two for each bucket i.e. +5 or +10
Here is my original query:
SELECT subq.Bucket, COUNT(*) 'Count'
FROM
(
SELECT
CASE
WHEN R.Value < 10 THEN '0-10'
WHEN R.Value Between 10 and 20 THEN '10-20'
WHEN R.Value Between 20 and 30 THEN '20-30'
WHEN R.Value Between 30 and 40 THEN '30-40'
WHEN R.Value > 40 THEN '40+'
END Bucket
FROM Table R
Where DateTime Between '2022-10-01' and '2022-11-10' and Type = 1
) subq
GROUP BY subq.Bucket
This is what i was trying to achomplish if it makes any sense in the realm of SQL
DECLARE #NoRows Int, #Range Int, #Count Int, #StartRange Int
Set #NoRows = 5
Set #StartRange = 0
Set #Range = 10
Set #Count = 0
SELECT subq.Bucket, COUNT(*) 'Count'
FROM
(
WHILE #NoRows <= #Count
BEGIN
SELECT
(
CASE
WHEN R.Value Between #StartRange and #Range THEN '#StartRange-#Range'
SET #Count = #Count + 1
SET #StartRange = #StartRange + #Range
END
WHEN R.Value > #StartRange THEN '#StartRange'
END Bucket
FROM Table R
Where DateTime Between '2022-10-01' and '2022-11-10' and Type = 1
) subq
GROUP BY subq.Bucket

This is untested, due to no sample data, but this should be enough to get you to where you need to be. I use an inline tally here to generate the data, but you could also use a tally function, or even build you own bucket function:
DECLARE #NoRows int = 5,
#Range int = 10,
#StartRange int = 0;
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP(#NoRows)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2), --UP to 100 rows, add more cross joins for more rows
Buckets AS(
SELECT #StartRange + ((I-1)*#Range) AS RangeStart,
#StartRange + ((I)*#Range) AS RangeEnd
FROM Tally)
SELECT YT.{Needed Columns},
CASE WHEN B.RangeStart IS NULL THEN CONCAT(#NoRows * #Range,'+')
ELSE CONCAT(B.RangeStart,'-', B.RangeEnd-1)
END AS Bucket
FROM dbo.YourTable YT
LEFT JOIN Buckets B ON YT.YourColumn >= B.RangeStart
AND YT.YourColumn < B.RangeEnd;
In SQL Server 2022+, you even have the built in function GENERATE_SERIES, which makes this even easier.

Related

Split a range of numbers in equal parts SQL Sever

I have following query to split the range of numbers in parts. For ex: i have range of 200 numbers and i have to group the numbers to 95 But Result is coming in reverse order.I Have attached expected result
declare #MinValue bigint = 1
declare #MaxValue bigint = 200;
declare #RowsPerGroup bigint =95
declare #RowsPerGroup1 bigint =(#RowsPerGroup-1)
;with src(val,rm) as (
select #MaxValue, (#MaxValue - #RowsPerGroup1) union all
select rm-1, case when rm-1 > #MinValue + #RowsPerGroup1 then rm-1 - #RowsPerGroup1 else #MinValue end from src where rm-1 >= #MinValue
)
select rm as 'Start', val as 'End',[Difference]=(val-rm)+1 from src order by rm asc
option(maxrecursion 0)
Current Result:
Start End Difference
1 10 10
11 105 95
106 200 95
Expected Result:
Pls let me know where am doing wrong
My variant:
DECLARE
#MinValue bigint = 1,
#MaxValue bigint = 200,
#RowsPerGroup bigint = 95 -- 300
;WITH cte AS(
SELECT #MinValue [Start],IIF(#RowsPerGroup>#MaxValue,#MaxValue,#RowsPerGroup) [End]
UNION ALL
SELECT [End]+1,IIF([End]+#RowsPerGroup>#MaxValue,#MaxValue,[End]+#RowsPerGroup)
FROM cte
WHERE [End]<#MaxValue
)
SELECT [Start],[End],[End]-[Start]+1 [Difference]
FROM cte
You can see the reason in the first line of the common table expression:
select #MaxValue, (#MaxValue - #RowsPerGroup1)
This inserts (200,106) intto src. The second select then counts down from existing rows. To adapt the CTE, exchange minimums with maximums (including for ranges), invert arithmetic, reverse comparisons and any other related exchanges:
select #MinValue, (#MinValue + #RowsPerGroup1) union all
select val+1, case
when val+1 < #MaxValue - #RowsPerGroup1
then val+1 + #RowsPerGroup1
else #MaxValue
end
from src
where val+1 <= #MaxValue
This particular statement can be simplified in parts:
select #MinValue, (#MinValue + #RowsPerGroup - 1) union all
select val + 1, case
when val + #RowsPerGroup < #MaxValue
then val + #RowsPerGroup
else #MaxValue
end
from src
where val < #MaxValue

SQL Server - loop through table and update based on count

I have a SQL Server database. I need to loop through a table to get the count of each value in the column 'RevID'. Each value should only be in the table a certain number of times - for example 125 times. If the count of the value is greater than 125 or less than 125, I need to update the column to ensure all values in the RevID (are over 25 different values) is within the same range of 125 (ok to be a few numbers off)
For example, the count of RevID = "A2" is = 45 and the count of RevID = 'B2' is = 165 then I need to update RevID so the 45 count increases and the 165 decreases until they are within the 125 range.
This is what I have so far:
DECLARE #i INT = 1,
#RevCnt INT = SELECT RevId, COUNT(RevId) FROM MyTable group by RevId
WHILE(#RevCnt >= 50)
BEGIN
UPDATE MyTable
SET RevID= (SELECT COUNT(RevID) FROM MyTable)
WHERE RevID < 50)
#i = #i + 1
END
I have also played around with a cursor and instead of trigger. Any idea on how to achieve this? Thanks for any input.
Okay I cam back to this because I found it interesting even though clearly there are some business rules/discussion that you and I and others are not seeing. anyway, if you want to evenly and distribute arbitrarily there are a few ways you could do it by building recursive Common Table Expressions [CTE] or by building temp tables and more. Anyway here is a way that I decided to give it a try, I did utilize 1 temp table because sql was throwing in a little inconsistency with the main logic table as a cte about every 10th time but the temp table seems to have cleared that up. Anyway, this will evenly spread RevId arbitrarily and randomly assigning any remainder (# of Records / # of RevIds) to one of the RevIds. This script also doesn't rely on having a UniqueID or anything it works dynamically over row numbers it creates..... here you go just subtract out test data etc and you have what you more than likely want. Though rebuilding the table/values would probably be easier.
--Build Some Test Data
DECLARE #Table AS TABLE (RevId VARCHAR(10))
DECLARE #C AS INT = 1
WHILE #C <= 400
BEGIN
IF #C <= 200
BEGIN
INSERT INTO #Table (RevId) VALUES ('A1')
END
IF #c <= 170
BEGIN
INSERT INTO #Table (RevId) VALUES ('B2')
END
IF #c <= 100
BEGIN
INSERT INTO #Table (RevId) VALUES ('C3')
END
IF #c <= 400
BEGIN
INSERT INTO #Table (RevId) VALUES ('D4')
END
IF #c <= 1
BEGIN
INSERT INTO #Table (RevId) VALUES ('E5')
END
SET #C = #C+ 1
END
--save starting counts of test data to temp table to compare with later
IF OBJECT_ID('tempdb..#StartingCounts') IS NOT NULL
BEGIN
DROP TABLE #StartingCounts
END
SELECT
RevId
,COUNT(*) as Occurences
INTO #StartingCounts
FROM
#Table
GROUP BY
RevId
ORDER BY
RevId
/************************ This is the main method **********************************/
--clear temp table that is the main processing logic
IF OBJECT_ID('tempdb..#RowNumsToChange') IS NOT NULL
BEGIN
DROP TABLE #RowNumsToChange
END
--figure out how many records there are and how many there should be for each RevId
;WITH cteTargetNumbers AS (
SELECT
RevId
--,COUNT(*) as RevIdCount
--,SUM(COUNT(*)) OVER (PARTITION BY 1) / COUNT(*) OVER (PARTITION BY 1) +
--CASE
--WHEN ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY NEWID()) <=
--SUM(COUNT(*)) OVER (PARTITION BY 1) % COUNT(*) OVER (PARTITION BY 1)
--THEN 1
--ELSE 0
--END as TargetNumOfRecords
,SUM(COUNT(*)) OVER (PARTITION BY 1) / COUNT(*) OVER (PARTITION BY 1) +
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY NEWID()) <=
SUM(COUNT(*)) OVER (PARTITION BY 1) % COUNT(*) OVER (PARTITION BY 1)
THEN 1
ELSE 0
END - COUNT(*) AS NumRecordsToUpdate
FROM
#Table
GROUP BY
RevId
)
, cteEndRowNumsToChange AS (
SELECT *
,SUM(CASE WHEN NumRecordsToUpdate > 1 THEN NumRecordsToUpdate ELSE 0 END)
OVER (PARTITION BY 1 ORDER BY RevId) AS ChangeEndRowNum
FROM
cteTargetNumbers
)
SELECT
*
,LAG(ChangeEndRowNum,1,0) OVER (PARTITION BY 1 ORDER BY RevId) as ChangeStartRowNum
INTO #RowNumsToChange
FROM
cteEndRowNumsToChange
;WITH cteOriginalTableRowNum AS (
SELECT
RevId
,ROW_NUMBER() OVER (PARTITION BY RevId ORDER BY (SELECT 0)) as RowNumByRevId
FROM
#Table t
)
, cteRecordsAllowedToChange AS (
SELECT
o.RevId
,o.RowNumByRevId
,ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY (SELECT 0)) as ChangeRowNum
FROM
cteOriginalTableRowNum o
INNER JOIN #RowNumsToChange t
ON o.RevId = t.RevId
AND t.NumRecordsToUpdate < 0
AND o.RowNumByRevId <= ABS(t.NumRecordsToUpdate)
)
UPDATE o
SET RevId = u.RevId
FROM
cteOriginalTableRowNum o
INNER JOIN cteRecordsAllowedToChange c
ON o.RevId = c.RevId
AND o.RowNumByRevId = c.RowNumByRevId
INNER JOIN #RowNumsToChange u
ON c.ChangeRowNum > u.ChangeStartRowNum
AND c.ChangeRowNum <= u.ChangeEndRowNum
AND u.NumRecordsToUpdate > 0
IF OBJECT_ID('tempdb..#RowNumsToChange') IS NOT NULL
BEGIN
DROP TABLE #RowNumsToChange
END
/***************************** End of Main Method *******************************/
-- Compare the results and clean up
;WITH ctePostUpdateResults AS (
SELECT
RevId
,COUNT(*) as AfterChangeOccurences
FROM
#Table
GROUP BY
RevId
)
SELECT *
FROM
#StartingCounts s
INNER JOIN ctePostUpdateResults r
ON s.RevId = r.RevId
ORDER BY
s.RevId
IF OBJECT_ID('tempdb..#StartingCounts') IS NOT NULL
BEGIN
DROP TABLE #StartingCounts
END
Since you've given no rules for how you'd like the balance to operate we're left to speculate. Here's an approach that would find the most overrepresented value and then find an underrepresented value that can take on the entire overage.
I have no idea how optimal this is and it will probably run in an infinite loop without more logic.
declare #balance int = 125;
declare #cnt_over int;
declare #cnt_under int;
declare #revID_overrepresented varchar(32);
declare #revID_underrepresented varchar(32);
declare #rowcount int = 1;
while #rowcount > 0
begin
select top 1 #revID_overrepresented = RevID, #cnt_over = count(*)
from T
group by RevID
having count(*) > #balance
order by count(*) desc
select top 1 #revID_underrepresented = RevID, #cnt_under = count(*)
from T
group by RevID
having count(*) < #balance - #cnt_over
order by count(*) desc
update top #cnt_over - #balance T
set RevId = #revID_underrepresented
where RevId = #revID_overrepresented;
set #rowcount = ##rowcount;
end
The problem is I don't even know what you mean by balance...You say it needs to be evenly represented but it seems like you want it to be 125. 125 is not "even", it is just 125.
I can't tell what you are trying to do, but I'm guessing this is not really an SQL problem. But you can use SQL to help. Here is some helpful SQL for you. You can use this in your language of choice to solve the problem.
Find the rev values and their counts:
SELECT RevID, COUNT(*)
FROM MyTable
GROUP BY MyTable
Update #X rows (with RevID of value #RevID) to a new value #NewValue
UPDATE TOP #X FROM MyTable
SET RevID = #NewValue
WHERE RevID = #RevID
Using these two queries you should be able to apply your business rules (which you never specified) in a loop or whatever to change the data.

Group data without changing query flow

For me it's hard to explait what do I want so article's name may be unclear, but I hope I can describe it with code.
I have some data with two most important value, so let it be time t and value f(t). It's stored in the table, for example
1 - 1000
2 - 1200
3 - 1100
4 - 1500
...
I want to plot a graph using it, and this graph should contain N points. If table has rows less than this N, then we just return this table. But if it hasn't, we should group this points, for example, N = Count/2, then for an example above:
1 - (1000+1200)/2 = 1100
2 - (1100+1500)/2 = 1300
...
I wrote an SQL script (it works fine for N >> Count) (MonitoringDateTime - is t, and ResultCount if f(t))
ALTER PROCEDURE [dbo].[usp_GetRequestStatisticsData]
#ResourceTypeID bigint,
#DateFrom datetime,
#DateTo datetime,
#EstimatedPointCount int
AS
BEGIN
SET NOCOUNT ON;
SET ARITHABORT ON;
declare #groupSize int;
declare #resourceCount int;
select #resourceCount = Count(*)
from ResourceType
where ID & #ResourceTypeID > 0
SELECT d.ResultCount
,MonitoringDateTime = d.GeneratedOnUtc
,ResourceType = a.ResourceTypeID,
ROW_NUMBER() OVER(ORDER BY d.GeneratedOnUtc asc) AS Row
into #t
FROM dbo.AgentData d
INNER JOIN dbo.Agent a ON a.CheckID = d.CheckID
WHERE d.EventType = 'Result' AND
a.ResourceTypeID & #ResourceTypeID > 0 AND
d.GeneratedOnUtc between #DateFrom AND #DateTo AND
d.Result = 1
select #groupSize = Count(*) / (#EstimatedPointCount * #resourceCount)
from #t
if #groupSize = 0 -- return all points
select ResourceType, MonitoringDateTime, ResultCount
from #t
else
select ResourceType, CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
from #t
where [Row] % #groupSize = 0
group by ResourceType, [Row]
order by MonitoringDateTime
END
, but it's doesn't work for N ~= Count, and spend a lot of time for inserts.
This is why I wanted to use CTE's, but it doesn't work with if else statement.
So i calculated a formula for a group number (for use it in GroupBy clause), because we have
GroupNumber = Count < N ? Row : Row*NumberOfGroups
where Count - numer of rows in the table, and NumberOfGroups = Count/EstimatedPointCount
using some trivial mathematics we get a formula
GroupNumber = Row + (Row*Count/EstimatedPointCount - Row)*MAX(Count - Count/EstimatedPointCount,0)/(Count - Count/EstimatedPointCount)
but it doesn't work because of Count aggregate function:
Column 'dbo.AgentData.ResultCount' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
My english is very bad and I know it (and i'm trying to improve it), but hope dies last, so please advice.
results of query
SELECT d.ResultCount
, MonitoringDateTime = d.GeneratedOnUtc
, ResourceType = a.ResourceTypeID
FROM dbo.AgentData d
INNER JOIN dbo.Agent a ON a.CheckID = d.CheckID
WHERE d.GeneratedOnUtc between '2015-01-28' AND '2015-01-30' AND
a.ResourceTypeID & 1376256 > 0 AND
d.EventType = 'Result' AND
d.Result = 1
https://onedrive.live.com/redir?resid=58A31FC352FC3D1A!6118&authkey=!AATDebemNJIgHoo&ithint=file%2ccsv
Here's an example using NTILE and your simple sample data at the top of your question:
declare #samples table (ID int, sample int)
insert into #samples (ID,sample) values
(1,1000),
(2,1200),
(3,1100),
(4,1500)
declare #results int
set #results = 2
;With grouped as (
select *,NTILE(#results) OVER (order by ID) as nt
from #samples
)
select nt,AVG(sample) from grouped
group by nt
Which produces:
nt
-------------------- -----------
1 1100
2 1300
If #results is changed to 4 (or any higher number) then you just get back your original result set.
Unfortunately, I don't have your full data nor can I fully understand what you're trying to do with the full stored procedure, so the above would probably need to be adapted somewhat.
I haven't tried it, but how about instead of
select ResourceType, CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
from #t
where [Row] % #groupSize = 0
group by ResourceType, [Row]
order by MonitoringDateTime
perhaps something like
select ResourceType, CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
from #t
group by ResourceType, convert(int,[Row]/#groupSize)
order by MonitoringDateTime
Maybe that points you in some new direction? by converting to int we are truncating everything after the decimal so Im hoping that will give you a better grouping? you might need to put your row-number over resource type for this to work?

Find all strings that share at least X characters, order by likeness

I'm working on a project that has the names of various drugs. Often, I will find something like Proscratinol and Proscratinol XR (extended release). I would like to find a query to pick up on all the names of this nature so I can put the 'parent' drug in a table and have these 'child' drugs reference it so when I write a query to do drug counts, I'm not double counting Proscratinol because it has an XR, CR, and whatever else version to it. I wrote the following in order to take a stab at it
;with x
as
(
select drug_name
from rx
group by drug_name
)
select distinct *
from x,x as x2
where LEFT(x2.drug_name,5) = LEFT(x.drug_name,5)
and x.drug_name !=x2.drug_name
This will give me a list of all the drugs whose names share the first five letters. Five is completely arbitrary here. What I've got so far does good enough, but I would like to order the results by descending likeness. So I would like to find their X-most characters reading from the left are the same.
e.g. Phenytoin and Phepil would be 3 (their first three letters are the same)
;with x
as
(
select drug_name
from rx
group by drug_name
)
select x.drug_name as xDrugName
,x2.drug_name as x2DrugName
,case when LEFT(x2.drug_name,6) = LEFT(x.drug_name,6)
then LEN(left(x.drug_name,6)) else '0' end
from x,x as x2
where LEFT(x2.drug_name,5) = LEFT(x.drug_name,5)
and x.drug_name !=x2.drug_name
group by x.drug_name,x2.drug_name
Instead of hard coding an int into the left function in the above query, I need that integer expression to return how many similar characters the two strings share. Any good way to do this?
This approach uses a number generator and then just tests the length of overlap:
select x.drug_name, x2.drug_name, MAX(c.seqnum) as OverlapLen
from x cross join
x x2 cross join
(select ROW_NUMBER() over (order by (select NULL)) seqnum
from INFORMATION_SCHEMA.COLUMNS c
) c
where LEFT(x.drug_name, c.seqnum) = LEFT(x2.drug_name, c.seqnum) and
len(x.drug_name) >= c.seqnum and len(x2.drug_name) >= c.seqnum
group by x.drug_name, x.drug_name
order by x.drug_name, OverlapLen desc
This assumes that information_schema.columns has enough rows for the longer drug names.
This joins x to itself and then joins in a list of numbers. The where clause is checking three conditions: (1) that the left part of each drug name is the same up to seqnum; (2) that the length of each drug name is less than or equal to seqnum.
The aggregation then takes each pair and chooses the highest value of seqnum -- this should be the longest substring match.
you want longest common sequence. here is a SQL server implementation:
select dbo.lcs(#string1, #string2), len(#string1), len(#string2)
CREATE FUNCTION [dbo].[LCS]( #s varchar(MAX), #t varchar(MAX) )
RETURNS INT AS
BEGIN
DECLARE #d varchar(MAX), #LD INT, #m INT, #n INT, #i INT, #j INT,
#s_i NCHAR(1), #t_j NCHAR(1)
SET #n = LEN(#s)
IF #n = 0 RETURN 0
SET #m = LEN(#t)
IF #m = 0 RETURN 0
SET #d = REPLICATE(CHAR(0),(#n+1)*(#m+1))
SET #i = 1
WHILE #i <= #n BEGIN
SET #s_i = SUBSTRING(#s,#i,1)
SET #j = 1
WHILE #j <= #m BEGIN
SET #t_j = SUBSTRING(#t,#j,1)
IF #s_i = #t_j
SET #d = STUFF(#d,#j*(#n+1)+#i+1,1,
NCHAR(UNICODE(
SUBSTRING(#d, (#j-1)*(#n+1)+#i-1+1, 1)
)+1))
ELSE
SET #d = STUFF(#d,#j*(#n+1)+#i+1,1,CHAR(dbo.Max2(
UNICODE(SUBSTRING(#d,#j*(#n+1)+#i-1+1,1)),
UNICODE(SUBSTRING(#d,(#j-1)*(#n+1)+#i+1,1)))))
SET #j = #j+1
END
SET #i = #i+1
END
SET #LD = UNICODE(SUBSTRING(#d,#n*(#m+1)+#m+1,1))
RETURN #LD
END

SQL Server FOR EACH Loop

I have the following SQL query:
DECLARE #MyVar datetime = '1/1/2010'
SELECT #MyVar
This naturally returns '1/1/2010'.
What I want to do is have a list of dates, say:
1/1/2010
2/1/2010
3/1/2010
4/1/2010
5/1/2010
Then i want to FOR EACH through the numbers and run the SQL Query.
Something like (pseudocode):
List = 1/1/2010,2/1/2010,3/1/2010,4/1/2010,5/1/2010
For each x in List
do
DECLARE #MyVar datetime = x
SELECT #MyVar
So this would return:-
1/1/2010
2/1/2010
3/1/2010
4/1/2010
5/1/2010
I want this to return the data as one resultset, not multiple resultsets, so I may need to use some kind of union at the end of the query, so each iteration of the loop unions onto the next.
edit
I have a large query that accepts a 'to date' parameter, I need to run it 24 times, each time with a specific to date which I need to be able to supply (these dates are going to be dynamic) I want to avoid repeating my query 24 times with union alls joining them as if I need to come back and add additional columns it would be very time consuming.
SQL is primarily a set-orientated language - it's generally a bad idea to use a loop in it.
In this case, a similar result could be achieved using a recursive CTE:
with cte as
(select 1 i union all
select i+1 i from cte where i < 5)
select dateadd(d, i-1, '2010-01-01') from cte
Here is an option with a table variable:
DECLARE #MyVar TABLE(Val DATETIME)
DECLARE #I INT, #StartDate DATETIME
SET #I = 1
SET #StartDate = '20100101'
WHILE #I <= 5
BEGIN
INSERT INTO #MyVar(Val)
VALUES(#StartDate)
SET #StartDate = DATEADD(DAY,1,#StartDate)
SET #I = #I + 1
END
SELECT *
FROM #MyVar
You can do the same with a temp table:
CREATE TABLE #MyVar(Val DATETIME)
DECLARE #I INT, #StartDate DATETIME
SET #I = 1
SET #StartDate = '20100101'
WHILE #I <= 5
BEGIN
INSERT INTO #MyVar(Val)
VALUES(#StartDate)
SET #StartDate = DATEADD(DAY,1,#StartDate)
SET #I = #I + 1
END
SELECT *
FROM #MyVar
You should tell us what is your main goal, as was said by #JohnFx, this could probably be done another (more efficient) way.
You could use a variable table, like this:
declare #num int
set #num = 1
declare #results table ( val int )
while (#num < 6)
begin
insert into #results ( val ) values ( #num )
set #num = #num + 1
end
select val from #results
This kind of depends on what you want to do with the results. If you're just after the numbers, a set-based option would be a numbers table - which comes in handy for all sorts of things.
For MSSQL 2005+, you can use a recursive CTE to generate a numbers table inline:
;WITH Numbers (N) AS (
SELECT 1 UNION ALL
SELECT 1 + N FROM Numbers WHERE N < 500
)
SELECT N FROM Numbers
OPTION (MAXRECURSION 500)
declare #counter as int
set #counter = 0
declare #date as varchar(50)
set #date = cast(1+#counter as varchar)+'/01/2013'
while(#counter < 12)
begin
select cast(1+#counter as varchar)+'/01/2013' as date
set #counter = #counter + 1
end
Off course an old question. But I have a simple solution where no need of Looping, CTE, Table variables etc.
DECLARE #MyVar datetime = '1/1/2010'
SELECT #MyVar
SELECT DATEADD (DD,NUMBER,#MyVar)
FROM master.dbo.spt_values
WHERE TYPE='P' AND NUMBER BETWEEN 0 AND 4
ORDER BY NUMBER
Note : spt_values is a Mircrosoft's undocumented table. It has numbers for every type. Its not suggestible to use as it can be removed in any new versions of sql server without prior information, since it is undocumented. But we can use it as quick workaround in some scenario's like above.
[CREATE PROCEDURE [rat].[GetYear]
AS
BEGIN
-- variable for storing start date
Declare #StartYear as int
-- Variable for the End date
Declare #EndYear as int
-- Setting the value in strat Date
select #StartYear = Value from rat.Configuration where Name = 'REPORT_START_YEAR';
-- Setting the End date
select #EndYear = Value from rat.Configuration where Name = 'REPORT_END_YEAR';
-- Creating Tem table
with [Years] as
(
--Selecting the Year
select #StartYear [Year]
--doing Union
union all
-- doing the loop in Years table
select Year+1 Year from [Years] where Year < #EndYear
)
--Selecting the Year table
selec]