I'm going to skip past your questions and try to offer broader guidelines/advice instead.
The definitive/canonical guide to dynamic SQL, the situations where it is applicable and where it can be avoided, is Erland Sommarskog's Dynamic Search Conditions in T-SQL. Read it, re-read, run through Erland's examples, make sure you understand the reasoning behind the recommendations.
You're dealing with a fairly common scenario and the approach you've taken is not unusual. A couple of points worth highlighting:
- Using temporary tables is probably unnecessary. Is there a reason they were introduced?
- You have probably over-indexed the table. Read Kimberly Tripp's "just because you can, doesn't mean you should" article on the topic.
- Because you've over-indexed on individual columns, you're probably lacking good covering indexes. With so many aggregations and such a wide range of search conditions, these will be a challenge to get right.
Now the most important part of getting these kinds of searches right... apply the 80/20 rule.
The majority of calls to your procedure are likely to comprise a relatively small number of the possible variations of parameters. You cannot create optimal indexes for all combinations of 15 parameters, so identify the most common patterns, create static stored procedures for these and index for them appropriately. Deal with the remaining combinations with dynamic SQL, following Erland's best practices.
In these scenarios, you will often find the usage patterns closer to 95/5 than 80/20 so the additional work of creating static procedures is not as labour intensive as it seems at first glance.
One execution of your script will produce one computer name and a list of one or more monitors. You can pass these to a SQL Server stored procedure, you just have to get the parameter types correct.
The computer name is simplest. With just this the SP definition would look like this:
create procedure dbo.MyProcedureName
@ComputerName varchar(100)
as
...
I use varchar(100)
as an example. You use whatever's right for you. The list of monitors is more difficult because it may contain many values. For this you have to use a table-valued parameter and for that you will have to define a type:
create type MonitorList AS TABLE (
MonitorName varchar(100)
);
GO
Now the SP looks like this:
create procedure dbo.MyProcedureName
@ComputerName varchar(100),
@Monitors MonitorList READONLY
as
...
Now to the body of the SP. If you use the names as the primary key in tables Computer
and Monitor
the INSERT
statements are straightforward. Assuming you have primary keys and foreign keys defined -- you should -- you have to insert in the correct sequence to respect these key definitions i.e. into Computer
and Monitor
and only then into the mapping table.
insert dbo.Computer (ComputerName)
values (@ComputerName);
The type we defined, and variables created off it, behave just like any other table:
insert dbo.Monitor (MonitorName)
select MonitorName
from @Monitors;
insert Monitor_Computer_Map (ComputerName, MonitorName)
select @ComputerName, MonitorName
from @Monitors;
If your tables have surrogate keys that use IDENTITY
, however, you have more work to do. You have to capture these identity values as they are generated in the parent tables and use them in the mapping table. This can be done with the OUPUT
clause:
declare @ComputerID table ( ComputerID int);
insert dbo.Computer (ComputerName)
output INSERTED.ID
into @ComputerID
values (@ComputerName);
Do the same thing for monitors and use the local table variables to populate the mapping table.
Of course you want to have appropriate validation, duplicate checking and error handling in the body of the SP, too.
You don't say what scripting language you use. The documentation for it will tell you how to declare and populate stored procedure parameters for SQL Server.
Response to OP's EDIT #2:
First, a few tips. Please post the full error message; it helps immensely with debugging. Second, if you're using SSMS you can double-click an error and it will highlight the code in error. Third, get in the habit of closing your statements with a semicolon. It is not required yet but it will be soon.
If all computers have exactly one monitor then the TVP is not needed. You are correct. How many developers have only one monitor these days? I've seen finance traders' stations with eight. In these cases you do want a TVP. Please, please, please do not be tempted to write @Serial1, @Serial2, @Serial3 ...
.
Your code will throw the error Must declare the scalar variable "@MonitorId".
This is because of your third INSERT statement:
INSERT INTO dbo.Monitor_Computers (Monitor_Computer_Monitor, Monitor_Computer_Computer)
VALUES (@MonitorId, @ComputerId);
When you use the INSERT..VALUES
syntax SQL Server demands that there can be only one value per variable. You have provided table valued variables which could (potentially) hold many values. What you need is the INSERT..SELECT
syntax. Yes, I know @ComputerID
only has one row but it is a table and it could have many rows. What you need is:
INSERT INTO dbo.Monitor_Computers (Monitor_Computer_Monitor, Monitor_Computer_Computer)
SELECT m.Id, c.Id
from @ComputerId AS c
cross join @MonitorId AS m;
Best Answer
There are several mistakes.
1. When specifying parameters of procedure, you don't need to specify size. E.g. it should be
CREATE OR REPLACE PROCEDURE insert_toys(toy_id OUT NUMBER,toy_name VARCHAR )
, not.... VARCHAR(30))
2. You created sequence
CREATE SEQUENCE toy_seq
, but trying to use sequence with different nametoy_id := seq_toy.NEXTVAL;
(toy_seq vs seq_toy) 3. Parameter name is the same as field name (Toy_Name). Even though it's not a compilation error, qualifying variables is always much better compared to relying on resolution rules :The procedure takes 2 parameters, and should be called like that.