Inserting data from one table to another using a loop - sql-server

I am trying to insert data from one table into another (from table [PPRS] to table [Verify]) where the Caption in PPRS is the same as in table [Master]. Someone suggested i use a loop to insert the data instead of hard coding it, however I am confused as to how to go about it.
Here's my code so far:
Declare #counter int
declare #total int
set #counter = 0
SELECT #total = Count(*) FROM PPRS
while #counter <= #total
begin
set #counter += 1
insert into [Verify]
select [Task_ID],
[Project_StartDate] ,
[PPR_Caption],
[Date]
FROM PPRS
where [PPR_Caption] in (SELECT [Caption] from Master)
end
No data is being inserted (0 rows affected)
The sample data I'm trying to insert:
17286 01/03/2018 MP - Youth Environmental Services (12/15) 15/10/2018

I suggest that this is along the lines of what you want to do:
INSERT INTO [Verify] (some_col) -- you never told us the name of this column
SELECT TOP 10 [PPR_Caption]
FROM PPRS
WHERE [PPR_Caption] IN (SELECT [Caption] FROM MasterRecords)
ORDER BY some_column;
That is, you want to insert ten records into the Verify table, determined by some order in the source PPRS table.

Try to declare specific columns to which you want to make insert. Please provide definition of table PPRS and Verify for more precise help from community.
Anyway idea please find bellow:
Declare #counter int
set #counter = 0
while #counter <= 10
begin
set #counter += 1
insert into [Verify] (NameofColumn1_Table_Verify, NameofColumn2_Table_Verify,...)
select PPRS.NameofColumn1_Table_PPRS,
PPRS.NameofColumn2_Table_PPRS,
...
FROM PPRS
where PPRS.[PPR_Caption] in (SELECT DISTINCT [Caption] from MasterRecords)
end
Anyway I think the loop is unnecesary. One batch should make it. Please write what you want to achieve at the end.

Related

generate row number by using variable in mssql

In MYSQL, I could use variable to generate the row number like below:
SET #num=0;
SELECT #num:=#num+1 rownum,id FROM test;
How should I do this in MSSQL? I tried below code:
DECLARE #num int
SET #num=0;
SELECT #num=#num+1 ,id FROM test;
then I got the error message:
A SELECT statement that assigns a value to a variable must not be
combined with data-retrieval operations.
I know there're some built-in functions in mssql to generate the row number, but I'd like to know how to use variables in this case to solve the issue.
Appreciate if someone could help on this. :)
As the error message says, you can't do variable assignment in a select statement that also does data retrieval.
You can assign a variable using a select statement...
declare #myInt int;
select #myvariable = 1; -- works just like "set" in this case
And you can do data retrieval...
select someColumn from MyTable
But not both at the same time.
You can assign a value to a variable when selecting from a table, as long as you don't also select columns that don't get assigned to variables.
declare #myInt int;
select #myInt = myIntColumn from MyTable
If MyTable has a lot of rows in it, it's not clear which row will be used to populate the value of #myInt. SQL is "allowed" to pick any row it wants.
Of course, in your case that doesn't matter, since you're assigning a literal value to the variable. You can do this, where by "can", I mean the syntax won't be rejected:
declare #myInt int = 0;
select #myInt = #myInt + 1
from MyTable
But don't do this. It's an interesting quirk, which I am showing it to you with the good faith assumption that you won't use it. It is not supported and should not be relied upon.
Jeff Moden wrote an article about this where he goes over the dangers.
But don't do it even if you follow his rules.
If you want to create a rownum based on the variable value, you can do that by updating the value in the table like below:
drop table if exists #t
create table #t (id int, rownum int)
insert #t(id) select 100
insert #t(id) select 200
insert #t(id) select 300
insert #t(id) select 400
declare #i int
set #i = 0
update #t set rownum = #i, #i = #i + 1
select * from #t
order by rownum

How to optimize cursor in a stored procedure

I'm having problems with a stored procedure that iterates over a table, it works fine with a few hundred rows however when the table is over the thousands it saturates the memory and crashes.
The procedure should iterate row by row and fill a column with a value which is calculated from another column in the row. I suspect it is the cursor that crashes the procedure and in other questions I've read to use a while loop but I'm no expert in sql and the examples I tried from those answers didn't work.
CREATE PROCEDURE [dbo].[GenerateNewHashes]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #module BIGINT = 382449983
IF EXISTS(SELECT 1 FROM dbo.telephoneSource WHERE Hash IS NULL)
BEGIN
DECLARE hash_cursor CURSOR FOR
SELECT a.telephone, a.Hash
FROM dbo.telephoneSource AS a
OPEN hash_cursor
FETCH FROM hash_cursor
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE dbo.telephoneSource
SET Hash = CAST(telephone AS BIGINT) % #module
WHERE CURRENT OF hash_cursor
FETCH NEXT FROM hash_cursor
END
CLOSE hash_cursor
DEALLOCATE hash_cursor
END
END
Basically the stored procedure is intended to fill a new column called Hash that was added to the existing table, when the script that updates the table ends the new column is filled with NULL values and then this stored procedure is supposed to fill each null value with the operation telephone number (which is a bigint) % module variable (big int as well).
Is there anything besides changing to a while loop that I can do to make it use less memory or just don't crash? Thanks in advance.
You could do the following:
WHILE 1=1
BEGIN
UPDATE TOP (10000) dbo.telephoneSource
SET Hash = CAST(telephone AS BIGINT)%#module
WHERE Hash IS NULL;
IF ##ROWCOUNT = 0
BEGIN
BREAK;
END;
END;
This will update Hash as long as there are NULL values and will exit once there have been no records updated.
Adding a filtered index could be useful as well:
CREATE NONCLUSTERED INDEX IX_telephoneSource_Hash_telephone
ON dbo.telephoneSource (Hash)
INCLUDE (telephone)
WHERE Hash IS NULL;
It will speed up lookups in order to update it. But this might be not needed.
Here is example of code to do it in loops from my comment above with out using a cursor, and if you add where your field you are updating IS NOT NULL into the inner loop it wont update ones that were already done (in case you need to restart the process or something.
I didnt include your specific tables in there but if you need me to I can add it in there.
DECLARE #PerBatchCount as int
DECLARE #MAXID as bigint
DECLARE #WorkingOnID as bigint
Set #PerBatchCount = 1000
--Find range of IDs to process using yoru tablename
SELECT #WorkingOnID = MIN(ID), #MAXID = MAX(ID)
FROM YouTableHere WITH (NOLOCK)
WHILE #WorkingOnID <= #MAXID
BEGIN
-- do an update on all the ones that exist in the offer table NOW
--DO YOUR UPDATE HERE
-- include this where clause where ID is your PK you are looping through
WHERE ID BETWEEN #WorkingOnID AND (#WorkingOnID + #PerBatchCount -1)
set #WorkingOnID = #WorkingOnID + #PerBatchCount
END
SET NOCOUNT OFF;
I would simply add computed column:
ALTER TABLE dbo.telephoneSource
ADD Hash AS (CAST(telephone AS BIGINT)%382449983) PERSISTED;

How to get and use the value returned by a stored procedure to a INSERT INTO... SELECT... statement

I am just new in SQL language and still studying it. I'm having hard time looking for answer on how can I use the stored procedure and insert value into a table.
I have this stored procedure:
CREATE PROCEDURE TestID
AS
SET NOCOUNT ON;
BEGIN
DECLARE #NewID VARCHAR(30),
#GenID INT,
#BrgyCode VARCHAR(5) = '23548'
SET #GenID = (SELECT TOP (1) NextID
FROM dbo.RandomIDs
WHERE IsUsed = 0
ORDER BY RowNumber)
SET #NewID = #BrgyCode + '-' + CAST(#GenID AS VARCHAR (30))
UPDATE dbo.RandomIDs
SET dbo.RandomIDs.IsUsed = 1
WHERE dbo.RandomIDs.NextID = #GenID
SELECT #NewID
END;
and what I'm trying to do is this:
INSERT INTO dbo.Residents([ResidentID], NewResidentID, [ResLogdate],
...
SELECT
[ResidentID],
EXEC TestID ,
[ResLogdate],
....
FROM
source.dbo.Resident;
There is a table dbo.RandomIDs containing random 6 digit non repeating numbers where I'm pulling out the value via the stored procedure and updating the IsUsed column of the table to 1. I'm transferring data from one database to another database and doing some processing on the data while transferring. Part of the processing is generating a new ID with the required format.
But I can't get it to work Sad I've been searching the net for hours now but I'm not getting the information that I need and that the reason for my writing. I hope someone could help me with this.
Thanks,
Darren
your question is little bit confusing, because you have not explained what you want to do. As i got your question, you want to fetch random id from randomids table and after performed some processing on nextid you want to insert it into resident table [newresidentid] and end of the procedure you fetch data from resident table. if i get anything wrong feel free to ask me.
your procedure solution is following.
CREATE PROCEDURE [TestId]
AS
SET NOCOUNT ON;
BEGIN
DECLARE #NEWID NVARCHAR(30)
DECLARE #GENID BIGINT
DECLARE #BRGYCODE VARCHAR(5) = '23548'
DECLARE #COUNT INTEGER
DECLARE #ERR NVARCHAR(20) = 'NO IDS IN RANDOM ID'
SET #COUNT = (SELECT COUNT(NEXTID) FROM RandomIds WHERE [IsUsed] = 0)
SET #GENID = (SELECT TOP(1) [NEXTID] FROM RandomIds WHERE [IsUsed] = 0 ORDER BY [ID] ASC)
--SELECT #GENID AS ID
IF #COUNT = 0
BEGIN
SELECT #ERR AS ERROR
END
ELSE
BEGIN
SET #NEWID = #BRGYCODE + '-' + CAST(#GENID AS varchar(30))
UPDATE RandomIds SET [IsUsed] = 1 WHERE [NextId] = #GENID
INSERT INTO Residents ([NewResidentId] , [ResLogDate] ) VALUES (#NEWID , GETDATE())
SELECT * FROM Residents
END
END
this procedure will fetch data from your randomids table and perform some processing on nextid than after it directs insert it into resident table and if you want to insert some data through user you can use parameter after declaring procedure name
E.G
CREATE PROCEDURE [TESTID]
#PARAM1 DATATYPE,
#PARAM2 DATATYPE
AS
BEGIN
END
I'm not convinced that your requirement is a good one but here is a way to do it.
Bear in mind that concurrent sessions will not be able to read your update until it is committed so you have to kind of "lock" the update so you will get a block until you're going to commit or rollback. This is rubbish for concurrency, but that's a side effect of this requirement.
declare #cap table ( capturedValue int);
declare #GENID int;
update top (1) RandomIds set IsUsed=1
output inserted.NextID into #cap
where IsUsed=0;
set #GENID =(select max( capturedValue) from #cap )
A better way would be to use an IDENTITY or SEQUENCE to solve your problem. This would leave gaps but help concurrency.

using script in a variable

Can you declare a variable, set it as a script definition and then execute it repeatedly throughout your script? I understand how to set a variable to the result of a script, but I want to re-use the definition itself. This is because I want to occasionally get the count from a script and sometimes the top result throughout the rest of my script and I want to make it so the script is easily customized by only needing to change the script once at the beginning.
An example:
declare #RepeatScript nvarchar(200)
declare #count int
declare #topresult int
set #RepeatScript = ' from Table1 where something = 1 and something else > getdate()-5'
set #count = select count(ID) & #RepeatScript
set #topresult = select top 1 (ID) & #RepeatScript
This very simple case would be simple to fix, but if I wanted to reference the same set of information multiple times without having to create and drop a temp_table over and over, this would be very helpful. I do this kind of thing in MS Access all the time, but I can't seem to figure out how to do it in SSMS.
You don't need to repeatedly run these queries. You don't even need to run more than 1 query to capture this information. This will capture both pieces of data in a single query. You can then reference that information anywhere else within the current batch. This meets your criteria of simply changing the script at the beginning.
declare #count int
, #topresult int
select #count = count(ID)
, #topresult = MAX(ID) --MAX would the same thing as top 1 order by ID desc
from Table1
where something = 1
declare #RepeatScript nvarchar(200)
declare #count varchar(200)
declare #topresult varchar(200)
set #RepeatScript = ' from Table1 where something = 1 and something else > getdate()-5'
set #count = 'select count(ID) '+#RepeatScript+''
set #topresult = 'select top 1 (ID)'+#RepeatScript+''
print (#count)
print (#topresult)
Something like that? but instead of using print you would be using exec to run the select statement. Does that help?

select TOP (all)

declare #t int
set #t = 10
if (o = 'mmm') set #t = -1
select top(#t) * from table
What if I want generally it resulted with 10 rows, but rarely all of them.
I know I can do this through "SET ROWCOUNT". But is there some variable number, like -1, that causing TOP to result all elements.
The largest possible value that can be passed to TOP is 9223372036854775807 so you could just pass that.
Below I use the binary form for max signed bigint as it is easier to remember as long as you know the basic pattern and that bigint is 8 bytes.
declare #t bigint = case when some_condition then 10 else 0x7fffffffffffffff end;
select top(#t) *
From table
If you dont have an order by clause the top 10 will just be any 10 and optimisation dependant.
If you do have an order by clause to define the top 10 and an index to support it then the plan for the query above should be fine for either possible value.
If you don't have a supporting index and the plan shows a sort you should consider splitting into two queries.
im not sure I understand your question.
But if you sometimes want TOP and other times don't just use if / else construct:
if (condition)
'send TOP
SELECT TOP 10 Blah FROM...
else
SELECT blah1, blah2 FROM...
You can use dynamic SQL (but I, personally, try to avoid dynamic SQL), where you create a string of the statement you want to run from conditions or parameters.
There's also some good information here on how to do it without dynamic SQL:
https://web.archive.org/web/20150520123828/http://sqlserver2000.databases.aspfaq.com:80/how-do-i-use-a-variable-in-a-top-clause-in-sql-server.html
declare #top bigint = NULL
declare #top_max_value bigint = 9223372036854775807
if (#top IS NULL)
begin
set #top = #top_max_value
end
select top(#top) *
from [YourTableName]
a dynamic sql version isn't that's hard to do.
CREATE PROCEDURE [dbo].[VariableTopSelect]
-- Add the parameters for the stored procedure here
#t int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #sql nvarchar(max)
if (#t=10)
begin
set #sql='select top (10) * from table'
end
else
begin
set #sql='select * from table'
end
exec sp_executesql #sql
END
with this sp, if they send 10 to the sp, it'll select the top 10, otherwise it'll select all.
The best solution I've found is to select the needed columns with all of your conditions into a temporary table, then do your conditional top:
DECLARE #TempTable TABLE(cols...)
INSERT INTO #TempTable
SELECT blah FROM ...
if (condition)
SELECT TOP 10 * FROM #tempTable
else
SELECT * FROM #tempTable
This way you follow DRY, get your conditional TOP, and are just as easy to read.
Cheers.
It is also possible with a UNION and a parameter
SELECT DISTINCT TOP 10
Column1, Column2
FROM Table
WHERE #ShowAllResults = 0
UNION
SELECT DISTINCT
Column1, Column2
FROM Table
WHERE #ShowAllResults = 1
I might be too late now, or getting too old
But I solved that by using Top(100)Percent
This goes around all complexities
Select Top(100)Percent * from tablename;
Use the statement "SET ROWCOUNT #recordCount" at the beginning of the result query.The variable "#recordCount" can be any positive integer. It should be 0 to return all records.
that means , "SET ROWCOUNT 0" will return all records and "SET ROWCOUNT 15" will return only the TOP 15 rows of result set.
Drawback can be the Performance hit when dealing with large number of records. Also the SET ROWCOUNT will be effective throughout the scope of execution of the whole query.

Resources