I have a stored procedure that I need to pass multiple parameters in SQL Server 2012. My application will build a report for all employees or certain employees. I have a check list box for the employees if the user wants to choose certain employees instead of all of them. I want to use those selected employees in the where clause in the stored procedure. I've read in SQL Server 2012 you can pass a table as a parameter with multiple values. I can't seem to find a good example to fit my situation. Any help would be greatly appreciated.
Let's say you are passing EmployeeIDs, and they are integers. First, create a table type in the database:
CREATE TYPE dbo.EmployeesTVP AS TABLE(EmployeeID INT PRIMARY KEY);
Now your stored procedure can say:
CREATE PROCEDURE dbo.GetEmployees
#empTVP dbo.EmployeesTVP READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT EmployeeID, Name FROM dbo.Employees AS e
WHERE EXISTS (SELECT 1 FROM #empTVP WHERE EmployeeID = e.EmployeeID);
END
GO
And if you want to handle the all scenario, you can say:
IF EXISTS (SELECT 1 FROM #empTVP)
SELECT EmployeeID, Name FROM dbo.Employees AS e
WHERE EXISTS (SELECT 1 FROM #empTVP WHERE EmployeeID = e.EmployeeID);
ELSE
SELECT EmployeeID, Name FROM dbo.Employees;
(You could combine these with an OR conditional, but I tend to find this just gives the optimizer fits.)
Then you create a DataTable in your C# code from the checkboxes, and pass the parameter to your stored procedure as a parameter type of Structured. You can fill in the rest but:
DataTable tvp = new DataTable();
// define / populate DataTable from checkboxes
using (connectionObject)
{
SqlCommand cmd = new SqlCommand("dbo.GetEmployees", connectionObject);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvparam = cmd.Parameters.AddWithValue("#empTVP", tvp);
tvparam.SqlDbType = SqlDbType.Structured;
// ... execute the cmd, grab a reader, etc.
}
Complementary to the Aaron response:
1/ Send your data packed as XML and use an XML data type for your parameter
or
2/ Join your employee IDs values into a single string, comma separated, then split those values.
Related
tried to insert many records of a data table to SQL table by stored procedure.I need insert data table's records all together.
I'm using C# programming language and i want to send many records to SQL server by ADO.net stored procedure all together. i want to know about data table types and use it if that helps me.
To pass many rows efficiently to a stored procedure, use a table-valued parameter. The C# app can specify parameter type structured and a value of a data table. For maximum performance, make sure the DataTable column types match the server-side table type column types (including max length for string columns).
Below is an example excerpt from the documentation link above:
// Assumes connection is an open SqlConnection object.
using (connection)
{
// Create a DataTable with the modified rows.
DataTable addedCategories = CategoriesDataTable.GetChanges(DataRowState.Added);
// Configure the SqlCommand and SqlParameter.
SqlCommand insertCommand = new SqlCommand("usp_InsertCategories", connection);
insertCommand.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam = insertCommand.Parameters.AddWithValue("#tvpNewCategories", addedCategories);
tvpParam.SqlDbType = SqlDbType.Structured;
// Execute the command.
insertCommand.ExecuteNonQuery();
}
Here are T-SQL snippets to create the table type and proc.
CREATE TYPE dbo.CategoryTableType AS TABLE
( CategoryID int, CategoryName nvarchar(50) );
GO
CREATE PROC dbo.usp_InsertCategories
AS
#tvpNewCategories dbo.CategoryTableType READONLY
INSERT INTO dbo.Categories (CategoryID, CategoryName)
SELECT nc.CategoryID, nc.CategoryName FROM #tvpNewCategories AS nc;
GO
Even for trivial inserts, TVP performance can provide performance similar to SqlBulkCopy (many thousands per second) with the advantage of a stored procedure interface.
I have ssis package in that I'm taking values from flat file and insert it into table.
I have taken one Execute SQL Task in that creating one temptable
CREATE TABLE [tempdb].dbo.##temptable
(
date datetime,
companyname nvarchar(50),
price decimal(10,0),
PortfolioId int,
stype nvarchar(50)
)
Insert into [tempdb].dbo.##temptable (date,companyname,price,PortfolioId,stype)
SELECT date,companyname,price,PortfolioId,stype
FROM ProgressNAV
WHERE (Date = '2011-09-30') AND (PortfolioId = 5) AND (stype in ('Index'))
ORDER BY CompanyName
Now in above query I need to pass (Date = '2011-09-30') AND (PortfolioId = 5) AND (stype in ('Index'))
these 3 parameter using variable name I have created variables in package so that I become dynamic.
In your Execute SQL Task, make sure SQLSourceType is set to Direct Input, then your SQL Statement is the name of the stored proc, with questionmarks for each paramter of the proc, like so:
Click the parameter mapping in the left column and add each paramter from your stored proc and map it to your SSIS variable:
Now when this task runs it will pass the SSIS variables to the stored proc.
The EXCEL and OLED DB connection managers use the parameter names 0 and 1.
I was using a oledb connection and wasted couple of hours trying to figure out the reason why the query was not working or taking the parameters. the above explanation helped a lot
Thanks a lot.
Along with #PaulStock's answer, Depending on your connection type, your variable names and SQLStatement/SQLStatementSource Changes
https://learn.microsoft.com/en-us/sql/integration-services/control-flow/execute-sql-task
SELECT, INSERT, UPDATE, and DELETE commands frequently include WHERE clauses to specify filters that define the conditions each row in the source tables must meet to qualify for an SQL command. Parameters provide the filter values in the WHERE clauses.
You can use parameter markers to dynamically provide parameter values. The rules for which parameter markers and parameter names can be used in the SQL statement depend on the type of connection manager that the Execute SQL uses.
The following table lists examples of the SELECT command by connection manager type. The INSERT, UPDATE, and DELETE statements are similar. The examples use SELECT to return products from the Product table in AdventureWorks2012 that have a ProductID greater than and less than the values specified by two parameters.
EXCEL, ODBC, and OLEDB
SELECT* FROM Production.Product WHERE ProductId > ? AND ProductID < ?
ADO
SELECT * FROM Production.Product WHERE ProductId > ? AND ProductID < ?
ADO.NET
SELECT* FROM Production.Product WHERE ProductId > #parmMinProductID
AND ProductID < #parmMaxProductID
The examples would require parameters that have the following names:
The EXCEL and OLED DB connection managers use the parameter names 0 and 1. The ODBC connection type uses 1 and 2.
The ADO connection type could use any two parameter names, such as Param1 and Param2, but the parameters must be mapped by their ordinal position in the parameter list.
The ADO.NET connection type uses the parameter names #parmMinProductID and #parmMaxProductID.
A little late to the party, but this is how I did it for an insert:
DECLARE #ManagerID AS Varchar (25) = 'NA'
DECLARE #ManagerEmail AS Varchar (50) = 'NA'
Declare #RecordCount AS int = 0
SET #ManagerID = ?
SET #ManagerEmail = ?
SET #RecordCount = ?
INSERT INTO...
I want to create a report in MS SQL Server BIDS (SSMS and Visual Studio). The user would enter a list of email addresses as a parameter. So #pEmails would be 'foo#bluh.com', 'bar#meh.org', etc. These email addresses may or may not be in a table.
I can simply do:
and Table.Email in (#pEmails)
and that works, except I need to return the email address if it's NOT found as well. So the results would be something like:
|email |found in table|
|------------|--------------|
|foo#bluh.com| Y |
|bar#meh.org | N |
I was thinking I could take the list of values entered as the #pEmails parameter and create a temp table with them, which I could then left join with, but my attempts to do so have not worked out.
declare #pEmails table (EmailAddress varchar(255));
insert into #pEmails values (#ReportParameter1);
select
*
from
#pEmails
The above works if only a single value is put into #ReportParameter1, but not if multiples are in it.
I am using SQL Server 2008. Any suggestions on how best to proceed?
As has been stated, you need some kind of split function, for analysis on the performance of various methods Split strings the right way – or the next best way is an excellent read. Once you have your function, you then need to define your query parameter as a string, rather than a table:
So your query would actually become:
DECLARE #pEmails TABLE (EmailAddress varchar(255));
INSERT #pEmails (EmailAddress)
SELECT Value
FROM dbo.Split(#pEmallString);
Then go to your dataset properties, and instead of passing the multivalue parameter #pEmails to the dataset, instead create a new one #pEmailString, and set the value as an expression, which should be:
=Join(Parameters!pEmails.Value, ",")
This turns your multivalue parameter into a single comma delimited string. It seems pretty backwards that you need to convert it to a delimited string, only to then split it in SQL, unfortunately I don't know of a better way.
Here are some learnings on this topic (standing on the shoulders of the information elsewhere in this thread).
Set a parameter (select 'multiple values' checkbox):
InputList
Establish dataset query:
SELECT *
INTO #InputTemp
FROM STRING_SPLIT(#InputListJoin, ',')
SELECT value as ValueName
FROM #InputTemp T2
WHERE NOT EXISTS (
SELECT MyValue
FROM MyTable T1
WHERE T1.MyValue = T2.value
)
Establish dataset parameters:
Name: #InputList | Value: [#InputList]
Name: #InputListJoin | Value(expression): =Join(Parameters!InputList.Value,",")
The element names can be changed as needed.
Somewhat on topic, other details that might be helpful:
[#InputList.IsMultiValue] --> true/false whether your parameter is multi-value (not whether there are multiple values)
[#InputList.Count] --> count of items in input list (excludes blank lines)
=Parameters!InputList.Value(2) --> return third value from list (counting from zero)
OK this seems like it should be insanely easy, but I cannot figure it out. Every where I look online says to create temp tables and VB scripts and I cannot believe I have to do that. My goal is to insert all the records in a table with a date later than the max date in that destination table.
UPDATE The 2 tables are in two different non linked SQL databases
So:
Select #[User::Dated] = MAX(Dateof) from Table2
Insert into Table2
Select *
From Table1
Where DateOf > #[User::Dated]
I am trying to do this in SSIS. I declared a variable, the SQL execution step looks like it is assigning the single row output to it. But when I got go into the data flow it give me no parameters to choose, when I force the known parameter which is in the project scope it says no parameter exists
Create two OLE DB data sources each pointing at you two databases.
Create a variable called max_date and make its data type String.
Place an Execute SQL Task on the Control Flow, change its connection type to OLE DB and for the connection select the name of the data source that contains Table2. Set the ResultSet to Single Row. Add the following for the SQLStatement:
SELECT CAST(MAX(Dateof) AS VARCHAR) AS max_date FROM Table2
Go to the Result Set pane, click Add and enter the following:
Result Name: max_date
Variable Name: User::max_date
You can now use the max_date variable in an expression to create a SQL statement, for example you could use it in another Execute SQL Task which would use the second Data Connection like so:
"INSERT INTO Table2
SELECT *
FROM Table1
WHERE DateOf > '" + #[User::max_date] + "'"
Or in an OLE DB Source in a data flow like so:
"SELECT *
FROM Table1
WHERE DateOf > '" + #[User::max_date] + "'"
You can do this in a single SQL Task if you want:
Insert into Table2
Select *
From Table1
Where DateOf > (Select MAX(Dateof) from Table2)
If you want to use multiple Execute SQL Task items in the control flow, or want to make use of the parameter in a data flow instead, you have to change the General > Result Set option for your MAX() query to Single Row, then move from General to Result Set and Add a new variable for your result set to occupy.
To use that variable in your INSERT INTO.... query via Execute SQL Task, you'll construct your query with a ? for each parameter and map them in the parameter mapping section. If a variable is used multiple times in a query it's easiest to use a stored procedure, so you can simply pass the relevant parameters in SSIS.
I have a bunch of employee names which need to be inserted in a table.
Should I represent my data like this and use OpenXML to insert into the database:-
<Employees>
<Employee>
Emp1
</Employee>
<Employee>
Emp2
</Employee>
<Employee>
Emp2
</Employee>
</Employees>
OR
I should represent the Employee like Emp1,Emp2,Emp3, split the string, add to a table variable and then insert into the database table.
Are there any performance difference between the two approaches. Please note that this is very simple structure without any nesting of employees in the XML and without more than one delimiter in the string. This XML is also not going to be used as schema or anything. Would OpenXML be the overkill? Could anybody give some direction on this?
Using the SQL Server XQuery support, you can easily shred the XML into bits:
INSERT INTO dbo.Employees(EmployeeName)
SELECT
Data.Emp.value('(.)[1]', 'varchar(100)')
FROM
#Input.nodes('/Employees/Employee') AS Data(Emp)
The same cannot be said of CSV files - so I would vote for the XML approach.
The biggest problems with using Table-Valued Parameters are:
1) You cannot modify a User Defined Table Type. You need to drop and recreate.
2) You cannot drop a User Defined Table Type if it is being referenced by another object i.e. stored proc or function
Given this, if your UDTT is used in multiple Stored Procs or functions, you need to drop them all, drop the UDTT, recreate the UDTT and then recreate all your SP's.
Well depending on what version of SQL you are using there is a third option that is very easy. If you are using SQL 2008 then you can create a user defined table type.
CREATE TYPE [dbo].[IntegerList] AS TABLE(
[n] [VARCHAR(100)] NOT NULL,
PRIMARY KEY CLUSTERED
(
[n] ASC
)WITH (IGNORE_DUP_KEY = OFF)
)
GO
I used this with integers but I don't see why you couldn't just change the type to be varchar. Then you use it like this:
CREATE PROCEDURE [dbo].[CheckIds]
#Ids IntegerList READONLY
AS
BEGIN
SELECT *
FROM [dbo].[Table]
WHERE [Id] IN (SELECT n FROM #Ids)
END
Then in your .net code you set it up like so:
int[] Ids = <You id array>
var IdList = new List<SqlDataRecord>();
SqlMetaData[] tvp_definition = { new SqlMetaData("n", SqlDbType.Int) };
foreach (int Id in Ids)
{
var rec = new SqlDataRecord(tvp_definition);
rec.SetInt32(0, Id);
IdList.Add(rec);
}
Then pass it in as param like usual to stored proc call except you make some minor changes:
sqlCommand.Parameters.Add(new SqlParameter { ParameterName = "#Ids", SqlDbType = SqlDbType.Structured, TypeName = "IntegerList", Value = IdList });
Might seem like a lot but it is actually not. It is really clean and no unnecessary code of parsing xml or strings.
I can't remember where I originally found this code. I thought it might have been on this site but when searching I couldn't find it. So if someone finds it I will gladly give credit to whomever it belongs to. Just wanted to share because I was not familiar with this in 2008 and felt it was a very clean approach.