Creating a range of numbers in an sql subquery - sql

Is there a better way to generate a range of numbers in SQL than below? I'm using MySql.
SELECT tens.x + ones.x + 1
FROM
(SELECT 0 x UNION ALL
SELECT 1 x UNION ALL
SELECT 2 x UNION ALL
...
SELECT 9 x ) ones
CROSS JOIN
(SELECT 0 x UNION ALL
SELECT 10 x UNION ALL
SELECT 20 x UNION ALL
...
SELECT 90 x ) tens;

A common way to do that in Oracle is to abuse the rownum pseudocolumn:
select rownum from all_objects where rownum<=100;

Using Sql server 2005+ you can make use of CTEs
DECLARE #Start INT, #End INT
SELECT #Start = 0, #End = 100000
;WITH Numbers AS (
SELECT #Start Num
UNION ALL
SELECT Num + 1
FROM Numbers
WHERE Num < #End
)
SELECT *
FROM Numbers
OPTION (MAXRECURSION 0);

PostgreSQL allows you to use:
select * from generate_series(2,4);
generate_series
-----------------
2
3
4
That is specific for the PostgresSQL engine. But it shouldn't be to hard to write a stored proedure for your data base.

Why not loop? like
BEGIN
DECLARE a INT Default 0 ;
simple_loop: LOOP
SET a=a+1;
insert into mytable(id) values(a);
IF a=1000 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
Modified from Mysql For Loop
Pardon me if the syntax is wrong as I am a pure SQL SERVER guy(:

Related

In SQL , how to build a loop that copies a row number of times

could someone please help? My starting table looks like this with 2 fields:
Name Counter
dave 2
Joe 3
I want my result to look like this:
Name Counter
dave 1
dave 2
joe 1
joe 2
joe 3
Essentially creating n number of records base on the counter and starts at 1. I tried to do a loop using counter as a variable, but the code just runs nonstop.. could someone help?
A procedural SQL Server solution:
declare #input table
(
name nvarchar(100)
,wantedrows int
,processed bit
,id uniqueidentifier
);
declare #output table
(
name nvarchar(100)
,rownum int
);
insert into #input
select 'Dave',3,0,newid()
union
select 'Joe',2,0,newid();
while exists(select * from #input where processed = 0)
begin
declare #currentid uniqueidentifier = (select top 1 id from #input where processed = 0);
declare #currentwantedrows int = (select wantedrows from #input where id = #currentid);
declare #i int = 0;
while #i < #currentwantedrows
begin
insert into #output
select name,#i+1
from #input
where id = #currentid;
set #i = #i + 1;
end;
update #input set processed = 1 where id = #currentid;
end
select name,wantedrows from #input;
select * from #output;
You can use a number-table or following trick using a system view to build a sequence:
WITH Nums AS
(
SELECT n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
)
SELECT Name, Counter = n
FROM Nums n CROSS JOIN Table1 t1
WHERE n BETWEEN 1 AND Counter
ORDER BY Name, Counter;
Demo
This view has only about 2000 rows, so if you need more you could use a number-table.
http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1
( presuming SQL-Server )
Is a hundred copies enough?
create table #c (num)
insert into #c (num)
select 0 union
select 1 union
select 2 union
select 3 union
select 4 union
select 5 union
select 6 union
select 7 union
select 8 union
select 9
select T.Name, c1.num * 10 + c0.num + 1
from T, #c c1, #c c0
where c1.num * 10 + c0.num < T.Counter
drop table #c
You didn't say which version of Sybase. The old ones I've worked on didn't allow derived tables so I had to throw the values into a temp table. But you can see how to extend the idea. This may not be the best approach if this is something you need to do more than once though.

Find missing numerical values in range [duplicate]

This question already has answers here:
How can we find gaps in sequential numbering in MySQL?
(16 answers)
Closed 8 years ago.
I've read several articles that one of the mistakes a common programmer does is not using SQL's potential and since then I started searching for replacing parts of my code with SQLish solutions rather than fetching data and processing with a programming language, although I'm a real rookie with SQL.
Say I have a table randomly populated with values from 0 to 10 and I want to know which values are missing in this range.
For example, the table consists these values: 0, 1, 3, 4, 5, 7, 8, 9.
The query should return: 2, 6, 10.
[F5] solution (assuming sql server):
-- table with id=0..10
drop table #temp
GO
create table #temp (
id int not null identity(0,1),
x int
)
GO
insert into #temp (x) values(0)
GO 11
-- your number:
drop table #numbers
GO
select
*
into #numbers
from (
select 0 as n union all select 1 union all select 3 union all select 4 union all select 5 union all select 7 union all select 8 union all select 9
) x
GO
-- result:
select
*
from #temp t
left join #numbers n
on t.id=n.n
where 1=1
and n.n is null
This solution uses SQL-Server-Syntax (but AFAIK only GO is specific to the SQL Server Management Studio)
I would join against a table valued function that gets you all numbers in a certain range (example fiddle):
CREATE FUNCTION dbo.GetNumbersInRange(#Min INT, #Max INT)
RETURNS #trackingItems TABLE (Number INT)
AS BEGIN
DECLARE #counter INT = #Min
WHILE (#counter <= #Max)
BEGIN
INSERT INTO #trackingItems (Number) SELECT #counter
SELECT #counter = #counter + 1
END
RETURN
END
GO
As an example I have set up a table that contains some numbers (with gaps)
CREATE TABLE MyNumbers (Number INT)
INSERT INTO MyNumbers (Number)
SELECT 1
UNION
SELECT 2
UNION
SELECT 4
UNION
SELECT 5
UNION
SELECT 7
UNION
SELECT 8
To find the missing numbers you can use a LEFT JOIN like this
SELECT
AllNumbers.Number
FROM GetNumbersInRange(1, 10) AS AllNumbers
LEFT JOIN MyNumbers ON AllNumbers.Number = MyNumbers.Number
WHERE MyNumbers.Number IS NULL

T-Sql count string sequences over multiple rows

How can I find subsets of data over multiple rows in sql?
I want to count the number of occurrences of a string (or number) before another string is found and then count the number of times this string occurs before another one is found.
All these strings can be in random order.
This is what I want to achieve:
I have one table with one column (columnx) with data like this:
A
A
B
C
A
B
B
The result I want from the query should be like this:
2 A
1 B
1 C
1 A
2 B
Is this even possible in sql or would it be easier just to write a little C# app to do this?
Since, as per your comment, you can add a column that will unambiguously define the order in which the columnx values go, you can try the following query (provided the SQL product you are using supports CTEs and ranking functions):
WITH marked AS (
SELECT
columnx,
sortcolumn,
grp = ROW_NUMBER() OVER ( ORDER BY sortcolumn)
- ROW_NUMBER() OVER (PARTITION BY columnx ORDER BY sortcolumn)
FROM data
)
SELECT
columnx,
COUNT(*)
FROM marked
GROUP BY
columnx,
grp
ORDER BY
MIN(sortcolumn)
;
You can see the method in work on SQL Fiddle.
If sortcolumn is an auto-increment integer column that is guaranteed to have no gaps, you can replace the first ROW_NUMBER() expression with just sortcolumn. But, I guess, that cannot be guaranteed in general. Besides, you might indeed want to sort on a timestamp instead of an integer.
I dont think you can do it with a single select.
You can use AdventureWorks cursor:
create table my_Strings
(
my_string varchar(50)
)
insert into my_strings values('A'),('A'),('B'),('C'),('A'),('B'),('B') -- this method will only work on SQL Server 2008
--select my_String from my_strings
declare #temp_result table(
string varchar(50),
nr int)
declare #myString varchar(50)
declare #myLastString varchar(50)
declare #nr int
set #myLastString='A' --set this with the value of your FIRST string on the table
set #nr=0
DECLARE string_cursor CURSOR
FOR
SELECT my_string as aux_column FROM my_strings
OPEN string_cursor
FETCH NEXT FROM string_cursor into #myString
WHILE ##FETCH_STATUS = 0 BEGIN
if (#myString = #myLastString) begin
set #nr=#nr+1
set #myLastString=#myString
end else begin
insert into #temp_result values (#myLastString, #nr)
set #myLastString=#myString
set #nr=1
end
FETCH NEXT FROM string_cursor into #myString
END
insert into #temp_result values (#myLastString, #nr)
CLOSE string_cursor;
DEALLOCATE string_cursor;
select * from #temp_result
Result:
A 2
B 1
C 1
A 1
B 2
Try this :
;with sample as (
select 'A' as columnx
union all
select 'A'
union all
select 'B'
union all
select 'C'
union all
select 'A'
union all
select 'B'
union all
select 'B'
), data
as (
select columnx,
Row_Number() over(order by (select 0)) id
from sample
) , CTE as (
select * ,
Row_Number() over(order by (select 0)) rno from data
) , result as (
SELECT d.*
, ( SELECT MAX(ID)
FROM CTE c
WHERE NOT EXISTS (SELECT * FROM CTE
WHERE rno = c.rno-1 and columnx = c.columnx)
AND c.ID <= d.ID) AS g
FROM data d
)
SELECT columnx,
COUNT(1) cnt
FROM result
GROUP BY columnx,
g
Result :
columnx cnt
A 2
B 1
C 1
A 1
B 2

How to insert n number of records in a table in a single command

I want to insert 10000 records in a table and i am currently writing this code
in sql server 2005
declare #n decimal(10,0);
set #n = 0;
while ( #n < 10000)
begin
insert into table1 values (#n+1)
set #n = #n + 1
end
in the above code insert command performs 10000 times is there any single command exists to do so.
Also you can use sys objects to your advantage:
INSERT INTO table1(n)
SELECT TOP 10000 ROW_NUMBER() OVER(ORDER BY a.object_id) AS n FROM sys.objects a CROSS JOIN sys.objects b
GO
You could use a CTE to create an in-memory table of 10000 items and use this to insert into your actual table.
;WITH q (n) AS (
SELECT 1
UNION ALL
SELECT n + 1
FROM q
WHERE n < 10000
)
INSERT INTO table1
SELECT * FROM q
OPTION (MAXRECURSION 0)
A quick way (from HERE):
declare #t table (number int)
insert into #t
select 0
union all
select 1
union all
select 2
union all
select 3
union all
select 4
union all
select 5
union all
select 6
union all
select 7
union all
select 8
union all
select 9
insert into numbers
select
t1.number + t2.number*10 + t3.number*100
from
#t as t1,
#t as t2,
#t as t3

In SQL can a sequenced range selection be done more efficiently than my algorithm (see code) that uses a cursor?

I need to collapse multiple ranges of sequential numbers (1 or more) to sets of their minimum and maximum values. I have unique integers (no duplicates) stored in a table column.
The obvious way (to me) to solve this problem is to use a cursor (see my algorithm below) and iterate through every integer. However, it seems inefficient to me so I am wondering if there is a more efficient algorithm. Perhaps there is a way using common table expressions with recursion. I have more than 32767 integers though, so any solution will need to use option (MAXRECURSION 0) which sets unlimited recursion.
Following is a simplified test case for my existing algorithm usign a cursor. It will output the minimum and maximum for each range of sequential numbers (e.g. 1-3, 9-11, 13-13, 15-16).
I am using MS SQL Server 2008. Please note comments begin with two dashes (--).
declare #minInt int, #maxInt int
declare #nextInt int, #prevInt int
--need a temporary table to store the ranges that were found
declare #rangeTable table (minInt int, maxInt int)
declare mycursor cursor for
select * from
(
select 1 as id union
select 2 as id union
select 3 as id union
select 9 as id union
select 10 as id union
select 11 as id union
select 13 as id union
select 15 as id union
select 16 as id
) tblRanges
order by id--order is needed for this algorithm if used with generic data
open mycursor
--initialise new sequence
fetch next from mycursor into #minInt
select #maxInt = #minInt--set the min and max to the smallest value
select #prevInt = #minInt--store the last int
declare #sequenceFound int
while ##FETCH_STATUS=0
begin
select #sequenceFound=1--set the default flag value to true
--loop while sequence found
while ##FETCH_STATUS=0 and #sequenceFound = 1
begin
fetch next from mycursor into #nextInt
if #nextInt = (#prevInt + 1)
begin
select #sequenceFound = 1
end
else
begin
select #sequenceFound = 0
end
select #prevInt = #nextInt--store the current value as the previous value for the next comparison
if #sequenceFound = 1 --if the nextInt is part of a sequence, then store the new maxInt
and #maxInt < #nextInt--should always be true for ordered output containing no duplicates
begin
select #maxInt = #nextInt
end
end--while sequenceFound
--store the sequence range and then check for more sequences
insert into #rangeTable (minInt,maxInt) values (#minInt,#maxInt)
--store the current value as the new minInt and maxInt for the next sequence iteration
select #minInt = #nextInt
select #maxInt = #nextInt
end--while more table rows found
select * from #rangeTable
close mycursor
deallocate mycursor
Courtesy of Itzik Ben-Gan:
WITH tblRanges AS
(
SELECT 1 AS ID UNION
SELECT 2 AS ID UNION
SELECT 3 AS ID UNION
SELECT 9 AS ID UNION
SELECT 10 AS ID UNION
SELECT 11 AS ID UNION
SELECT 13 AS ID UNION
SELECT 15 AS ID UNION
SELECT 16 AS ID
),
StartingPoints AS
(
SELECT ID, ROW_NUMBER() OVER(ORDER BY ID) AS rownum
FROM tblRanges AS A
WHERE NOT EXISTS
(SELECT *
FROM tblRanges AS B
WHERE B.ID = A.ID - 1)
),
EndingPoints AS
(
SELECT ID, ROW_NUMBER() OVER(ORDER BY ID) AS rownum
FROM tblRanges AS A
WHERE NOT EXISTS
(SELECT *
FROM tblRanges AS B
WHERE B.ID = A.ID + 1)
)
SELECT S.ID AS start_range, E.ID AS end_range
FROM StartingPoints AS S
JOIN EndingPoints AS E
ON E.rownum = S.rownum;
You can read a full explanation from his chapter in SQL Sever MVP Deep Dives called Gaps and Islands. He explains various techniques (including cursors) and compares them in terms of performance.