Question : How do I create a loop to insert all rows of one table to another with stored procedure

I written this stored procedure to populate my Traps table but the procedure only populates one row multiple times.  I can't seem to get the procedure to insert the correct rows via a loop.  How can I tweak my script to insert the rows via a loop?
Code Snippet:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
if exists (select * from sysobjects where name = N'dbm_HostName2Trap')
 
   drop procedure dbm_HostName2Trap
go
 
CREATE PROCEDURE  dbm_HostName2Trap
AS
 
SET NOCOUNT OFF
-- declare variables
Declare @T_ID int
Declare @IP_Address varchar(15)
Declare @Hostname varchar(255)
Declare @N_ID int
Declare @iLoop int
Declare @iNextRowId int
Declare @iCurrentRowId int
 
-- initialize variables 
SET IDENTITY_INSERT dbo.Traps ON
SELECT @T_ID = (MAX(TRAPID) + 1) FROM Traps;
SELECT @Hostname = Hostname from IP_Resolve;
SELECT @IP_Address = IP_Address from IP_Resolve;
Select @N_ID = NodeID from Nodes,IP_Resolve where IP_Resolve.IP_Address = Nodes.IP_Address;
SELECT @iLoop = 1;
SELECT @iNextRowId = MAX(ID) FROM IP_Resolve;
 
IF ISNULL(@iNextRowId,0) = 0
 
   BEGIN
            SELECT 'No data in found in table!'
            RETURN
   END
 
-- Retrieve the first one
select @iCurrentRowId = ID from IP_Resolve where ID = @iNextRowId;
-- insert statement
INSERT INTO dbo.Traps (TrapID,EngineID,DateTime,IPAddress,Community,Tag,Acknowledged,Hostname,NodeID,TrapType,ColorCode) VALUES ( @T_ID,'2',DEFAULT,@IP_Address,'public',DEFAULT,DEFAULT,@Hostname,@N_ID,DEFAULT,DEFAULT ); 
 
-- start the main processing loop.
 
WHILE @iLoop = 1
 
   BEGIN
 
     -- This is where you perform your detailed row-by-row
 
     -- processing.     
 
     -- Reset looping variables.            
 
            SELECT   @iNextRowId = NULL            
 
            -- get the next iRowId
SELECT @T_ID = (MAX(TRAPID) + 1) FROM Traps;
 
            SELECT   @iNextRowId = MAX(ID) FROM IP_Resolve
            WHERE    ID < @iCurrentRowId
 
            -- did we get a valid next row id?
 
            IF ISNULL(@iNextRowId,0) = 0
 
               BEGIN
 
                        BREAK
 
               END
 
            -- get the next row.
select @iCurrentRowId = ID from IP_Resolve where ID=@iNextRowId;
 
      INSERT INTO dbo.Traps (TrapID,EngineID,DateTime,IPAddress,Community,Tag,Acknowledged,Hostname,NodeID,TrapType,ColorCode) VALUES ( @T_ID,'2',DEFAULT,@IP_Address,'public',DEFAULT,DEFAULT,@Hostname,@N_ID,DEFAULT,DEFAULT ); 
       
   END
RETURN
Open in New Window Select All

Answer : How do I create a loop to insert all rows of one table to another with stored procedure

Sure,

First up let us look at the different pieces of the puzzle...

1) Traps is being populated.
2) Traps has an identity column TrapID.
3) When we insert, we want to make sure TrapID is incremented
4) We need to get information mainly from IP_Resolve, but also from Nodes

OK, so lets look at the overall task first - denoted by point 1.

Somewhere we need to construct an INSERT TRAPS statement. Which is terrific, but where and how do we get to the data ? That is always the 64 million dollar question that consumes most T-SQL programmers time, and is where we start the journey.

We know we have to get the data from IP_Resolve and Nodes. One way we can connect those two tables is using a join, or we could even lookup if there is nothing obvious to join on. So that is our first task and we can start with a select statement...

Select R.*, N.*
from IP_Resolve R
Inner join Nodes N on r.id_address = n.ip_address


Notice how I use a table alias for both IP_Resolve and Nodes (being R and N respectively). Using an Alias means that the table is now referenceable using those aliases, and save a bit of typing, creates flexibility (could change the table without having to change the rest of the query), and allows for duplicate sources (could have another table of the same name, but with a different alias).

Whe I run that query, I am looking at the number of rows and the columns being selected to ensure I am getting the rught results. So let us fine tune that query a bit more, and include some of the other things we want.

Select '2' as engineid, r.ip_address, 'public' as cummunity, r.hostname, n.nodeid
from ip_resolve r
inner join nodes n on r.id_address = n.ip_address
order by r.ip_address,n.nodeid  

Now, I can keep playing with that query until I am happy with the results, and maybe because there are other "common" columns, might need to fine tune that join a bit more, or even add in a "where" clause to help filter those results. For example, there might be duplicates, and we just want each distinct combination of IP_Address and Nodeid per Hostname, so we can add that in. Also, we may as well exclude the (not real, but as an example) "test" server (so you see a where clause in action).

Select Distinct '2' as engineid, r.ip_address, 'public' as cummunity, r.hostname, n.nodeid
from ip_resolve r
inner join nodes n on r.ip_address = n.ip_address
where r.hostname <> 'My_Test_Dummy_Dev_QA_Server'
order by r.ip_address,n.nodeid  

By now our query should be complete. In so much as we have identified where we are getting the data from and happy with the results. So, now we can start looking at the INSERT statement.

It is at this point in time when you can decide what kind of action needs to be taken. It could be that a cursor is appropriate, however, it normally is not. Looking at row by row is what the cursor is all about, and allowing SQL to get back a complete "set" of data so it can manage it is much much more efficient.

First up, always use the column list. If your table changes underneath, then using the column list will keep it one step independant of some of those changes. It is best to always code for the known conditions. We also have our data source from the query above, so now our insert statement will take on the form of INSERT () select from . Next thing to look at is the inserted ...

We need / must populate a few 'known' columns. They are any column that cannot be NULL, along with the data elements we want to know. There are also a few columns we do not have to worry about. They are those columns which can be null, or, SQL is going to manage for us such as columns with a default value, or IDENTITY columns. With that in mind, let us now build our insert statement....

INSERT TRAPS (engineid, ipaddress, community, hostname, nodeid)     -- note trapid is an identity so SQL will do that for us.
SELECT Distinct '2' as engineid, r.ip_address, 'public' as cummunity, r.hostname, n.nodeid
FROM ip_resolve r
INNER JOIN nodes n on r.ip_address = n.ip_address                            -- inner join means that a row MUST exist for r.ip_address in the NODES table
where r.hostname <> 'My_Test_Dummy_Dev_QA_Server'
order by r.ip_address,n.nodeid                                                             -- no real need for an order by in this case, it was more so we can see the rows in our query


And that is basically what we needed to achieve. Looking now at CGLuttrell's posting :

INSERT INTO dbo.Traps (EngineID,IPAddress,Community,Hostname,NodeID)
SELECT '2',IP_Resolve.IP_Address,'public',IP_Resolve.Hostname,Nodes.NodeID
FROM IP_Resolve
INNER JOIN Nodes ON IP_Resolve.IP_Address = Nodes.IP_Address;

It has all the same elements as we have built up, and so, the individual components should hopefully be recognisable to you, and if not please let me know which parts are still a bit confusing...

Now all we have to do is wrap that up in a procedure if we still want to. Normally a procedure is good for coding when there are various conditional steps, or parameters involved. In this case you can execute the query directly, so doesn't really have to be encapsulate in a stored procedure, but no real reason why it can't be. If it is a stored procedure the one big advantage is that you are effectively hiding the raw code from the end-user space and can be desirable to help prevent SQL injection (where additional SQL commands can be inserted).

There is one other facet to consider. And that is the possibility of duplicates in terms of data content. That can sometimes happen when there is a surrogate key such as an identity used for the primary key. It means I could add in the same ip_address a few times, and it will never be regarded as a duplicate. There are a couple of strategies for that, and believe the best is to explicity code and cater for that. Found one of the easiest ways is to include an EXISTS statement which simply tests a results set. So the complete query statement could be more like :


INSERT TRAPS (engineid, ipaddress, community, hostname, nodeid)     -- note trapid is an identity so SQL will do that for us.
SELECT Distinct '2' as engineid, r.ip_address, 'public' as cummunity, r.hostname, n.nodeid
FROM ip_resolve r
INNER JOIN nodes n on r.ip_address = n.ip_address                            -- inner join means that a row MUST exist for r.ip_address in the NODES table
WHERE r.hostname <> 'My_Test_Dummy_Dev_QA_Server'
AND NOT EXISTS (Select NULL from traps T where t.engineid = '2' and t.ip_address = r.ip_address and t.hostname = r.hostname and t.nodeid = n.nodeid)


The above addition of NOT EXISTS is now testing to see if there are existing rows with the same columns, and we want to add if not already there.

One comment, we populated engineid with '2' and if it is an integer then do not use the single quotes, e.g. 2 as engineid

Do you want the original stored procedure exlained now ?



Random Solutions  
 
programming4us programming4us