How to bulk insert a bunch of sequential numbers quickly? - sql-server

I recently inherited this SQL:
TRUNCATE TABLE [tb_Whitelist]
--DECLARE #Counter INT, #Max INT
SELECT #Counter = 10000000
SELECT #Max = 19999900
WHILE #Counter <= #Max
BEGIN
INSERT [tb_Whitelist] ([AvailableId]) VALUES(#Counter)
SELECT #Counter = #Counter + 1
END
It's taking 7 ish hours to run, which I'm told is too slow. Are there any other bulk insert strategies that will allow me to insert a bunch of sequential numbers more quickly, or anything I can do to make this one run faster?

Try something like this....
INSERT [tb_Whitelist] ([AvailableId])
SELECT TOP (9999900)
10000000 + ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
cross join master..spt_values t2
cross join master..spt_values t3
While loop will be slow, I just ran this query on my laptop which has 8GB Ram and pretty standard processor and it took me 1 min 45 sec to insert these records.

Avoid RBAR. Give it a try with a CTE and inserting in a single statement.
DECLARE
#counter INT = 1,
#max INT = 50000;
with i (i) AS (
select #counter i
UNION ALL
select i+1 from i
where
i< #MAX
)
insert into [tb_Whitelist]
([AvailableId])
select
i
from i
OPTION(MAXRECURSION 0)

Related

Substring is slow with while loop in SQL Server

One of my table column stores ~650,000 characters (each value of the column contains entire table). I know its bad design however, Client will not be able to change it.
I am tasked to convert the column into multiple columns.
I chose to use dbo.DelimitedSplit8K function
Unfortunately, it can only handle 8k characters at max.
So I decided to split the column into 81 8k batches using while loop and store the same in a variable table (temp or normal table made no improvement)
DECLARE #tab1 table ( serialnumber int, etext nvarchar(1000))
declare #scriptquan int = (select MAX(len (errortext)/8000) from mytable)
DECLARE #Counter INT
DECLARE #A bigint = 1
DECLARE #B bigint = 8000
SET #Counter=1
WHILE ( #Counter <= #scriptquan + 1)
BEGIN
insert into #tab1 select ItemNumber, Item from dbo.mytable cross apply dbo.DelimitedSplit8K(substring(errortext, #A, #B), CHAR(13)+CHAR(10))
SET #A = #A + 8000
SET #B = #B + 8000
SET #Counter = #Counter + 1
END
This followed by using below code
declare #tab2 table (Item nvarchar(max),itemnumber int, Colseq varchar(10)) -- declare table variable
;with cte as (
select [etext] ,ItemNumber, Item from #tab1 -- insert table name
cross apply dbo.DelimitedSplit8K(etext,' ')) -- insert table columns name that contains text
insert into #tab2 Select Item,itemnumber, 'a'+ cast (ItemNumber as varchar) colseq
from cte -- insert values to table variable
;WITH Tbl(item, colseq) AS(
select item, colseq from #tab2
),
CteRn AS(
SELECT item, colseq,
Rn = ROW_NUMBER() OVER(PARTITION BY colseq ORDER BY colseq)
FROM Tbl
)
SELECT
a1 Time,a2 Number,a3 Type,a4 Remarks
FROM CteRn r
PIVOT(
MAX(item)
FOR colseq IN(a1,a2,a3,a4)
)p
where a3 = 'error'
gives the desired output. However, just the loop takes 15 minutes to complete and overall query completes by 27 minutes. Is there any way I can make it faster? Total row count in my table is 2. So I don't think Index can help.
Client uses Azure SQL Database so I can't choose PowerShell or Python to accomplish this either.
Please let me know if more information is needed. I tried my best to mention everything I could.

How to use the while loop and how to use the count in the loop in sql server

My query:
Declare #counter int
set #counter = 1
while #counter <= 12
Begin update table_1
set count = #counter
set #counter = #counter + 1
end
I'm using this query and I'm expecting my table have 12 records in that column (1-12),but after I run the query, nothing show up in my existing table. Can anyone tell me how to deal with this?
You can use recursive CTE :
with cte as (
select 1 as cnt
union all
select cnt + 1
from cte c
where c.cnt < 12
)
insert into table_1 (count)
select c.cnt
from cte c;
If you have more count then use MAX Recursion Hint, recursive cte has default 100 recursive.
insert into table_1 (count)
select c.cnt
from cte c
option(maxrecursion 0);

How to insert multiple rows into a table based on a range of numbers

I have to insert a specific number of rows into a SQL Server table.
DECLARE #val AS INT = 20,
#val2 AS VARCHAR(50),
#Date AS DATETIME = CONVERT(DATETIME,'02-05-2016'),
#i AS INT = 0
SET #val2 = 'abc'
DECLARE #tbl TABLE
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[val2] VARCHAR(50) NULL,
[datum] [datetime] NULL
)
--INSERT INTO #tbl
SELECT #val2, DATEADD(DAY, #i, #Date)
UNION ALL
SELECT #val2, DATEADD(DAY, #i, #Date)
In this query, I have to insert dates starting from a given date till the number of value assigned to the variable '#val'. So, in this case, 20 rows need to be inserted into the table starting from '02-05-2016' and then date increasing 1 day for each row.
How can I do it in a single statement without any looping or multiple insert statements?
You can use a numbers table if you have one, use master.dbo.spt_values if you want one that has values till 2048, or create one of your own. In this case, you could use master.dbo.spt_values:
DECLARE #val AS INT=20, #val2 AS VARCHAR(50);
DECLARE #Date AS DATETIME = CONVERT(DATETIME,'02-05-2016');
SET #val2 = 'abc'
INSERT INTO dbo.YourTable
SELECT #val2, DATEADD(DAY,number,#Date)
FROM master.dbo.spt_values
WHERE type = 'P'
AND number <= #val;
Though since this starts at zero, you'll get 21 rows as a result
Besides the detailed answer I pointed to in my comment, this is the idea in short:
DECLARE #start INT=0;
DECLARE #end INT=19; --0 to 19 are 20 days
DECLARE #StartDate DATE={d'2016-01-01'};
--Create a List of up to 1.000.000.000 rows on the fly
--This is limited by start and end parameter
;WITH x AS(SELECT 1 AS N FROM(VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS tbl(N))--10^1
,N3 AS (SELECT 1 AS N FROM x CROSS JOIN x AS N2 CROSS JOIN x N3) --10^3
,Tally AS(SELECT TOP(#end-#start +1) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) + #start -1 AS Nr FROM N3
CROSS JOIN N3 N6 CROSS JOIN N3 AS N9)
--INSERT INTO your_table
SELECT #val2 --your #val2 here as a constant value
,DATEADD(DAY,Nr,#StartDate)
FROM Tally
You could use a recursive CTE.
DECLARE #i INT = 1
, #m INT = 19
, #d DATETIME2 = '2016-05-02';
WITH i AS (
SELECT 0 AS increment
UNION ALL
SELECT i.increment + #i
FROM i
WHERE i.increment < #m
)
SELECT i.increment
, DATEADD(DAY, i.increment, #d)
FROM i
OPTION (MAXRECURSION 100);
Note the OPTION (MAXRECUSION 100) hint at the bottom, which is not strictly necessary but I have included it to illustrate how it works. By default, there is a limit of 100 results using this method, so without this statement and if #m were a large number e.g. 1000 then SQL would generate an error. You can set the lmit to 0 which means unbounded, but only do this after testing your code, because it can get stuck in an infinite loop this way (which is why the limit exists by default).

SQL SERVER INSERT STATEMENT WITH LOOP Needs Optimized

I have SQL Statement running in SQL server 2014.
I have 3 columns: id, text1, text2
Inserting records in text1, text2
text1 is nvarchar
text2 is varchar
so far inserted 1.2 million rows in about 3.5 hrs
trying to insert 3 million need help in reduce time for insert
CODE:
DECLARE #i as int
SET #i = 0
WHILE #i < 3000000
BEGIN
SET #i = #i + 1
insert into test (text1 , text2)
values(N'你好','VJ'+ cast(#i as varchar(20)))
END
Here is another way to do it ... It finish pretty fast ... less then 5 sec on my SQL server
if object_id('tempdb..#Numbers') is not null drop table #Numbers
create table #Numbers (Num int)
insert into #Numbers (Num)
SELECT TOP (3000000) n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
OPTION (MAXDOP 1);
if object_id('tempdb..#test') is not null drop table #test
create table #test (text1 nvarchar(50), text2 nvarchar(50))
insert into #test (text1, text2)
select N'你好' [text1], 'VJ' + cast(Num as nvarchar) [text2] from #Numbers
You can try like this
WITH CTE_TEST
AS
(
SELECT N'你好' AS CODE,'VJ'+ cast(1 as varchar(20)) NAME, 1 AS VCOUNT
UNION ALL
SELECT N'你好' ,'VJ'+ cast(VCOUNT+1 as varchar(20)) NAME, VCOUNT+1 AS VCOUNT
FROM CTE_TEST
WHERE VCOUNT+1 < 3000000
)
INSERT INTO test (text1 , text2)
SELECT CODE,NAME FROM CTE_TEST
OPTION (MAXRECURSION 0)
You can use your additional logic inside the CTE, and simply insert the result set to actual table. Here the insert statement is outside the loop (1 insert statement with 3000000 records) , so it will faster than inserting the record inside a loop 3000000 times (3000000 insert statements with 1 record each)
By default the MAXRECURSION is 100 to avoid infinite looping, here you need to override this (but it is not a good practice).
An alternative that does not use recursive CTE is to use a known table with many enough records, so that you can iterate against it:
-- generation parameters
declare #batchCount INT = 100000
declare #totalCount INT = 30000000
declare #loopCount INT = #totalCount / #batchCount
DECLARE #i as int = 0
-- loops are slow, but here we have only a few
WHILE (#i < #loopCount)
BEGIN
-- insert can be put just here to actually perform the insert
-- ROW_NUMBER gives us the numbering, but order does not matter, so using SELECT 1
select TOP (#batchCount) N'你好','VJ'+ cast(#i * #batchCount + ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as varchar(20))
from sys.messages
SET #i = #i + 1
END
sys.messages is a pretty large table (at least 200K records), so it can safely be used for batches of 100K.
Time using recursive CTE: 51s
Time using above solution: 28s
(tested on a SQL Server 2014 Express instance, SELECT only)

Execute SQL Statement more than once like a For Loop

I have a SQL query (normal one). I have to run this query 4 times continuously (like a For Loop in programming). How can I have something like an array and repeat the query execution?
SQL Server
Update :
I am updating some data based on a column TargetLocation. This target location has values from 1 to 5. For each value I need to update the records that have same target location.
If you are running the query in SQL Server Management Studio, then you can use GO N to run a query N times. For example:
insert into MyTable (MyCol) select 'NewRow'
go 4
This will insert 4 rows into MyTable with the text 'NewRow' in them.
If you really need to loop over something in another application, then I recommend using the while loop as suggested by Peter Tirrell.
Note that loops are usually unnecessary in SQL. They may indicate code that is written with procedural logic instead of set-based logic.
Something like a simple SQL WHILE loop?
declare #counter int
set #counter = 0
while #counter < 10
begin
select 'foo'
set #counter = #counter + 1
end
I think you want a join in your UPDATE, as in:
--create two sample tables that we can work on
declare #tabletoupdate table(ID int,TARGETLOCATION int);
declare #sourcetable table(ID int,SOURCELOCATION int);
--drop in sample data
insert into #tabletoupdate select 1,10 union select 2,20 union select 3, 30;
insert into #sourcetable select 1,100 union select 2,200 union select 3, 300;
--see the 'before'
select * from #tabletoupdate
select * from #sourcetable
--make target look like source
update #tabletoupdate
set
targetlocation = s.sourcelocation
from
#tabletoupdate t
inner join #sourcetable s on s.id = t.id;
--show 'after'
select * from #tabletoupdate
select * from #sourcetable
/*
--if you really insist on doing it with a loop
--bad because its
--1) slower
--2) less readable
--3) less reliable when other users are accessing the data
declare #currentID int = 0;
declare #maxID int = (select max(id) from #sourcetable);
while #currentID < #maxID
begin
set #currentID = #currentID + 1;
declare #newval int = (select sourcelocation
from #sourcetable
where id = #currentID
);
if #newval is not null
begin
update #tabletoupdate
set TARGETLOCATION = #newval
where id = #currentID;
end
end
--*/

Resources