I have a method to insert user information into the SQL Server database. I have my combobox populate on pageload event. The user selects the input they want and hit update if they are updating a old record or insert if creating a new record. When they do so my database is not storing the right value if they select 4 it stores 3. Here is my insert method and populate method.
Insert method: I have to join the StockID because it is a primary key.
using (dbConn2 = new SqlConnection(Properties.Settings.Default["tville"].ToString()))
{
SqlCommand addNewFormat = new SqlCommand(#"INSERT INTO PackLabelFormat ( PackFormatID, Description, PrintWeight, PrintPlantCode, PrintPrice, StockID) VALUES (#PackFormatID, #Description, #PrintWeight, #PrintPlantCode, #PrintPrice, (SELECT #StockID from LabelStockReference LSR WHERE LSR.StockID = #StockID))", dbConn2);
addNewFormat.Parameters.AddWithValue("#PackFormatID", Convert.ToInt32(IDselect));
addNewFormat.Parameters.AddWithValue("#Description", rTxtBoxDescription.Text);
addNewFormat.Parameters.AddWithValue("#PrintPrice", rChkBoxPrintPrice.Checked);
addNewFormat.Parameters.AddWithValue("#PrintWeight", rChkBoxWeight.Checked);
addNewFormat.Parameters.AddWithValue("#PrintPlantCode", rChkBoxPlantCode.Checked);
addNewFormat.Parameters.AddWithValue("#StockID", Convert.ToInt32(cmboBoxStock.SelectedIndex));
dbConn2.Open();
addNewFormat.ExecuteNonQuery();
Populate method:
if (labelList != null)
{
foreach (LabelData l in labelList)
{
cmboBoxStock.Items.Add(string.Format("{0} - {1}", l.PackFormatID, l.Description));
}
}
If there is anything else I'm leaving out just let me know. Thanks.
There are two options for your INSERT statement:
(1) you can use INSERT ... VALUES .... and in this case, you must supply as many values as you have columns to insert data into, and you have to supply literal values or SQL Server variables only - you cannot use a SELECT to provide a value:
DECLARE #ASqlServerVariable VARCHAR(100)
SET #ASqlServerVariable = 'any possible value that's legal for this datatype'
INSERT INTO dbo.YourTable(ID, Col1, Col2, Col3)
VALUES (42, 'Some fixed value', #ASqlServerVariable, 'Another literal value')
What you could do is use a SELECT to store the value you're interested in into a SQL Server variable:
DECLARe #StockID INT
SELECT #StockID = ID
FROM dbo.LabelStockReference LSR
WHERE LSR.StockID = 4711
(2) if you can't provide all literal values or variables, then you must use the INSERT .... SELECT ... option, which requires you to provide as many columns in your SELECT as the INSERT expects to insert a row into the target table:
DECLARE #ASqlServerVariable VARCHAR(100)
SET #ASqlServerVariable = 'any possible value that's legal for this datatype'
INSERT INTO dbo.YourTable(ID, Col1, Col2, Col3)
SELECT
42, 'Some fixed value', #ASqlServerVariable, aTableColumn
FROM
dbo.SomeOtherTable
See the official TechNet documentation for INSERT for all the details and exact syntax of all possible options etc.
The first SelectedIndex is 0, and your first ID is 1 ,so just make this change:
addNewFormat.Parameters.AddWithValue("#StockID", Convert.ToInt32(cmboBoxStock.SelectedIndex)+1);
Related
I am using c# to insert a record into the SQLite DB table 'VS_Types' with one parameter from table 'VS_Groups'. I test it with SQLite Browser and it works. While I implement the same sqlite command inside the C# with System.Data.SQlite. It doesn't work.
I modify the commandText to as following it works. Looks like the parameter '#value0' influence the c# SQLite command execution.
cmd.CommandText = "INSERT INTO VS_Types(Name, Group_id, Color) Values('20', 10, 1245679);";
SQLite SQL:
SELECT #value0=ID FROM VS_Groups WHERE (Name = 'Ato_h');
INSERT INTO VS_Types(Name, Group_id, Color) VALUES('20', #value0, 65536);
The Code inside the C#
using (SQLiteConnection conn = new SQLiteConnection(cs))
{
try
{
conn.Open();
SQLiteCommand cmd = new SQLiteCommand(conn);
cmd.CommandText = "Select #groudId = ID From VS_Groups Where(Name = 'Ato_h'); "
+ "INSERT INTO VS_Types(Name, Group_id, Color) Values('20', #groudId, 1245679);";
cmd.ExecuteNonQuery();
conn.Close();
testOutput.WriteLine("Insert Successful");
}
catch (Exception ex)
{
this.testOutput.WriteLine("Failed to open connection: {0}", ex.Message);
}
}
Please give me some suggestions about how to use the parameter inside C# Sqlite command in this situation.
Update: the VS_Groups and VS_Types table and its content
CREATE TABLE "VS_Groups" (
`ID` INTEGER PRIMARY KEY AUTOINCREMENT,
`Name` TEXT,
`Width` INTEGER,
`Height` INTEGER,
`Flags` INTEGER,
`Limi_recognition` INTEGER,
`Base` TEXT,
`Flags1` INTEGER,
`Limit_recognition` INTEGER,
`Flags2` INTEGER,
`Limit_recognition2` INTEGER,
`Distance_threshold` INTEGER
)
CREATE TABLE `VS_Types` (
`ID` INTEGER PRIMARY KEY AUTOINCREMENT,
`Name` TEXT,
`Group_id` INTEGER,
`Color` INTEGER
)
The existing record is:9,Ato_h,160,140,65536,,,,400,,,100
Sqlite SQL does not work like that. You cannot declare and store a value in a variable, then reuse that variable later. Terms like #groudId are only placeholders for parameters passed to the query. That means that the expression #groudId = ID is NOT an assignment, rather it is a comparison. Since you are not binding the parameter #groudId to anything, the parameter value is null, so the expression is a comparison like null = ID which will result in false which numerically is 0 (zero). The select statement returns 0 and is not used in the INSERT statement.
If the INSERT is working at all, it is probably resulting in something like INSERT INTO VS_Types(Name, Group_id, Color) Values('20', null, 1245679);
At the end of the question, you also say "The existing record is ..." and you only show a single record for VS_Groups, although the insert statement is for the table VS_Types. But you don't show output for the VS_Types table! You are inspecting the wrong table for the inserted data. If you query the table VS_Types, you will likely find many records with Name == '20', GroupID == null and Color == 123456789... exactly as the INSERT statement says.
In summary, you are not using parameters correctly, but you really don't even need a parameter in the code you show, so it is difficult to know how to answer properly. An answer showing proper use of parameters would be wasted, but a replacement SQL may not be want you want in the end either. I suggest researching parameters separately to learn how to use them properly. For now, use this nested statement:
INSERT INTO VS_Types(Name, Group_id, Color)
VALUES ('20', (Select ID From VS_Groups Where Name = 'Ato_h'), 1245679);
Regrettably that's not all. The table definition does not show that VS_Groups.Name is unique, so technically there could be multiple rows that match the nested query, so the INSERT statement could still fail. I suggest adding a UNIQUE constraint to the VS_Groups.Name column.
I want to use a SELECT statement into a table based on multiple values from ListBox and get multiple IDs and then run an INSERT statement and store the multiple IDs into a different table from INSERT statement.
My code below is not working as I am getting "NULL" in single row instead of multiple IDs in multiple rows.
I am using a stored procedure for all the SQL statements.
Please see my code below:
Code-behind of my ASPX web page:
string listboxvalue = "";
foreach (ListItem item in listbox.Items)
{
if (item.Selected)
{
listboxvalue += item.Text + ',';
}
}
listboxvalue = listboxvalue.Substring(0, listboxvalue.Length - 1);
cmd.Parameters.AddWithValue("spselectvalue", listboxvalue);
Stored procedure:
#spselectvalue nvarchar(MAX),
// Select multiple Ids based on multiple items from list box
DECLARE #Dis TABLE (DisID int)
INSERT INTO #Dis
SELECT DId
FROM [table name]
WHERE [COLUMN] IN ('+#spselectvalue +')
EXEC sp_executesql #Dis
// Insert multiple Ids (from above select statement) into different table
INSERT INTO [dbo].[DifferentTable] ([SelectedIds])
VALUES
(
(SELECT DisID from #Dis)
)
Seems like your problem is that you don't know how to get a list of ID's into a stored procedure for processing. Hopefully this example will help. If you do it this way, you won't have to mess with comma-delimited strings or dynamic SQL.
First, define a SQL type that can contain your list of IDs:
CREATE TYPE dbo.MyList
AS TABLE
(
ID VarChar(50)
);
Then, write a stored procedure that accepts this type as its input parameter:
CREATE PROCEDURE dbo.InsertAList
#List AS dbo.MyList READONLY
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[DifferentTable]
SELECT ID FROM #List
END
Now to bind your data to the stored procedure from the c# end. To bind it, you have to store the data in a DataTable first, which is relatively easy:
var table = new DataTable();
table.Columns.Add("ID", typeof(string));
foreach (ListItem item in listBox.Items.Where(i => i.Selected))
{
table.Rows.Add(item.Text);
}
Then submit the table to the stored procedure like so:
var cmd = new SqlCommand()
{
CommandType = CommandType.StoredProcedure,
CommandText = "InsertAList",
Connection = myConnection
};
cmd.Parameters.Add(new SqlParameter("#List", SqlDbType.Structured)
{
TypeName = "dbo.MyList",
Value = table
});
cmd.ExecuteNonQuery();
That doesn't seem like a valid (or logical) SQL. The exec sp_executesql #Dis shouldn't be there or #Dis shouldn't be a table.
When using sp_executesql the parameter should be a string containing the statement to execute (documentation).
Detailed answer can be found here.
Also: the INSERT INTO #Dis ... line won't work as you expect when written that way, for the reasons mentioned in the linked question (and Erland Sommarskog blog mentioned there).
Selected values considered as numeric.
DECLARE #Sqltext nvarchar(max) =
'INSERT INTO [dbo].[DifferentTable] ([SelectedIds])
SELECT DId
FROM [table name]
WHERE [COLUMN] IN (' + #spselectvalue + ')'
EXEC sp_executesql #Sqltext
I have the following bit of code:
string updatestr= "UPDATE t1 SET c3= #v3,c4=#v4,c5=#v5,c6=#v6 WHERE c2 = 'test' SELECT SCOPE_IDENTITY()";
SqlCommand updatecmd= new SqlCommand(updatestr, conn);
updatecmd.Parameters.AddWithValue("#v3",value3)
updatecmd.Parameters.AddWithValue("#v4",value3)
updatecmd.Parameters.AddWithValue("#v5",value3)
updatecmd.Parameters.AddWithValue("#v6",value3)
object ind = updatecmd.ExecuteScalar();
int distindex = int.Parse(ind.ToString());
Now I would expect the output of the scope identity to be an int (namely 29) however I get a format exception. at int.Parse(ind.ToString());. I printed the value of ind.tostring and for some reason it's test. Note that the first colum and the primairy key of the database (called id) is an autoincrementing int and I was hoping to select that. What am I doing wrong?
Oh and here is a better formated version of the query:
UPDATE t1
SET c3= #v3,c4=#v4,c5=#v5,c6=#v6
WHERE c2 = "test"
SELECT SCOPE_IDENTITY()
SELECT SCOPE_IDENTITY()
Is for getting the auto-generated Id when a new row is INSERTED, in your case its an update so its probably returning null, you should use
select CAST(##rowcount as int)
instead
As I have said in comment:
You are updating based on non primary key so there can be more that
1 updated row. Which one do you want in result? You are looking for
OUTPUT clause I think.
So the solution is to use OUTPUT clause with Inserted or Deleted virtual tables in case of update statement:
string updatestr= "UPDATE t1 SET c3= #v3,c4=#v4,c5=#v5,c6=#v6
OUTPUT Inserted.id
WHERE c2 = 'test'";
try this:
int distindex = 0;
object ind = updatecmd.ExecuteScalar();
if(ind != null)
int.TryParse(ind.ToString(), out distindex);
Can i ignore the duplicate records of a data already present in sql database from a datatable which i am passing to SqlBulkCopy. If Yes then How and also explain me if No and other option.
No, that's not built-in. You need to clean the data on the client or insert into a staging table first.
As previous poster said, this is not built in. I achieve similar using the following:
SQL Stored Procedure that accepts a TableValuedParameter with the data you require.
In the stored proc, I then INSERT all records into a temp table. Once you have it there, you can use SQLs MERGE statement in your stored proc to insert data where it doesn't already exist.
So, let us assume that our data is simply people's names stored in a table people. We hold only an ID and a name. I also assume this table is called 'people'.
Here's how I create my Table Valued Parameter type (created in SQL Server)
CREATE TYPE udt_person AS TABLE(
[id] [INT] NOT NULL,
[name] [nvarchar(50)] NULL
)
GO
I now create the stored procedure:
CREATE PROCEDURE SaveNewPeople #pPeople udt_Person
AS
BEGIN
-- Create Temp table
CREATE TABLE #tmpPeople (id INT, name VARCHAR 50)
-- We will stage all data passed in into temp table
INSERT INTO #tmpPeople
SELECT id, name FROM #pPeople
-- NB: you will need to think about locking strategy a bit here
MERGE people AS p
USING #tmpPeople AS t
ON p.id = t.id
WHEN NOT MATCHED BY TARGET THEN
-- We want to insert new person
INSERT (id, name) VALUES (t.id, t.name)
WHEN MATCHED THEN
-- you may not need this, assume updating name for example
UPDATE SET p.name = t.name
END
Now we have the SQL in place.
Let us create the bulk of data in C#:
DataTable ppl = new DataTable();
ppl.Columns.Add("id", typeof(int));
ppl.Columns.Add("name", typeof(string));
// table is created, let's add some people
var bob = ppl.NewRow();
bob["id"] = 1;
bob["name"] = "Bob";
ppl.Rows.Add(bob);
var jim = ppl.NewRow();
jim["id"] = 2;
jim["name"] = "Jim";
ppl.Rows.Add(jim);
// that's enough people for now, let's call the stored procedure
using(var conn = new SqlConnection("YouConnStringHere"))
{
using(var cmd = new SqlCommand("SaveNewPeople", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
var tvp = new SqlParameter
{
ParameterName = "#pPeople",
SqlDbType = SqlDbType.Structured,
Value = ppl,
TypeName = "udt_person"
}
cmd.Parameters.Add(tvp);
conn.Open();
cmd.ExecuteNonQuery();
}
}
Hopefully this gives you the idea. If you then modified the C# datatable, you should see rows inserted, updated or ignored.
Good luck.
Another way to do it is to create a database trigger to replace the inserts initiated by SqlBulkCopy. The performance will be impeded, depending on, among other things, the size of the batch, but it works nonetheless.
CREATE TABLE [dbo].[TempTable] (
[Id] INT IDENTITY PRIMARY KEY,
[Val] NVARCHAR(20)
)
GO
CREATE OR ALTER TRIGGER [IgnoreDuplicates] ON [dbo].[TempTable]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[TempTable]([Val])
SELECT [Val] FROM [INSERTED] WHERE [Val] NOT IN (
SELECT [Val] FROM [dbo].[TempTable]
)
END
GO
When a user deletes a row in the database, I want to archive it into a separate table in the database rather than delete it or flag it in the current table. I figure I would need to do something like in this link:
How to copy a row from one SQL Server table to another
The thing is, the archive table has 1 extra column in it that does not match the original table (ArchiveTimeStamp). This ArchiveTimeStamp does not exist in the original table, instead I would use something like
archiveComm.Parameters.AddWithValue("ArchiveTimeStamp", Date.Time.Now);
This is what I have so far:
SqlCommand archiveComm = new SqlCommand("INSERT INTO Archive_Table SELECT * FROM Table WHERE RowID = #rowID", conn);
Is there a way for me to modify the SqlCommand to add another param that doesn't exist in the original Table?
Why not just handle this on the back end? You can create a trigger on the original table to insert into another table after every delete?
Your trigger will look like this:
CREATE TRIGGER onOriginalTableDelete
ON originalTable
FOR DELETE
AS
INSERT INTO anotherTable
SELECT * FROM deleted;
When a record is deleted on the original table, it will insert the deleted record into the other table. You might want to read on using the deleted table here.
Check this SQL Fiddle. Since you're inserting the timestamp in another column, you can just add this on the INSERT INTO SELECT statement:
INSERT INTO OtherTable
SELECT *, CURRENT_TIMESTAMP FROM MainTable;
This could be the query for your trigger:
CREATE TRIGGER onOriginalTableDelete
ON originalTable
FOR DELETE
AS
INSERT INTO anotherTable
SELECT *, CURRENT_TIMESTAMP FROM deleted;
Good question. I'd suggest (as Gian has also suggested) moving the logic you require to backup the deleted row into a trigger that gets fired on delete.
Triggers are events in a database associated to a table which get fired upon an action occurring i.e. insert / update / delete.
So in your scenario, if you create an ON DELETE trigger in the source table, it will get fired when a delete occurs. The SQL contained within the trigger can specify what to do with the deleted data, which in your scenario will be: insert the deleted info into the archive table with a timestamp.
So if you have:
Source_Table:
Col_1
Col_2
Col_3
Archive_Table:
Col_1
Col_2
Col_3
Time_Stamp
You'll need to create a FOR DELETE trigger against Source_Table (something like this):
CREATE TRIGGER SourceDeletedTrigger
ON database.dbo.Source_Table
FOR DELETE
AS
INSERT INTO Archive_Table(Col_1, Col_2, Col_3, Time_Stamp)
SELECT
DELETED.Col_1,
DELETED.Col_2,
DELETED.Col_3,
GETDATE()
FROM DELETED
GO
The above is some rough SQL which may contain a couple of syntax errors but the guts of the idea is conveyed.
You will have to use to explicit column list and values form of the INSERT statement:
INSERT INTO Archive_Table (
Col1
,Col2
,Col3 )
SELECT
Col1
,Col2
,Col3
FROM
Table
WHERE
Row_ID = #Row_ID
See Insert into ... values ( SELECT ... FROM ... )
I think you have to specify the columns with something like this
INSERT INTO tab1
(col1, col2)
SELECT col1, col2
FROM tab2
WHERE RowID = #rowID"
You need to specify the columns name in that case:
archiveComm.Parameters.AddWithValue("ArchiveTimeStamp", Date.Time.Now);
string SQL = "INSERT INTO Archive_Table (Col1,Col2,ArchiveTimeStamp) " & _
"SELECT Col1,Col2,#ArchiveTimeStamp FROM Table WHERE RowID = #rowID"
SqlCommand archiveComm = new SqlCommand(SQL, conn);
Here is my suggestion, you are forced to supply the column names or it won't let you run the query, however I understand you would prefer a generic solution that worked for any table so I suggest building the insert SQL dynamically, cache it on your application, and then just execute it with your extra archive column. Here is a c# example:
public class ArchiveTableRow
{
private static readonly IDictionary<string, string> _cachedInsertStatements = new Dictionary<string, string>();
public void Archive(string tableName, string rowId)
{
if (_cachedInsertStatements.ContainsKey(tableName) == false)
{
BuildInsertStatement(tableName);
}
var insertQuery = _cachedInsertStatements[tableName];
// ...
SqlCommand archiveComm = new SqlCommand(insertQuery, conn);
// ...
archiveComm.Parameters.AddWithValue("ArchiveTimeStamp", Date.Time.Now);
// ...
}
private void BuildInsertStatement(string tableName)
{
// Get the columns names:
var getColumnNamesQuery = #"
SELECT Table_Schema, Column_Name
FROM Information_Schema.Columns
WHERE Table_Name = '" + tableName + "'
Order By Ordinal_Position";
// Execute the query
SqlCommand archiveComm = new SqlCommand(getColumnNamesQuery, conn);
// Loop and build query and add your archive in the end
// Add to dictionary
}
}
You would use it with something like:
var archiveRow = new ArchiveTableRow();
archiveRow.Archive("TableName", rowId);