Column names in each table must be unique exception - c#

Column names in each table must be unique. Column name 'Delivery' in table 'Sales' is specified more than once.
How can I solve this? I get a System.Data.SqlClient.SqlException.
class DataAccess
{
string ConnectionString;
public DataAccess()
{
ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["CafeteriaDBConnectionString"].ConnectionString;
}
public bool RecordASale(ArrayList ProductsList, DateTime SaleTime, int SalesmanID, decimal CashGiven, decimal TotalBill, decimal CashReturn, string Delivery)
{
int SaleID = ReturnSaleID();
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("alter table [Sales] add [Delivery] varchar default 5 NOT NULL"))
{
command.Connection = connection;
command.ExecuteNonQuery();
}
}
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
connection.Open();
/*Start a local transaction*/
SqlTransaction sqlTran = connection.BeginTransaction();
/*Enlist a command in the current transaction*/
SqlCommand command = connection.CreateCommand();
command.Transaction = sqlTran;
try
{
// Execute separate commands.
command.Parameters.AddWithValue("#SaleTime", SaleTime);
command.Parameters.AddWithValue("#SalesmanID", SalesmanID);
command.Parameters.AddWithValue("#CashGiven", CashGiven);
command.Parameters.AddWithValue("#TotalBill", TotalBill);
command.Parameters.AddWithValue("#CashReturn", CashReturn);
/*command.Parameters.AddWithValue("#Delivery", Delivery);*/
command.CommandText =
"Insert into Sales (SaleTime, SalesmanID, CashGiven, TotalBill, CashReturn, Delivery) values (#SaleTime, #SalesmanID, #CashGiven, #TotalBill, #CashReturn, #Delivery)";
command.ExecuteNonQuery();
foreach (Details ProductDetail in ProductsList)
{
//// Execute separate commands.
//command.Parameters.AddWithValue("#ProductName", ProductDetail.Name);
//command.Parameters.AddWithValue("#ProductPrice", ProductDetail.Price);
//command.Parameters.AddWithValue("#ProductQuantity", ProductDetail.Quantity);
//command.Parameters.AddWithValue("#ProductTotal", ProductDetail.Total);
//command.Parameters.AddWithValue("#SaleID", SaleID);
command.CommandText =
"Insert into SaleItems (ProductName, ProductPrice, ProductQuantity, ProductTotal, SaleID) values ('" + ProductDetail.Name + "', '" + ProductDetail.Price + "', '" + ProductDetail.Quantity + "', '" + ProductDetail.Total + "', '" + SaleID + "')";
command.ExecuteNonQuery();
}
// Commit the transaction.
sqlTran.Commit();
//connection.Close();
return true;
}
catch (Exception ee)
{
throw ee;
}
}
}

If you want to check if your column exists before adding it to the datatable you should change your code in this way
string sql = #"if NOT exists(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE Column_Name = 'Delivery' AND Table_Name = 'Sales')
ALTER TABLE Sales ADD Delivery VARCHAR DEFAULT 5 NOT NULL";
using (SqlCommand command = new SqlCommand(sql))
{
command.Connection = connection;
command.ExecuteNonQuery();
}
In this way the operation to add the column Delivery to he table Sales will be executed only if the query on INFORMATION_SCHEMA.COLUMNS doesn't return a value
I have also noticed now that you are building your INSERT command concatenating strings together. This is a well know security problem that you can learn about searching how one can use it for Sql Injection
Also, if the SalesID column is an Identity column, you should not try to pass a value for it in the sql query

Related

SqlBulkCopy got exception : "Received an invalid column length from the bcp client for colid ."

I have code using SqlBulkCopy to clone a lot of tables, it used to work before, but very weird, recently got exception
Received an invalid column length from the bcp client for colid
I have search this exception and still not solve my problem.
sqlBulkCopy.WriteToServer(reader) will raise this exception if a table has two continous columns which are both of type Char(1) or nvarchar(nn), and both have NULL. Sometime, changing the SqlBulkCopy.BatchSize makes it work, but many times, it will not.
After simplify, I have test case as follow, and it is reproduceable on two servers:
Create a table like below: (tested on SQL Server 2012 SP 4 and SQL Server 2016 SP2)
IF OBJECT_ID('dbo.TestTable', 'U') IS NOT NULL
DROP TABLE dbo.TestTable;
CREATE TABLE [dbo].[TestTable]
(
[value2] [char](1) NULL,
[value1] [char](1) NULL
) ON [PRIMARY]
GO
DECLARE #i int = 0
WHILE #i < 262
BEGIN
SET #i = #i + 1
INSERT INTO [dbo].[TestTable]([value2], [value1])
VALUES (null, null)
END
C# console (.net framework 4.7) code as below
class Program
{
// [change here]
static string sourceConn = #"Server={YourServer};Database={YourDatabase};User ID={userYourName};Password={yourPassword};connect timeout=15";
static void Main(string[] args)
{
CopyTable(sourceConn, sourceConn, "TestTable", "testTableBAK");
Console.ReadLine();
}
static void CopyTable(string sConnSource, string sConnDest, string sTableSource, string sTableDest)
{
if (IsTableExist(sConnDest, sTableDest))
{
RunNonQuerySQL(sConnDest, "DROP TABLE " + sTableDest);
Console.WriteLine($"existing table {sTableDest} dropped");
}
CopySchema(sConnDest, sTableSource, sTableDest);
using (SqlConnection connSource = new SqlConnection(sConnSource))
{
connSource.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = connSource;
cmd.CommandText = "SELECT * FROM " + sTableSource;
// using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(sConnDest, SqlBulkCopyOptions.KeepNulls | SqlBulkCopyOptions.KeepIdentity))
using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(sConnDest))
{
// sqlBulkCopy.BatchSize = 1380; // this optional setting will work if set value smaller than 1397 for testTable on my new server (SQL server 13.0.5102.14)
// sqlBulkCopy.BatchSize = 261; // this optional setting will work if set value smaller than 261 for testTable on 2 older server (SQL server 11.0.7001)
sqlBulkCopy.DestinationTableName = sTableDest;
SqlDataReader reader = cmd.ExecuteReader();
try
{
// exception here
sqlBulkCopy.WriteToServer(reader);
Console.WriteLine("table copied");
}
catch (SqlException ex)
{
Console.WriteLine(ex.Message);
}
sqlBulkCopy.Close();
}
}
}
static bool IsTableExist(string sConn, string sTableName)
{
bool result = false;
using (SqlConnection conn = new SqlConnection(sConn))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
string[] s = sTableName.Split('.');
if (s.Length > 1)
{
cmd.CommandText = "select count (*) as counter from information_schema.tables where table_name = '" + s[1] + "' and TABLE_SCHEMA='" + s[0] + "'";
}
else
{
cmd.CommandText = "select count (*) as counter from information_schema.tables where table_name = '" + sTableName + "'";
}
cmd.Connection = conn;
var count = Convert.ToInt32(cmd.ExecuteScalar());
result = count > 0;
}
return result;
}
static bool RunNonQuerySQL(string sConn, string sSQL)
{
bool result = false;
using (SqlConnection conn = new SqlConnection(sConn))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = sSQL;
cmd.Connection = conn;
var count = cmd.ExecuteNonQuery();
result = true;
}
return result;
}
static public bool CopySchema(string sConn, string sTableSource, string sTableDest)
{
return RunQuerySQL(sConn, "select * into " + sTableDest + " from " + sTableSource + " where 1=2");
}
static public bool RunQuerySQL(string sConn, string sSQL)
{
using (SqlConnection conn = new SqlConnection(sConn))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = sSQL;
cmd.Connection = conn;
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
return true;
}
else
{
return false;
}
}
}
}
I just experienced this error.
I tried to insert a string with a lenght of 5 into a table column with a definition of "varchar(4)".
The error message in my case was:
"Received an invalid column length from the bcp client for colid 2".
"Colid 2" refered to the second column of the row (DataRow) that was part of the DataTable which I used as parameter for the call to the SqlBulkCopy.WriteToServer(DataTable table) method.
The solution in my case was to add validation code that checks the lenght of the strings in my input data before trying to call SqlBulkCopy.WriteToServer().

Adding unknown count of values inside a SELECT statement

I have code that adds a value inside a SQL table :
SqlCommand command;
SqlDataAdapter adapter = new SqlDataAdapter();
String strSQL = "";
strSQL = "INSERT INTO tblTest (value1) VALUES ('" + strPLCData + "')";
command = new SqlCommand(strSQL, cnn);
adapter.InsertCommand = new SqlCommand(strSQL, cnn);
try
{
int rows = adapter.InsertCommand.ExecuteNonQuery();
txtStatusLogging.Text += "Inserted " + rows + " row(s) in the database." + Environment.NewLine;
}
catch (Exception ex)
{
MessageBox.Show("Failed to write to database : " + ex.Message);
}
command.Dispose();
But I'm a bit stuck when I want to add an unknown count of values in the database (according to a list of unknown size).
e.g. sometimes add only value1, other times add value1, value2 and value3 .... (depending on whats in a certain list).
How would I go about doing this?
There's absolutely no need for that SqlDataAdapter. If you want to add an arbitrary number of values to a table - use a straight INSERT SqlCommand and just loop over the list of values to insert.
Also: you should always use parametrized queries - no exceptions - and you should put your SqlConnection and SqlCommand objects in using () { ... } blocks - something like this:
Something like this:
public void InsertValues(List<int> values)
{
// define the insert query
string qryInsert = "INSERT INTO dbo.tblTest (value1) VALUES (#singleValue);";
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmdInsert = new SqlCommand(qryInsert, conn))
{
// define parameter
cmdInsert.Parameter.Add("#singleValue", SqlDbType.Int);
conn.Open();
// loop over values
foreach (int aValue in values)
{
// set the parameter value, execute query
cmdInsert.Parameters["#singleValue"].Value = aValue;
cmdInsert.ExecuteNonQuery();
}
conn.Close;
}
}

Invalid attempt to call read when reader is closed when inserting data

i have a button that when clicked inserts data from textbox and combobox fields into database tables, but every time i insert it gives me "Invalid attempt to call read when reader is closed". How can i get rid of this error. And tips on optimising the code are welcome, because i know im a total noob. thanks
private void btnSave_Click(object sender, RoutedEventArgs e)
{
try
{
SqlConnection sqlCon = new SqlConnection(#"Data Source=(localdb)\mssqllocaldb; Initial Catalog=Storagedb;");
sqlCon.Open();
string Query1 = "insert into location(Storage, Shelf, columns, rows) values(" + txtWarehouse.Text + ", " + txtShelf.Text + ", " + txtColumn.Text + ", " + txtRow.Text + ")";
SqlCommand sqlCmd = new SqlCommand(Query1, sqlCon);
SqlDataAdapter dataAdp = new SqlDataAdapter(sqlCmd);
dataAdp.SelectCommand.ExecuteNonQuery();
sqlCon.Close();
}
catch (Exception er)
{
MessageBox.Show(er.Message);
}
try
{
SqlConnection sqlCon = new SqlConnection(#"Data Source=(localdb)\mssqllocaldb; Initial Catalog=Storagedb;");
sqlCon.Open();
string Query3 = "SELECT LOCATION_ID FROM LOCATION WHERE storage='" + txtWarehouse.Text + "' AND shelf='" + txtShelf.Text + "' AND columns='"
+ txtColumn.Text + "' AND rows='" + txtRow.Text + "'";
SqlCommand sqlCmd1 = new SqlCommand(Query3, sqlCon);
SqlDataReader dr = sqlCmd1.ExecuteReader(); ;
while (dr.Read())
{
string LocationId = dr[0].ToString();
dr.Close();
string Query2 = "insert into product(SKU, nimetus, minimum, maximum, quantity,location_ID,category_ID,OrderMail_ID) values ('" + txtSku.Text + "','" + txtNimetus.Text + "', '"
+ txtMin.Text + "', '" + txtMax.Text + "', '" + txtQuan.Text + "', '" + LocationId + "', '" + (cbCat.SelectedIndex+1) + "', '" + (cbMail.SelectedIndex+1) + "')";
SqlCommand sqlCmd = new SqlCommand(Query2, sqlCon);
SqlDataAdapter dataAdp = new SqlDataAdapter(sqlCmd);
dataAdp.SelectCommand.ExecuteNonQuery();
}
sqlCon.Close();
}
catch (Exception ed)
{
MessageBox.Show(ed.Message);
}
}
Let's try to make some adjustments to your code.
First thing to consider is to use a parameterized query and not a
string concatenation when you build an sql command. This is mandatory
to avoid parsing errors and Sql Injections
Second, you should encapsulate the disposable objects in a using statement
to be sure they receive the proper disposal when you have finished to
use them.
Third, you can get the LOCATION_ID from your table without running a
separate query simply adding SELECT SCOPE_IDENTITY() as second batch to your first command. (This works only if you have declared the LOCATION_ID field in the first table as an IDENTITY column)
Fourth, you put everything in a transaction to avoid problems in case
some of the code fails unexpectedly
So:
SqlTransaction tr = null;
try
{
string cmdText = #"insert into location(Storage, Shelf, columns, rows)
values(#storage,#shelf,#columns,#rows);
select scope_identity()";
using(SqlConnection sqlCon = new SqlConnection(.....))
using(SqlCommand cmd = new SqlCommand(cmdText, sqlCon))
{
sqlCon.Open();
using( tr = sqlCon.BeginTransaction())
{
// Prepare all the parameters required by the command
cmd.Parameters.Add("#storage", SqlDbType.Int).Value = Convert.ToInt32(txtWarehouse.Text);
cmd.Parameters.Add("#shelf", SqlDbType.Int).Value = Convert.ToInt32(txtShelf.Text);
cmd.Parameters.Add("#columns", SqlDbType.Int).Value = Convert.ToInt32(txtColumn.Text );
cmd.Parameters.Add("#rows", SqlDbType.Int).Value = Convert.ToInt32(txtRow.Text);
// Execute the command and get back the result of SCOPE_IDENTITY
int newLocation = Convert.ToInt32(cmd.ExecuteScalar());
// Set the second command text
cmdText = #"insert into product(SKU, nimetus, minimum, maximum, quantity,location_ID,category_ID,OrderMail_ID)
values (#sku, #nimetus,#min,#max,#qty,#locid,#catid,#ordid)";
// Build a new command with the second text
using(SqlCommand cmd1 = new SqlCommand(cmdText, sqlCon))
{
// Inform the new command we are inside a transaction
cmd1.Transaction = tr;
// Add all the required parameters for the second command
cmd1.Parameters.Add("#sku", SqlDbType.NVarChar).Value = txtSku.Text;
cmd1.Parameters.Add("#nimetus",SqlDbType.NVarChar).Value = txtNimetus.Text;
cmd1.Parameters.Add("#locid", SqlDbType.Int).Value = newLocation;
.... and so on for the other parameters required
cmd1.ExecuteNonQuery();
// If we reach this point the everything is allright and
// we can commit the two inserts together
tr.Commit();
}
}
}
}
catch (Exception er)
{
// In case of exceptions do not insert anything...
if(tr != null)
tr.Rollback();
MessageBox.Show(er.Message);
}
Notice that in the first command I use parameters of type SqlDbType.Int because you haven't used single quotes around your text. This should be verified against the real data type of your table columns and adjusted to match the type. This is true as well for the second command where you put everything as text albeit some of those fields seems to be integer (_location_id_ is probably an integer). Please verify against your table.

How to insert date into datetime column in SQL Server using ado.net programming with transactions in mvc

Not able to insert date into datetime column in sql server.
While debugging after the first executeNonQuery() statement debugger jumps to catch block
and it's displaying the following message in sqlError parameter in catch block:
'Incorrect Syntax near 12'
public ActionResult AddStudentData(StudentDetails sd)
{
SqlConnection db = new SqlConnection("Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\StudentDB.mdf;Integrated Security=True;User Instance=True");
SqlTransaction transaction;
db.Open();
transaction = db.BeginTransaction();
SqlCommand cmd = db.CreateCommand();
try
{
**cmd.CommandText = "insert into StudentDetails(StudentName, DOB, Description, Gender) values ('" + sd.StudentName + "'," +sd.DOB + ",'" + sd.Description + "','" + sd.Gender + "')";
cmd.Transaction = transaction;
cmd.ExecuteNonQuery();**
cmd.CommandText = "select max(StudentID) from StudentDetails";
int id = (int)cmd.ExecuteScalar();
cmd.CommandText = "insert into Qualification (StudentID, Qualification, POY) values ("+id+",'"+sd.Qualification+"',"+sd.POY+")";
cmd.Transaction = transaction;
cmd.ExecuteNonQuery();
transaction.Commit();
}
catch (SqlException sqlError)
{
string s = sqlError.Message.ToString();
transaction.Rollback();
}
db.Close();
return View("SuccessAddStudentData");
}
Thank You
Dilipkumar
Note that date values (DOB) must also contain single quotes.
cmd.CommandText = "insert into StudentDetails(StudentName, DOB, Description, Gender)
values ('" + sd.StudentName + "','" +sd.DOB + "','" +
sd.Description + "','" + sd.Gender + "')";
Also, keep in mind the date format based on SQL Server.
And lastly as other posts mentioned, use bind parameters to prevent SQL injection.
You should ALWAYS use parameterized queries when accessing a SQL database, otherwise your application might be vulnerable to SQL injection. You also should use using when dealing with objects for database access. For example, when the using block is left, the connection will be closed no matter if an exception was thrown or not.
try
{
using (var db = new SqlConnection("connection string"))
using (var transaction = db.BeginTransaction())
using (var cmd = db.CreateCommand())
{
cmd.Transaction = transaction;
db.Open();
cmd.CommandText = "insert into StudentDetails(StudentName, DOB, Description, Gender) values(#name, #dob, #desc, #gender)";
cmd.Parameters.AddWithValue("#name", sd.StudentName);
cmd.Parameters.AddWithValue("#dob", sd.DOB);
cmd.Parameters.AddWithValue("#desc", sd.Description);
cmd.Parameters.AddWithValue("#gender", sd.Gender);
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
// other queries
transaction.Commit();
}
}
catch (SqlException ex)
{
string s = ex.Message.ToString();
}

Check if table name exists SQL

How can I check if a table already exists before creating a new one?
Updated Code:
private void checkTable()
{
string tableName = quotenameTxt.Text + "_" + firstTxt.Text + "_" + surenameTxt.Text;
string connStr = #"Data Source=|DataDirectory|\LWADataBase.sdf";
// SqlCeConnection conn = new SqlCeConnection(connStr);
// if (conn.State == ConnectionState.Closed) { conn.Open(); }
using (SqlCeConnection conn = new SqlCeConnection(connStr))
{
conn.Open();
SqlCeCommand cmd = new SqlCeCommand(#"SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #tname", conn);
cmd.Parameters.AddWithValue("#tname", tableName);
SqlCeDataReader reader = cmd.ExecuteReader();
if(reader.Read()){
MessageBox.Show("Table exists");}
else{
MessageBox.Show("Table doesn't exist");
createtable();}
Sql Server Compact supports the INFORMATION_SCHEMA views
using (SqlCeConnection conn = new SqlCeConnection(connStr))
{
conn.Open();
SqlCeCommand cmd = new SqlCeCommand(#"SELECT TOP 1 *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #tname", conn);
cmd.Parameters.AddWithValue("#tname", tableName)
SqlCeDataReader reader = cmd.ExecuteReader();
if(reader.Read())
Console.WriteLine("Table exists");
else
Console.WriteLine("Table doesn't exist");
}
EDIT
In version 3.5 it seems that the TOP 1 instruction is not accepted. However, given the WHERE clause it should make no difference using it or not so, to make it work just change the query to
SqlCeCommand cmd = new SqlCeCommand(#"SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #tname", conn);
SECOND EDIT
Looking at the code that creates the table.
(It is In chat, I suggest to add it to the question for completeness)
using (SqlCeCommand command = new SqlCeCommand(
"CREATE TABLE ['" + tableName + "'] " +
"(Weight INT, Name NVARCHAR, Breed NVARCHAR)", con))
The single quotes around the tableName variables becomes part of the name of the table. But the check for table exists doesn't use the quotes. And your code fall through the path that tries to create again the table with the quotes. Just remove the quotes around the name. They are not needed.
You can use the SqlClientConnection to get list of all objects in the db.
private void checkTable()
{
string tableName = quotenameTxt.Text + "-" + firstTxt.Text + "-" + surenameTxt.Text;
string connStr = #"Data Source=|DataDirectory|\LWADataBase.sdf";
using (SqlCeConnection conn = new SqlCeConnection(connStr))
{
bool isTableExist = conn.GetSchema("Tables")
.AsEnumerable()
.Any(row => row[2] == tableName);
}
if (!isTableExist)
{
MessageBox.Show("No such data table exists!");
}
else
{
MessageBox.Show("Such data table exists!");
}
}
Source: https://stackoverflow.com/a/3005157/1271037

Categories