C# SQL parameterized SQL Query - c#

I just keep on getting Syntax error when I used parameterized sql query.
public List<string> Cat(string product,string table)
{
List<string> Products = new List<string>();
Global global = new Global();
string sql = "SELECT #prod FROM #tbl";
MySqlConnection connection = new MySqlConnection(global.ConnectionString);
MySqlCommand command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("#prod", product);
command.Parameters.AddWithValue("#tbl", table);
connection.Open();
MySqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
Products.Add(reader.GetString("#prod"));
}
connection.Close();
return Products;
}
public List<string> CallProducts(string category)
{
string table;
string product;
List<string> stacks = new List<string>();
if (category == "Accessories")
{
product = "Accessories_Name";
table = "tbl_accessories";
stacks.AddRange(Cat(product, table).ToArray());
}
else if (category == "Batteries")
{
table = "tbl_batteries";
}
else if (category == "Cotton")
{
table = "tbl_cotton";
}
else if (category == "Juices")
{
table = "tbl_juices";
}
else if (category == "Kits")
{
table = "tbl_kits";
}
else if (category == "Mods")
{
table = "tbl_mods";
}
else
{
table = "tbl_vapeset";
}
return stacks;
}
I just keep on getting SQL Syntax Error. It works if the table and the name is manually inputted rather than using parameters.
Hoping you can help.
Need for a project.
Thanks!

Correct use would be:
string sql = $"SELECT {product} FROM {table}";
Because table and column are not parameters.
Moreover, I would recommend using Command.Parameters.Add(...).Value(...),
over Parameters.AddWithValue, since in first approach you can explicitly decide what datatype you want to pass and prevent SQL from guessing it.

Related

Get an identity value in a controller for INSERT (Execute Insert command and return inserted Id in Sql)

Currently, I am using MVC on creating a project. Now I want to insert an Identity ID value into an INSERT statement.
In my controller:
string payment = #"INSERT INTO Payment(Payment_Method,Currency_Type,Total_Amount)
VALUES('{0}','{1}',{2})";
int pay = DBUtl.ExecSQL(payment, "Cash", currency,total);
if (pay == 1)
{
string pid = "SELECT TOP 1 Payment_id FROM Payment ORDER BY Payment_id DESC";
int paymentid = DBUtl.ExecSQL(pid);
if (cart.Count() != 0)
{
string order = #"INSERT INTO [Order](Order_Name,Order_Description,Order_Quantity,Payment_Id)
VALUES('{0}','{1}',{2},{3})";
Now, I want to the payment_id that already been inserted into the payment table which is the first statement and retrieve the payment_id and use it into the INSERT statement for the Order table
How can I achieve that?
Please Help
Thank you
Actually, DBUtil code consists of:
public static int ExecSQL(string sql, params object[] list)
{
List<String> escParams = new List<String>();
foreach (object o in list)
{
if (o == null)
escParams.Add("");
else
escParams.Add(EscQuote(o.ToString()));
}
DB_SQL = String.Format(sql, escParams.ToArray());
int rowsAffected = 0;
using (SqlConnection dbConn = new SqlConnection(DB_CONNECTION))
using (SqlCommand dbCmd = dbConn.CreateCommand())
{
try
{
dbConn.Open();
dbCmd.CommandText = DB_SQL;
rowsAffected = dbCmd.ExecuteNonQuery();
}
catch (System.Exception ex)
{
DB_Message = ex.Message;
rowsAffected = -1;
}
}
return rowsAffected;
}
And as you know ExecuteNonQuery denotes the numbers affecting the row
So, You can do as shown below:
FOR SQL SERVER 2005 and above
using (SqlConnection con=new SqlConnection(#"Your connectionString"))
{
using(SqlCommand cmd=new SqlCommand("INSERT INTO Payment(Payment_Method,Currency_Type,Total_Amount) output INSERTED.Payment_id VALUES(#Payment_Method,#Currency_Type,#Total_Amount)",con))
{
cmd.Parameters.AddWithValue("#Payment_Method", "Cash");
cmd.Parameters.AddWithValue("#Currency_Type", currency);
cmd.Parameters.AddWithValue("#Total_Amount", total);
con.Open();
int payment_id =Convert.ToInt32(cmd.ExecuteScalar());
if (con.State == System.Data.ConnectionState.Open)
con.Close();
return payment_id ;
}
}
also, you can change your query to:
"INSERT INTO Payment(Payment_Method,Currency_Type,Total_Amount) VALUES(#Payment_Method,#Currency_Type,#Total_Amount)"; SELECT SCOPE_IDENTITY()"
For now, I tried something like this:
public IActionResult SaveDetail(List<Cart_Has_Services> cart,double total,string currency)
{
string payment = #"INSERT INTO Payment(Payment_Method,Currency_Type,Total_Amount)
VALUES('{0}','{1}',{2});";
int pay = DBUtl.ExecSQL(payment, "Cash", currency,total);
if (pay == 1)
{
object pid = DBUtl.GetList("SELECT Payment_id FROM Payment");
int paymentid = Convert.ToInt32(pid);
if (cart.Count() != 0)
{
string order = #"INSERT INTO [Order](Order_Name,Order_Description,Order_Quantity,Payment_Id)
VALUES('{0}','{1}',{2},{3})";
foreach (var item in cart)
{
int ord = DBUtl.ExecSQL(order, item.Cart_Service, item.Additional_Notes, item.Quantity,paymentid);
As for now, the codes will run by inserting the values into the payment table. After that, I want to get the payment_id for my next insert which is the order table.
The method that I tried to get the payment_id does not work.

How to return results from SQL SELECT raw Query?

I have a method in my controller class that is supposed to return the results from a raw SQL query inside the method. The problem is I can't pull return more than one column result to the list in a query that is supposed to return multiple column results.
I know that the problem has to do with how I am adding to the results list during the Read, but I am unsure how to structure this properly to return multiple values.
Here is my current method:
public IActionResult Search ([FromRoute]string input)
{
string sqlcon = _iconfiguration.GetSection("ConnectionStrings").GetSection("StringName").Value;
List<string> results = new List<string>();
using (var con = new SqlConnection(sqlcon))
{
using (var cmd = new SqlCommand()
{
CommandText = "SELECT u.UserID, u.User FROM [dbo].[Users] u WHERE User = 'Value';",
CommandType = CommandType.Text,
Connection = con
})
{
con.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
results.Add(reader.GetString(0));
}
con.Close();
return Ok(new Search(results));
}
}
}
}
The SQL query is supposed to return the UserID and User based on the entered User, however, only the User gets returned here.
Does anyone know what I am missing to return multiple column names for this SQL query and method? Any advice would be greatly appreciated.
FYI, I can't use a stored procedure here, I do not have permission to create an SP on this database.
You can create a class for the results of the Query
public class ClassForResults(){
public int UserID { get; set; };
public string User { get; set; }
}
public IActionResult Search ([FromRoute]string input)
{
string sqlcon = _iconfiguration.GetSection("ConnectionStrings").GetSection("StringName").Value;
List<ClassForResults> results = new List<ClassForResults>();
using (var con = new SqlConnection(sqlcon))
{
using (var cmd = new SqlCommand()
{
CommandText = "SELECT u.UserID, u.User FROM [dbo].[Users] u WHERE User = 'Value';",
CommandType = CommandType.Text,
Connection = con
})
{
con.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
ClassForResults result = new ClassForResults();
result.UserID = reader.GetInt(0);
result.User = reader.GetString(1);
results.Add(result);
}
con.Close();
return Ok(new Search(results));
}
}
}
}

SQL query on ADO.net limitation with 2100+ parameters

I am trying to implement an ADO.NET code which executes the SQL query with multiple parameters. Looks like SQL parameter limit is 2100 and does not accept more than this limit. How do I achieve with my below code to have this accept more than the limitation.
I am finding it difficult to understand the implementations when validating online articles related how to send the queries in subsets or chunks to fulfill my request.
This is my code:
using (Connection = new SqlConnection(CS))
{
Connection.Open();
string query = "SELECT FamilyID, FullName, Alias FROM TABLE (nolock) WHERE FamilyID IN ({0})";
var stringBuiler = new StringBuilder();
var familyIds = new List<string>();
string line;
while ((line = TextFileReader.ReadLine()) != null)
{
line = line.Trim();
if (!familyIds.Contains(line) & !string.IsNullOrEmpty(line))
{
familyIds.Add(line);
}
}
var sqlCommand = new SqlCommand
{
Connection = Connection,
CommandType = CommandType.Text
};
var index = 0; // Reset the index
var idParameterList = new List<string>();
foreach (var familyId in familyIds)
{
var paramName = "#familyId" + index;
sqlCommand.Parameters.AddWithValue(paramName, familyId);
idParameterList.Add(paramName);
index++;
}
sqlCommand.CommandText = String.Format(query, string.Join(",", idParameterList));
var dt = new DataTable();
using (SqlDataReader sqlReader = sqlCommand.ExecuteReader())
{
dt.Load(sqlReader);
}
try
{
if (dt.Rows.Count > 0)
{
OutputdataGridView.DataSource = lstDownloadOwnerOutput;
OutputdataGridView.ColumnHeadersDefaultCellStyle.Font = new Font(DataGridView.DefaultFont, FontStyle.Bold);
OutputdataGridView.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
Gridviewdisplaylabel.Text = "Total no of rows: " + this.OutputdataGridView.Rows.Count.ToString();
}
else if (dt.Rows.Count == 0)
{
MessageBox.Show("Data returned blank!!!");
}
}
catch (Exception Ex)
{
if (Connection != null)
{
Connection.Close();
}
MessageBox.Show(Ex.Message);
}
}
Having a WHERE IN clause with 2100, or even 100, parameters is generally not good coding practice. You might want to consider putting those values into a separate bona fide table, e.g.
families (ID int PK, ...)
Then, you may rewrite your query as:
SELECT FamilyID, FullName, Alias
FROM TABLE (nolock)
WHERE FamilyID IN (SELECT ID FROM families);
You could also express the above using an EXISTS clause or a join, but all three approaches might just optimize to a very similar query plan anyway.
You can just add a table load call every 2000 parameters in your code:
var index = 0; // Reset the index
var idParameterList = new List<string>();
var dt = new DataTable();
foreach (var familyId in familyIds) {
var paramName = "#familyId" + index;
sqlCommand.Parameters.AddWithValue(paramName, familyId);
idParameterList.Add(paramName);
index++;
if (index > 2000) {
sqlCommand.CommandText = String.Format(query, string.Join(",", idParameterList));
using (SqlDataReader sqlReader = sqlCommand.ExecuteReader())
dt.Load(sqlReader);
sqlCommand.Parameters.Clear();
idParameterList.Clear();
index = 0;
}
}
if (index > 0) {
sqlCommand.CommandText = String.Format(query, string.Join(",", idParameterList));
using (SqlDataReader sqlReader = sqlCommand.ExecuteReader())
dt.Load(sqlReader);
}
For dynamic sql like this, I generally recommend using a Table-Valued Parameter.
It does require a bit of setup: you have to create a user-defined Type in the DB to hold the values, but that is a fairly trivial operation:
CREATE TYPE PrimaryKeyType AS TABLE ( VALUE INT NOT NULL );
We generally use these in conjunction with stored procedures:
CREATE PROCEDURE dbo.getFamily(#PrimaryKeys PrimaryKeyType READONLY)
AS
SELECT FamilyID, FullName, Alias
FROM TABLE (nolock) INNER JOIN #PrimaryKeys ON TABLE.FamilyID = #PrimaryKeys.Value
GO
However, you can also use inline SQL if you prefer.
Assigning the values to the stored proc or inline parameter is fairly straightforward, but there is one gotcha (more later):
public static void AssignValuesToPKTableTypeParameter(DbParameter parameter, ICollection<int> primaryKeys)
{
// Exceptions are handled by the caller
var sqlParameter = parameter as SqlParameter;
if (sqlParameter != null && sqlParameter.SqlDbType == SqlDbType.Structured)
{
// The type name may look like DatabaseName.dbo.PrimaryKeyType,
// so remove the database name if it is present
var parts = sqlParameter.TypeName.Split('.');
if (parts.Length == 3)
{
sqlParameter.TypeName = parts[1] + "." + parts[2];
}
}
if (primaryKeys == null)
{
primaryKeys = new List<int>();
}
var table = new DataTable();
table.Columns.Add("Value", typeof(int));
foreach (var wPrimaryKey in primaryKeys)
{
table.Rows.Add(wPrimaryKey);
}
parameter.Value = table;
}
The thing to watch out for here is the naming of the parameter. See the code in the method above that removes the database name to resolve this issue.
If you have dynamic SQL, you can generate a correct parameter using the following method:
public static SqlParameter CreateTableValuedParameter(string typeName, string parameterName)
{
// Exceptions are handled by the caller
var oParameter = new SqlParameter();
oParameter.ParameterName = parameterName;
oParameter.SqlDbType = SqlDbType.Structured;
oParameter.TypeName = typeName;
return oParameter;
}
Where typeName is the name of your type in the DB.

The specified field 'ID' could refer to more than one table

I keep getting this error when I try to find by ID:
system.data.oledb.oledbexception the speciefied field 'ID' could refer
to more than one table listed in the FROM clause of your SQL Statement
Here's my code:
public static Invoice GetInvoice(string id)
{
OleDbConnection conn = GetConnection();
Invoice invoice = null;
if (conn == null)
{
return null;
}
string sqlString = "SELECT * FROM Person INNER JOIN Employee ON " +
"Person.ID=Employee.ID WHERE ID = #ID";
OleDbCommand comm = new OleDbCommand(sqlString, conn);
comm.Parameters.AddWithValue("#ID", id);
OleDbDataReader dr = null;
try
{
conn.Open();
dr = comm.ExecuteReader(CommandBehavior.SingleRow);
if (dr.Read())
{
invoice = new Invoice();
invoice.PersonID = (string)dr["ID"];
invoice.FirstName = (string)dr["FirstName"];
invoice.LastName = (string)dr["LastName"];
invoice.Age = (int)dr["Age"];
}
}
catch (Exception ex)
{
invoice = null;
MessageBox.Show(ex.ToString());
}
finally
{
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
}
return invoice;
}
You need to change your query, at the moment you're selecting a wildcard '*', which means it will pull both the Persons ID and the Employee ID, but wont have a unique reference. Change your wildcard to pull the exact tables ID like below:
SELECT Person.ID, FirstName, LastName FROM...
You will also need to change your WHERE statement to something like:
WHERE Person.ID = #ID
as the where statement doesnt know which tables ID to filter on (i know they're the same values, but SQL doesnt care about that)

Retrieve List of Tables from Specific Database on Server C#

How can one retrieve the tables' names into a List<string> from a specific database on a server?
System.Data.SqlClient has what you need without a formal query on sys.Tables (though that's what it's using in the background). Use the GetSchema() method on the SqlConnection object and designate that you want the "Tables" and it will send you a DataTable object back with a row for each table. It sends back database name, table schema name, table name, and table type in each row (in that column order). The code would look like this:
public static List<string> GetTables(string connectionString)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
DataTable schema = connection.GetSchema("Tables");
List<string> TableNames = new List<string>();
foreach (DataRow row in schema.Rows)
{
TableNames.Add(row[2].ToString());
}
return TableNames;
}
}
For SQL Server 2005 and higher:
using (SqlConnection connection = new SqlConnection(#"Data Source=(local);Integrated Security=True;Initial Catalog=DB_Name;")) {
connection.Open();
using (SqlCommand command = connection.CreateCommand()) {
command.CommandText =
#"SELECT s.name, o.name
FROM sys.objects o WITH(NOLOCK)
JOIN sys.schemas s WITH(NOLOCK)
ON o.schema_id = s.schema_id
WHERE o.is_ms_shipped = 0 AND RTRIM(o.type) = 'U'
ORDER BY s.name ASC, o.name ASC";
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
string schemaName = reader.GetString(0);
string tableName = reader.GetString(1);
// your code goes here...
}
}
}
}
To fetch all the user defined tables from the DB ( SQlServer), we have to query system catalogs.
SELECT Name from Sysobjects where xtype = 'u'
this query will return all the user defined tables in the DataBase
I think that this SQL should return a table containing tableNames in your DB:
SELECT * FROM information_schema.tables
You can use LINQ query to get all tables name fairly easily:
public List<string> GetSqlTables(SqlConnection conn)
=> (from DataRow row in conn.GetSchema("Tables").Rows.Cast<DataRow>()
select row["TABLE_NAME"].ToString()).ToList();
.Net Core extension using SqlConnection that opens/closes the connection.
public static class SQLExtensions
{
public static List<string> GetTableNames(this SqlConnection connection)
{
using (connection)
{
connection.Open();
return connection.GetSchema("Tables")
.Rows
.Cast<DataRow>()
.Select(row => row["TABLE_NAME"].ToString())
.OrderBy(nme => nme)
.ToList();
} // Auto Close exiting the block
}
}
Usage (Linqpad)
var connectionStr = #"Data Source=.;Integrated Security=SSPI;Initial Catalog=Rasa";
(new SqlConnection(connectionStr).GetTableNames()).Dump();
this my solution:
public void opencon()
{
if (conn == null)
{
conn = new SqlConnection(#"Your connection");
}
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
}
public void Closecon()
{
if ((conn != null) && (conn.State == ConnectionState.Open))
{
conn.Close();
}
}
public void GetTables(ComboBox cb)
{
chuoiketnoi();
DataTable schema = conn.GetSchema("Tables");
foreach (DataRow row in schema.Rows)
{
cb.Items.Add(row[2].ToString());
}
dongketnoi();
}

Categories