Must declare table variable error with c# paramaterized query - c#

I am getting an error with an SQL statement:
must declare the table variable #table
I don't know how I should fix it. Here is the code for the part that makes the query (for example 3 is Communications, 4 is Radio:basic, 5 is 45, 6 is 5, and 7 is 0).
string table = "Skills" + info[3];
SqlCommand cmd = new SqlCommand("use palladium; insert into #category(skill, basepercent, lvlpercent, special) values ( #skillname , #base , #lvl , #special);");
cmd.Parameters.AddWithValue("#category", table);
cmd.Parameters.AddWithValue("#skillname", info[4]);
cmd.Parameters.AddWithValue("#base", info[5]);
cmd.Parameters.AddWithValue("#lvl", info[6]);
cmd.Parameters.AddWithValue("#special", info[7]);
general.dbsend(cmd, connection);

You can't have a table name as a variable in a static query. You can in a dynamic query, by formatting the table name in the query rather than supplying it as a parameter.
SqlCommand cmd = new SqlCommand("use palladium; insert into ["+table.Replace("]","]]")+"](skill, basepercent, lvlpercent, special) values ( #skillname , #base , #lvl , #special);");
The replace bit guards against SQL Injection.

Related

passing string as a table type from c# code in to stored procedure

I want to pass a table as a string into stored procedure as a parameter and want to retrieve data from that particular table for e.g. I have made some tables which is year wised like purchase20162017,transfer20162017.
so problem is this i have created a dropdown box in which i have shown the years list like 2016-2017,2017-2018,2018-2019 user select the year from the dropdown box and click a button called generate ledger button. so i take a string from this dropdown box like this
string str = DropDownList1.SelectedItem.ToString();
str = str.Replace(#"-", "");
string purdtb = "purchase" + str;
now i have got exact table in purdtb but it is in string, i want to pass this string value as a parameter in stored procedure. Please tell me how to do this so that i can convert this string as a table name.
There are 2 ways.
A) The best one is:
string connectionString =
ConsoleApplication1.Properties.Settings.Default.ConnectionString;
//
// In a using statement, acquire the SqlConnection as a resource.
//
using (SqlConnection con = new SqlConnection(connectionString))
{
//
// Open the SqlConnection.
//
con.Open();
//
// The following code uses an SqlCommand based on the SqlConnection.
//
using (SqlCommand command = new SqlCommand("SELECT TOP 2 * FROM Dogs1", con))
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("{0} {1} {2}",
reader.GetInt32(0), reader.GetString(1), reader.GetString(2));
}
}
}
In Dogs1 put your table, use variables to modify the query via code. Why you want to put dynamic table names which is pretty awkward in you stored procedures when you can manage everything from the code with a variable?
B) The second way (not good but works):
Use in your stored procedure sp_executesql, pass the table names as parameters and build the query inside the stored procedure as shown in the documentation and catch the result.
This way you can build the query piece by piece and put in it everything you want (except for SQL reserved keyword, with which you should be careful anyway).
declare #SqlString nvarchar(2000)
declare #ParamDef nvarchar(2000)
set #SqlString = N'exec proc1 #param1, #param2, #param3'
set #ParamDef = N'#param1 bit, #param2 bit, #param3 bit'
EXECUTE sp_executesql #SqlString ,#ParamDef, #param1 = 0, #param2 = 1, #param3 = 1
To explain, sp_executesql works something like this
EXECUTE sp_executesql
N'proc1', -- SQL
N'#param1 bit, #param2 bit, #param3 bit', -- DECLARE
#param1 = 0, #param2 = 1, #param3 = 1 -- VALUES
Which gets translated to
EXECUTE sp_executesql
N'proc1', -- SQL
N'#param1 bit, #param2 bit, #param3 bit', -- DECLARE
#param1 = 0, #param2 = 1, #param3 = 1 -- VALUES
-- DECLARE
declare #param1 bit, #param2 bit, #param3 bit
-- VALUES
select #param1 = 0, #param2 = 1, #param3 = 1
-- SQL
proc1
3) Worst scenario:
Calling sp_executesql from C# code via Sqlcommand

SQL Server stored procedure that returns a boolean if table exists, c# implementation

I have created a stored procedure that takes a single argument, the name of a table, and returns 1 if it exists in the database, 0 if it does not. In SQL Server Management Studio testing my stored procedure works exactly as I'd like it to, however I'm having trouble getting that value for use in my C# program.
My options seem to be ExecuteScalar(), ExecuteNonQuery() or ExecuteReader(), none of which seem appropriate for the task, nor can I get them to even retrieve my stored procedure's result.
I have tried assigning my parameter with both cmd.Parameters.AddWithValue and cmd.Parameters.Add again to no avail.
Assuming you have a stored procedure like this which selects either a 0 (table does not exist) or 1 (table does exist)
CREATE PROCEDURE dbo.DoesTableExist (#TableName NVARCHAR(100))
AS
BEGIN
IF EXISTS (SELECT * FROM sys.tables WHERE Name = #TableName)
SELECT 1
ELSE
SELECT 0
END
then you can write this C# code to get the value - use .ExecuteScalar() since you're expecting only a single row, single column:
// set up connection and command
using (SqlConnection conn = new SqlConnection("your-connection-string-here"))
using (SqlCommand cmd = new SqlCommand("dbo.DoesTableExist", conn))
{
// define command to be stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// add parameter
cmd.Parameters.Add("#TableName", SqlDbType.NVarChar, 100).Value = "your-table-name-here";
// open connection, execute command, close connection
conn.Open();
int result = (int)cmd.ExecuteScalar();
conn.Close();
}
Now result will contain either a 0 if the table doesn't exist - or 1, if it does exist.
Use this:
var returnParameter = cmd.Parameters.Add("#ReturnVal", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
Your stored procedure should return 0 or 1.

Execute stored procedure with array-like string separated by comma [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Help with a sql search query using a comma delimitted parameter
I want to write a stored procedure that performs a select on a table and need one input variable of type varchar(max).
I'd like to send a bunch of values separated by , as the input parameter, e.g.
'Jack','Jane','Joe'
and then get the rows that contain one of these names.
In SQL the code would be
Select * from Personnel where Name in ('Jack','Joe','Jane');
Now I want to have a variable in my C# app, say strNames and fill it like
string strNames = "'Jack','Joe','Jane'";
and send this variable to the SP and execute it. Something like
Select * from Personnel where Name in (''Jack','Joe','Jane'') -- this is wrong
But how can I tell SQL Server to run such command?
I need to make this happen and I know it's possible, please give me the clue.
First of all, the single names don't need to be quoted when you pass them to the stored procedure.
using (SqlCommand cmd = new SqlCommand("MyStoredProc", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#longFilter", "Jack,Jill,Joe");
using (SqlDataReader reader = cmd.ExecuteReader())
{
...
}
}
Then, in the stored procedure, you can use simple text functions and a temporary table as follows to split up the string at the commas and an an entry to the temporary table for each part of the string:
DECLARE #temp AS TABLE (Name NVARCHAR(255))
IF ISNULL(#longFilter, '') <> ''
BEGIN
DECLARE #s NVARCHAR(max)
WHILE LEN(#longFilter) > 0
BEGIN
IF CHARINDEX(',', #longFilter) > 0
BEGIN
SET #s = LTRIM(RTRIM(SUBSTRING(#longFilter, 1, CHARINDEX(',', #longFilter) - 1)))
SET #longFilter = SUBSTRING(#longFilter, CHARINDEX(',', #longFilter) + 1, LEN(#longFilter))
END ELSE
BEGIN
SET #s = LTRIM(RTRIM(#longFilter))
SET #longFilter= ''
END
-- This was missing until 20140522
INSERT INTO #temp (Name) VALUES (#s)
END
END
Later use the following SELECT to get a list of all people the name of which is in #temp or all of them if #temp doesn't contain any rows (unfiltered result):
SELECT * FROM Personnel WHERE Name IN (SELECT Name FROM #temp) OR (SELECT COUNT(*) FROM #temp) = 0
You could use Table Valued Parameters.
Basically, you could insert a list of values as a parameter in the procedure, and use them as a table, something along the lines of
Select * from Personnel
where Name in (select name from #NamesTable).
Now, the specifics
To use table valued parameters, the type of the parameter must be predefined in sql server, using
create type NamesTable as table (Name varchar(50))
You can then use the defined type as a parameter in the procedure
create procedure getPersonnelList
#NamesTable NamesTable readonly
as
begin
select * from personnel
where Name in (select Name from #NamesTable)
end
You can see that in action, in this SQL Fiddle
On the C# side of things you need to create the parameter. If you have the names in a collection, and build the string, you can just use that to generate the parameter, and if they are a comma-separated string, a quick string.Split could take care of that. Since I do not know your specifics, I'll assume you have a List<string> called names. You'll need to convert that to a table valued parameter to be sent to the procedure, using something like:
DataTable tvparameter = new DataTable();
tvparameter.Columns.Add("Name", typeof(string));
foreach (string name in names)
{
tvparameter.Rows.Add(name);
}
You can find more info on how to generate a TVP in C# code in the SO Question..
Now you just need to send that parameter to the procedure, and that's that. Here is a complete console program that executes the procedure and outputs the results.
List<string> names = new List<string> { "Joe", "Jane", "Jack" };
using (SqlConnection cnn = new SqlConnection("..."))
{
cnn.Open();
using (SqlCommand cmd = new SqlCommand("getPersonnelList", cnn))
{
cmd.CommandType = CommandType.StoredProcedure;
DataTable tvparameter = new DataTable();
tvparameter.Columns.Add("Name", typeof(string));
foreach (string name in names)
{
tvparameter.Rows.Add(name);
}
cmd.Parameters.AddWithValue("#NamesTable", tvparameter);
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
Console.WriteLine("{0} - {1}", dr["ID"], dr["Name"]);
}
}
}
}
I guess you need Split Function in Sql Server to break Comma-Separated Strings into Table. Please refer these links.
Split Function in Sql Server to break Comma-Separated Strings into Table
SQL User Defined Function to Parse a Delimited String
You can select the data from table using
Select * from
Personnel where
Name in (select items from dbo.Split ('Jack,Joe,Jane',','))
You could simply check if Name is contained in the string. Note the commas at the start of the end to ensure you match the full name
string strNames = ",Jack,Joe,Jane,";
The the SQL becomes
select * from Personnel where PATINDEX('%,' + Name + ',%', #strNames) > 0
See http://www.sqlfiddle.com/#!3/8ee5a/1

How can i get the ##IDENTITY returned from a INSERT from MySQL (2008) using C# [duplicate]

Using C# in Visual Studio, I'm inserting a row into a table like this:
INSERT INTO foo (column_name)
VALUES ('bar')
I want to do something like this, but I don't know the correct syntax:
INSERT INTO foo (column_name)
VALUES ('bar')
RETURNING foo_id
This would return the foo_id column from the newly inserted row.
Furthermore, even if I find the correct syntax for this, I have another problem: I have SqlDataReader and SqlDataAdapter at my disposal. As far as I know, the former is for reading data, the second is for manipulating data. When inserting a row with a return statement, I am both manipulating and reading data, so I'm not sure what to use. Maybe there's something entirely different I should use for this?
SCOPE_IDENTITY returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, two statements are in the same scope if they are in the same stored procedure, function, or batch.
You can use SqlCommand.ExecuteScalar to execute the insert command and retrieve the new ID in one query.
using (var con = new SqlConnection(ConnectionString)) {
int newID;
var cmd = "INSERT INTO foo (column_name)VALUES (#Value);SELECT CAST(scope_identity() AS int)";
using (var insertCommand = new SqlCommand(cmd, con)) {
insertCommand.Parameters.AddWithValue("#Value", "bar");
con.Open();
newID = (int)insertCommand.ExecuteScalar();
}
}
try this:
INSERT INTO foo (column_name)
OUTPUT INSERTED.column_name,column_name,...
VALUES ('bar')
OUTPUT can return a result set (among other things), see: OUTPUT Clause (Transact-SQL). Also, if you insert multiple values (INSERT SELECT) this method will return one row per inserted row, where other methods will only return info on the last row.
working example:
declare #YourTable table (YourID int identity(1,1), YourCol1 varchar(5))
INSERT INTO #YourTable (YourCol1)
OUTPUT INSERTED.YourID
VALUES ('Bar')
OUTPUT:
YourID
-----------
1
(1 row(s) affected)
I think you can use ##IDENTITY for this, but I think there's some special rules/restrictions around it?
using (var con = new SqlConnection("connection string"))
{
con.Open();
string query = "INSERT INTO table (column) VALUES (#value)";
var command = new SqlCommand(query, con);
command.Parameters.Add("#value", value);
command.ExecuteNonQuery();
command.Parameters.Clear();
command.CommandText = "SELECT ##IDENTITY";
int identity = Convert.ToInt32(command.ExecuteScalar());
}

Build SQL query string using user input

I have to make a string by using the values which the user selects on the webpage,
Suppose I need to display files for multiple machines with different search criteria...
I currently use this code:
DataTable dt = new DataTable();
SqlConnection connection = new SqlConnection();
connection.ConnectionString = ConfigurationManager
.ConnectionStrings["DBConnectionString"].ConnectionString;
connection.Open();
SqlCommand sqlCmd = new SqlCommand
("SELECT FileID FROM Files
WHERE MachineID=#machineID and date= #date", connection);
SqlDataAdapter sqlDa = new SqlDataAdapter(sqlCmd);
sqlCmd.Parameters.AddWithValue("#machineID", machineID);
sqlCmd.Parameters.AddWithValue("#date", date);
sqlDa.Fill(dt);
Now this is a fixed query where the user just has one machine and just selects one date...
I want to make a query in which the user has multiple search options like type or size if he/she wants depending on what he/she selects.
Also if he/she can select multiple machines...
SELECT FileID FROM Files
WHERE (MachineID=#machineID1 or MachineID = #machineID2...)
AND (date= #date and size=#size and type=#type... )
All of this happens at runtime... otherwise I have to create a for loop to put multiple machines one by one... and have multiple queries depending on the case the user selected...
This is quite interesting and I could use some help...
If you are going to do this via dynamic SQL, you need to build a call to the IN function. (e.g. In(id1, id2, id3...)
private string GetSql( IList<int> machineIds )
{
var sql = new StringBuilder( "SELECT FileID FROM Files Where MachineID In(" );
for( var i = 0; i < machineIds.Count; i++ )
{
if ( i > 0 )
sql.Append(", ")
sql.Append("#MachineId{0}", i);
}
sql.Append(" ) ");
//additional parameters to query
sql.AppendLine(" And Col1 = #Col1" );
sql.AppendLine(" And Col2 = #Col2 ");
...
return sql.ToString();
}
private DataTable GetData( IList<int> machineIds, string col1, int col2... )
{
var dt = new DataTable();
var sql = GetSql( machineIds );
using ( var conn = new SqlConnection() )
{
conn.ConnectionString = ConfigurationManager.ConnectionStrings["DBConnectionString"].ConnectionString;
using ( var cmd = new SqlCommand( sql, conn ) )
{
conn.Open();
for( var i = 0; i < machineIds.Count; i++ )
{
var parameterName = string.Format("#MachineId{0}", i );
cmd.Parameters.AddWithValue( parameterName, machineIds[i] );
}
cmd.Parameters.AddWithValue( "#Col1", col1 );
cmd.Parameters.AddWithValue( "#Col2", col2 );
...
using ( var da = new SqlDataAdapter( cmd ) )
{
da.Fill( dt );
}
}
}
return dt;
}
You can use WHERE MachineID IN ('Machine1', 'Machine2', 'Machine3', ... 'MachineN')
Then in your loop you would just add the 1..n machines. The IN clause works with 1 element or n elements, so it should be fine.
However, I'd look at using a stored procedure to do it rather than hardcoding the SQL into your application.
Build a real table and load the machine ids into it.
Then your SQL would be:
where MachineID in ( select MachineID from userMachine where userID = x)
When you are done, remove all rows for the userID:
delete from userMachine where userID = x.
Normally when I want to create a "search" type query, I use optional parameters. This allows me to send something or nothing to the parameter, making the query go from vague to very specific.
Example:
SELECT
COL1,
COL2,
COL3
FROM TABLE
WHERE (#COL1 IS NULL OR #COL1 = '' OR #COL1 = COL1)
As you'll notice above, if you pass in NULL or BLANK it won't add the parameter to the query. If you do enter a value, then it'll be used in the comparison.
Ideally you are trying to arrive at a solution similar to creating "MachineID in (1, 2, 3, 4)" dynamically.
Option 1
There are many ways to complete this task from passing in a comma separated string into the stored proc and dynamically build the sql string and then calling "EXEC sp_executesql #sql"
WHERE IN (array of IDs)
Option 2
You can pass in a string of comma separated values and then parse out the values into their own temp-table and then join on to it
http://vyaskn.tripod.com/passing_arrays_to_stored_procedures.htm
Option 3 - my choice
You can now pass in the array of values using XML and then select the array items easily.
http://support.microsoft.com/kb/555266
.
I would also recommend using a stored procedure because otherwise you will leave yourself open to an SQL injection attack - especially where you are building up a string based on user input.
Something like:
a' or 1=1; -- Do bad things
You can use sp_executesql in SQL to run an SQL statement that is built up with a where clause like #dcp suggests and although it wouldn't optimize well it is probably a quick command to run anyway.
SQL Injection attacks by example
One way to achieve this would be using charindex. This example demonstrates how a stored procedure could be run when passed a space separated list of ids:
declare #machine table (machineId int, machineName varchar(20))
declare #files table (fileId int, machineId int)
insert into #machine (machineId, machineName) values (1, 'machine')
insert into #machine (machineId, machineName) values (2, 'machine 2.0')
insert into #machine (machineId, machineName) values (3, 'third machine')
insert into #machine (machineId, machineName) values (4, 'machine goes forth')
insert into #machine (machineId, machineName) values (5, 'machine V')
insert into #files (fileId, machineId) values (1, 3)
insert into #files (fileId, machineId) values (2, 3)
insert into #files (fileId, machineId) values (3, 2)
insert into #files (fileId, machineId) values (4, 1)
insert into #files (fileId, machineId) values (5, 3)
insert into #files (fileId, machineId) values (6, 5)
declare #machineText1 varchar(100)
declare #machineText2 varchar(100)
declare #machineText3 varchar(100)
set #machineText1 = '1 3 4'
set #machineText2 = '1'
set #machineText3 = '5 6'
select * from #files where charindex(rtrim(machineId), #machineText1, 1) > 0
-- returns files 1, 2, 4 and 5
select * from #files where charindex(rtrim(machineId), #machineText2, 1) > 0
-- returns file 4
select * from #files where charindex(rtrim(machineId), #machineText3, 1) > 0
--returns file 6
So you can create this stored procedure to achieve your aim:
create procedure FilesForMachines (#machineIds varchar(1000))
as
select * from [Files] where charindex(rtrim(machineId), #machineIds, 1) > 0
The charindex tip is from BugSplat.

Categories