Microsoft SQL Server, selecting an id and inserting into another table - c#

I have this code for inserting some rows into the following table - and i am getting the id from another table that is already populated. Using Compact 4.0 Local Database Here with C# and Razor.
get id from sortedWord table:
Id | SortedWord
0 act
insert data into words table:
Id | Word | SortedId
0 cat 0
1 tac 0
for (var i = 0; i < words.Count(); i++){
queryString = "SELECT Id FROM SortedWords WHERE SortedWord = #0";
var sortedId = db.QuerySingle(queryString, sortWord(words[i]));
queryString = "INSERT INTO Words (Word, SortedId) VALUES (#0, #1)";
db.Query(queryString, words[i], sortedId.Id);
}
Trouble is the select statement is inefficient, is it possible to do this without a select statement, something like select into: I saw some examples here but cant make sense of it.
http://technet.microsoft.com/en-us/library/ms189872(v=sql.105).aspx

Yes you can simply use:
INSERT Words (Word, SortedID)
SELECT #0, ID
FROM SortedWords
WHERE SortedWord = #1;
Then your c# would become:
queryString = " INSERT Words (Word, SortedID) SELECT #0, ID FROM SortedWords WHERE SortedWord = #1;"
db.Query(queryString, words[i], sortedwords[i]);
Or you could just embed your first query into your second:
queryString = "INSERT INTO Words (Word, SortedId) VALUES (#0, (SELECT TOP 1 Id FROM SortedWords WHERE SortedWord = #1))";
db.Query(queryString, words[i], sortWord(words[i]));
However, if you are using SQL Server 2008 or later, I would go one further and do all the inserts at once using a Table valued parameters. The first step would be to create the type:
CREATE TYPE dbo.TwoStringList AS TABLE (Value1 VARCHAR(MAX), Value2 VARCHAR(MAX));
I have used a generic name so the type is more reusable. You can then create a procedure that accepts this type as a parameter:
CREATE PROCEDURE dbo.InsertWords #StringList dbo.TwoStringList READONLY
AS
INSERT Words (Word, SortedID)
SELECT sl.Value1, sw.ID
FROM SortedWords sw
INNER JOIN #StringList sl
ON sw.SortedWord = sl.Value2;
Then you can pass this to an SQL command with something like this:
var datatable = new DataTable();
datatable.Columns.Add(new DataColumn("Value1", typeof(string)));
datatable.Columns.Add(new DataColumn("Value2", typeof(string)));
for (var i = 0; i < words.Count(); i++)
{
var dr = datatable.NewRow();
dr[0] = words[i];
dr[1] = sortedwords[i];
datatable.Rows.Add(dr);
}
using (var connection = new SqlConnection("your Connection String"))
using (var command = new SqlCommand("dbo.InsertWords", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter{
ParameterName = "#StringList",
SqlDbType = SqlDbType.Structured,
TypeName = "dbo.TwoStringList",
Value = datatable
});
connection.Open();
command.ExecuteNonQuery();
}
Example on SQL Fiddle

I'm not very familiar with C# syntax, but you can do something like this:
INSERT INTO Words (Word, SortedId)
SELECT #0,id
FROM sortedWords
WHERE sortedWord = #0;

Related

C# & SQL Server : parse Excel range into data table but data table returning as empty

I am trying to save values from an excel range to a data table. I am then parsing my data table into a stored procedure to save into the database.
System.Data.DataTable dt = new System.Data.DataTable();
dt.Columns.Add("Territory", typeof(string));
dt.Columns.Add("OptionID", typeof(string));
dt.Columns.Add("ProductClass", typeof(string));
dt.Columns.Add("Taken", typeof(string));
for (var idx = 7; idx < lastUsedRow; idx++)
{
string Territory = territory;
string optionID = ((string)Worksheet.Range["B" + idx].Value2);
string ProductClass = ((string)Worksheet.Range["C" + idx].Value2);
string Taken = ((string)Worksheet.Range["V" + idx].Value2);
}
dt.Rows.Add(optionID, Territory, ProductClass, Taken) //this is an issue it returns dt{}
SQL:
var connectionstring2 = ConfigurationManager.ConnectionStrings["SDYConString"].ConnectionString;
var cn2 = new SqlConnection(connectionstring2);
cn2.Open();
var cmd2 = new SqlCommand("dbo.sp_ui_sm_insertrows", cn2);
SqlParameter tvparam = cmd2.Parameters.AddWithValue("#dt", dt);
var da2 = new SqlDataAdapter(cmd2);
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.ExecuteNonQuery();
cn2.Close();
SQL:
CREATE PROCEDURE [dbo].[sp_ui_sm_insertrows]
#dt AS tbl_type_lines READONLY
AS
BEGIN
INSERT INTO dbo.tbl_newline (TERRITORY, OPTION_ID, PRODUCT_CLASS, TAKEN)
SELECT
TERRITORY, OPTION_ID, PRODUCT_CLASS, TAKEN
FROM #dt;
END
I get this error:
Cannot insert the value NULL into column 'OPTION_ID', table '#dt'; column does not allow nulls. INSERT fails. The data for table-valued parameter "#dt" doesn't conform to the table type of the parameter.
SQL Server error is: 515, state: 2 The statement has been terminated.
The dt {} seems to be empty so it doesn't insert anything in it. When I go into debug mode there is a value in option ID variable - is this the correct way to insert the rows into the datatable? I need to map the created columns and rows together then parse into my stored procedure.

How to query from SQL comparing with array members?

So I have an array containing computer names called hostnames[].
Contains: compname1, compname2, compname3 etc.
Actually it gets it's members from another SQL query.
I have a data table and I need to query all rows where hostname column has any of the computer names in my array.
something like:
select * from table where hostname in hostnames[]
How should I proceed to achieve my goal?
EDIT:
I was thinking on the below:
string temp = "'" + hostnames[0] + "'";
for(int i=1; i<hostnames[].Lenght; i++)
{
temp = temp + ",'" + hostnames[i] + "'";
}
string query = "SELECT * FROM table WHERE hostname IN (" + temp + ")";
The best way to use parameters for an IN clause is to use Table-valued parameters, in your case you will need a type as a table with one nvarchar column. I've used a generic name so the type can be reused without confusion:
CREATE TYPE dbo.StringList AS TABLE (value NVARCHAR(MAX));
Then it is simply a case of adding your values to a DataTable and passing this as a parameter to your select command:
var dataTable = new DataTable();
dataTable.Columns.Add(new DataColumn("Value", typeof(string)));
for (int i = 0; i < hostnames.Length; i++)
{
var dr = dataTable.NewRow();
dr[0] = "";
dataTable.Rows.Add(dr);
}
using (var connection = new SqlConnection("connectionString"))
using (var command = new SqlCommand("SELECT * FROM Table WHERE HostName IN (SELECT Value FROM #StringList)", connection))
{
SqlParameter stringListParameter = new SqlParameter("#StringList", SqlDbType.Structured);
stringListParameter.Value = dataTable;
stringListParameter.TypeName = "dbo.StringList";
command.Parameters.Add(stringListParameter);
// OPEN CONNECTION EXECUTE COMMAND ETC
}
There are 4 ways to achieve what you want. You choose what works best for you.
Use IN as in your code
Break down to OR. A IN (1, 2, 3) => A=1 OR A=2 OR A=3
Use Table Valued Parameters
User the sql query passed earlier. ex: "SELECT * FROM table WHERE hostname IN (Select hostname from tableusedEarlier)"

How to get table name from string that contain SELECT statement

i have a string that contain a sql command,
something like this:
strCommand = "Select [Feild1],
[Feild2]
From TableName
Order By [Feild1] desc" ;
How can find table name in this string?
The solutions so far have all gone with the searching within strings approach. You've not mentioned if your SQL queries will always look similar, but there are many variants of a query to include which these solutions will break on. Consider...
SELECT Field1, Field2 FROM TableName
SELECT Field1, Field2 FROM [TableName]
SELECT Field1, Field2 FROM dbo.TableName
SELECT Field1, Field2 FROM Table1Name, Table2Name
If the query you're trying to parse is one you have the database for, you can get SQL server to do the hard work of parsing the query for you, instead of trying to account for all the cases in SQL. You can execute a query using SET SHOWPLAN_ALL ON, which will produce a table of the query plan. You can then analyse the Arguments column, which contains all of the fields the query will involve in a standard format. An example program is below:
SqlConnection conn = new SqlConnection(CONNECTIONSTRING);
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "SET SHOWPLAN_ALL ON";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT [Field1], [Field2] FROM [TableName]";
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
Regex objectRegex = new Regex(#"^OBJECT:\(\[(?<database>[^\]]+)\]\.\[(?<schema>[^\]]+)\]\.\[(?<table>[^\]]+)\]\.\[(?<field>[^\]]+)\]\)$", RegexOptions.ExplicitCapture);
List<string> lstTables = new List<string>();
foreach (DataRow row in dt.Rows)
{
string argument = row["Argument"].ToString();
if (!String.IsNullOrEmpty(argument))
{
Match m = objectRegex.Match(argument);
if (m.Success)
{
string table = m.Groups["schema"] + "." + m.Groups["table"];
if (!lstTables.Contains(table))
{
lstTables.Add(table);
}
}
}
}
Console.WriteLine("Query uses the following tables: " + String.Join(", ", lstTables));
This will deal with all forms of query name and return all tables which are involved in the query, no matter how they are included.
If this is the same pattern all of the time then:
string tableName = strCommand.Split(' ', strCommand)[4];
but if you can add / remove fields just iterate through the splitted string and search for "From", and the next string will be your table name
I would say- what is after "From" as a more reliable way of getting the table name. Loop through the array created, when you reach "From", the next one is the table.
This is the Method which gives us tablename just change the SQL query string, connection String
Works with simple query, joins too
public static List<string> getTablenames(string connString, string QueryString)
{
SqlConnection con = new SqlConnection(connString);
con.Open();
DataTable dt = con.GetSchema("Tables");
List<string> getTableName = new List<string>();
List<string> tablenames = new List<string>();
foreach (DataRow dr in dt.Rows)
tablenames.Add(dr[2].ToString());
for (int i = 0; i < dt.Rows.Count; i++)
{
string myTable = tablenames[i];
Boolean checkMyTable = QueryString.Contains(myTable);
if (checkMyTable == true)
getTableName.Add(myTable);
}
con.Close();
return getTableName;
}
You can use the substring (This way it does not matter how many column you have to select)
string table = strCommand.ToLower().Substring(strCommand.IndexOf("FROM".ToLower())).Split(' ')[0];
ISun's answer met my needs but one change is required to get the table name:
string table = strCommand.ToLower().Substring(strCommand.IndexOf("FROM".ToLower())).Split(' ')[1];
not
string table = strCommand.ToLower().Substring(strCommand.IndexOf("FROM".ToLower())).Split(' ')[0];
If you want a solution in SQL, try this
declare #q varchar(1000) = 'Select [Feild1], [Feild2] From TableName Order By [Feild1] desc',
#tableName varchar(100) = '',
#temp varchar(1000),
#temp2 char(1)
declare #frmIndex int = CHARINDEX('From', #q, 0);
declare #flag int = 0, #counter int = 1;
select #temp = SUBSTRING(#q, #frmIndex, len(#q))
set #temp = LTRIM(REPLACE(#temp,'From',''))
while(#flag <> 1)
begin
set #temp2 = SUBSTRING(#temp, #counter, 1)
if(#temp2 = ' ')
set #flag = 1
select #tableName = #tableName + #temp2
set #counter = #counter + 1
end
select #tableName as TableName

update sql statement with unknown name/amount of params

I have a classic ASP site, that I am slowly upgrading. I would like to create a function to securely update a SQL database without specifying parameters manually. Something just a tad more dynamic.
(I do not want to use entity framework or Linq)
Here is the code so far:
string updateSql = "UPDATE sometable" + "SET test1= #testData1 " + "WHERE a = #aData1";
SqlCommand UpdateCmd = new SqlCommand(updateSql, conn);
UpdateCmd.Parameters.Add("#testData1 ", SqlDbType.NVarChar, 10, "testData1 ");
UpdateCmd.Parameters.Add("#aData1", SqlDbType.NVarChar, 20, "aData1");
UpdateCmd.Parameters["#testData1 "].Value = "21515";
UpdateCmd.Parameters["#aData1"].Value = "32t3t";
UpdateCmd.ExecuteNonQuery();
pseudo-code (what I would like to achieve)
Create an Ilist covering all variables {get; set:} [validate type/length here]
For every variable that contains a value (without validation issues) create sql update string.
Execute it.
Possible problem:
The only problem I can foresee, is that the list may have 500 variables, but each SQL update may only have only 2 or 3 columns being updated. Is this not efficient?
you need to do something like this....needs more coding obviously....
static void Main(string[] args)
{
var values = new Dictionary<string, object>( );
values.Add( "name", "timmerz" );
values.Add( "dob", DateTime.Now );
values.Add( "sex", "m" );
SqlUpdate( "sometable", values );
}
public static void SqlUpdate( string table, Dictionary<string,object> values, string where )
{
var equals = new List<string>( );
var parameters = new List<SqlParameter>( );
var i = 0;
foreach( var item in values )
{
var pn = "#sp" + i.ToString( );
equals.Add( string.Format( "{0}={1}", item.Key, pn ) );
parameters.Add( new SqlParameter( pn, item.Value ) );
i++;
}
string command = string.Format( "update {0} set {1} where {2}", table, string.Join( ", ", equals.ToArray( ) ), where );
var sqlcommand = new SqlCommand(command);
sqlcommand.Parameters.AddRange(parameters.ToArray( ) );
sqlcommand.ExecuteNonQuery( );
}
I'm not sure I fully understand what you're trying to do, but this might be close to what you're looking for. You can create an arbitrarily long list of parameters and respective values, then build the corresponding UPDATE dynamically from that list.
//set up SqlCommand
SqlCommand UpdateCmd = new SqlCommand();
UpdateCmd.Connection = conn;
//build your dictionary (probably happens elsewhere in your code)
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("col1", "col1 value");
parameters.Add("col2", 42);
parameters.Add("col3", DateTime.Now);
//build a command string and add parameter values to your SqlCommand
StringBuilder builder = new StringBuilder("UPDATE sometable SET ");
foreach(KeyValuePair<string, object> parameter in parameters) {
builder.Append(parameter.Key).Append(" = #").Append(parameter.Key).Append(",");
UpdateCmd.Parameters.AddWithValue("#" + parameter.Key, parameter.Value);
}
builder.Remove(builder.Length - 1,1);
//set the command text and execute the command
UpdateCmd.CommandText = builder.ToString();
UpdateCmd.ExecuteNonQuery();
If you are using SQL Server 2008 you have the option of passing in the parameters and their values as a table to a Stored Procedure.
Inside the Stored Procedure you can join the table to be updated with the table passed in. That would probably be more efficient than creating hundreds of sep update statements.
Here is a link that may help http://msdn.microsoft.com/en-us/library/bb675163.aspx
And here is some sample code based on the code you posted in your question
First Create a table to play with and populate it with some data
CREATE TABLE [dbo].[sometable](
[Test1] [nvarchar](100) NULL,
[a] [nvarchar](100) NULL
) ON [PRIMARY]
Insert sometable Select 'rerere', '122342'
Insert sometable Select 'sfsfw', '343'
Insert sometable Select 'sfdrgss', '434545'
Insert sometable Select 'srgegrgeg', '3939932'
Then Create the Type in SQL Server
Create TYPE dbo.ParamsType AS TABLE
( Test1 nvarchar(100), a nvarchar(100) )
Then Create the Stored Procedure that accepts the type as a parameter
CREATE PROCEDURE usp_UpdateSomeTable
#Parameters dbo.ParamsType READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE sometable
SET sometable.Test1 = p.Test1
FROM sometable INNER JOIN #Parameters as p
ON sometable.a = p.a;
END
GO
To test from SQL Server Management Studio you can run
Declare #t as ParamsType
Insert #t Select 'newValue1', '122342'
Insert #t Select 'morenew ', '343'
Insert #t Select 'again', '434545'
Insert #t Select 'OnceMore', '3939932'
exec usp_UpdateSomeTable #Parameters=#t
To Test from C# Try
static void Main(string[] args)
{
System.Data.DataTable YourData = new DataTable("Parameters");
DataColumn column;
DataRow row;
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "Test1";
YourData.Columns.Add(column);
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "a";
YourData.Columns.Add(column);
row = YourData.NewRow();
row["Test1"] = "newValue1";
row["a"] = "122342";
YourData.Rows.Add(row);
row = YourData.NewRow();
row["Test1"] = "morenew";
row["a"] = "343";
YourData.Rows.Add(row);
row = YourData.NewRow();
row["Test1"] = "again";
row["a"] = "434545";
YourData.Rows.Add(row);
SqlConnectionStringBuilder connString = new SqlConnectionStringBuilder();
connString.DataSource = "127.0.0.1";
connString.InitialCatalog = "SO";
connString.IntegratedSecurity = true;
using (SqlConnection conn = new SqlConnection())
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = "usp_UpdateSomeTable";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlParameter p = cmd.Parameters.AddWithValue("#Parameters", YourData);
p.SqlDbType = SqlDbType.Structured;
p.TypeName = "dbo.ParamsType";
cmd.Connection = conn;
conn.ConnectionString = connString.ConnectionString;
conn.Open();
cmd.ExecuteNonQuery();
}
}

.net - tricky sql transaction (SQL Server, but Oracle in mind too, for later)

I have to perform a bulk operation bool AddEntitiesX(List<X>):
For each insert of X into X_Table (with X_UID as auto-increment ID), I have to insert k-times another entity Y into Y_Table (with X_UID as FK and Y_UID as auto-increment ID), since X contains a list of k-Y entities.
Then, for each inserted X, I need to insert also a Z entity in Z_Table (with X_UID as FK and Z_UID as auto-increment ID).
My pseudo-code will look like this:
// cannot use 'TransactionScope' since there are problems with Oracle
// so, prepare SqlTransaction
foreach (X)
{
//1. call 'InsertX' SP
foreach (Y in x.ListY)
//2. call 'InsertY' SP
//3. call 'InsertZ' SP
}
// commit transaction
How can I retrieve the X_UID from InsertX SP to pass to the next stored procs?
If there is no way, then since I cannot have this big transaction in one stored procedure, how should I model it?
I would like to know best practices to handle this kind of operations from business to data layer using transactions.
Thank you... and please let me know if my question is not clear enough.
One way is to use SCOPE_IDENTITY(), like cdel already suggested. Another way is to use the OUTPUT clause of INSERT
INSERT INTO table (field, field, ...)
OUTPUT INSERTED.ID
VALUES (#value, #value, ...);
This inserts the record and also produces a result set, which contains the inserted row generated identity value. In C# you read this result set just as if you'd have executed a SELECT, ie. you use ExecuteReader().
One advantage of using OUTPUT clause is that is the only way it can reliable return multiple row IDs. Say you insert not one, but 100, you can get back the IDs of all 100 inserted rows in a single result set. In case you wonder how to insert 100 rows in one swoop, see Arrays and Lists in SQL Server 2008: Using Table-Valued Parameters.
Another advantage of using OUTPUT clause is that you can chain two statements into a single one, see Chained Updates.
You can use Bulk Copy. Using this, first bulk insert all the records of x in a new table and bulk insert all records of Y in a new table.
Now you can use Cross joins in between these two new tables..like below
Select X.TableXVal, Y.TableYVal from NewTableX X
Cross Join NewTableY Y
This query can be written in the Stored Procedure and stored procedure can be called like below
using (System.Data.SqlClient.SqlConnection con = new SqlConnection("YourConnection string")) {
con.Open();
SqlCommand cmd = new SqlCommand();
string expression = "Parameter value";
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Your Stored Procedure";
cmd.Parameters.Add("Your Parameter Name",
SqlDbType.VarChar).Value = expression;
cmd.Connection = con;
using (IDataReader dr = cmd.ExecuteReader())
{
if (dr.Read())
{
}
}
}
You can create the sample tables like below
create table NewTableX
(
ID int Primary Identity(1,1),
TableXVal int
)
create table NewTableY
(
ID int Primary Identity(1,1),
TableYVal int
)
In this way, you can skip inserting the records one by one. hope this will help you.
For more information about using BulkCopy. Below is the code.
private void CreateDataTableFromList()
{
//supose you have list for X like below
List<int> x = new List<int>();
x.Add(1);
x.Add(2);
x.Add(3);
x.Add(4);
//supose you have list for Y like below
List<int> y = new List<int>();
y.Add(1);
y.Add(2);
y.Add(3);
y.Add(4);
DataTable dt = new DataTable();
DataColumn dc;
DataRow dr;
dc = new DataColumn();
dc.DataType = System.Type.GetType("System.Int32");
dc.ColumnName = "TableXVal";
dt.Columns.Add(dc);
dr = dt.NewRow();
dr["TableXVal"] = 1;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["TableXVal"] = 2;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["TableXVal"] = 3;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["TableXVal"] = 4;
dt.Rows.Add(dr);
SqlBulkCopy copy = new SqlBulkCopy("Your Connection String");
copy.DestinationTableName = "NewTableX";
copy.WriteToServer(dt);
dt = new DataTable();
dc = new DataColumn();
dc.DataType = System.Type.GetType("System.Int32");
dc.ColumnName = "TableYVal";
dt.Columns.Add(dc);
dr = dt.NewRow();
dr["TableYVal"] = 1;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["TableYVal"] = 2;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["TableYVal"] = 3;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["TableYVal"] = 4;
dt.Rows.Add(dr);
copy = new SqlBulkCopy("Your Connection String");
copy.DestinationTableName = "NewTableY";
copy.WriteToServer(dt);
}
Step1 - Use CreateDataTableFromList function
Step 2 - Call stored procedure as mentioned above
Your stored procedure must have the select statement as mentioned above.
Have your InsertX sp structured like this:
ALTER PROCEDURE dbo.InsertX
(
-- other parameters
#ID int = null OUTPUT,
#ErrMsg nvarchar(512) = null OUTPUT
)
AS
SET NOCOUNT ON
DECLARE #ret as int
SET #ret = 1
BEGIN TRY
INSERT INTO [dbo].[XTable]
([Column1])
VALUES
(null)
SET #ID = SCOPE_IDENTITY()
SET #ErrMsg = 'OK'
END TRY
BEGIN CATCH
SET #ErrMsg = ERROR_MESSAGE()
SET #ret = -1
END CATCH
RETURN #ret
After the call you get the ID and feed it into InsertY

Categories