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_Serv
er'
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 ?