I'm doing a small program in C#, and I get the "invalid column name" error using the reader, although my column name exists. I looked for previous answers on StackOverflow, but didn't really understand the answers given.. Can someone explains me :
Why it bugs ?
How to solve this problem ?
Here is my code.
[HttpGet]
[Route("IP/{Station=0}/{Timestp=0}")]
public ActionResult IP(int station, string timestp)
{
timestp = timestp.Replace("_", ":");
SqlConnection myConn = new SqlConnection(#"Server=(LocalDb)\MSSqlLocalDB;Integrated security=SSPI;database=Serene7_Default_v1");
List<string> listeIps = new List<string>();
string listDb = string.Format("SELECT DISTINCT IdSource FROM [Serene5_Default_v1].[tcpdump].[TCPDump] WHERE Station = '{0}' AND TimeStp = '{1}';",station,timestp);
SqlCommand myCommand = new SqlCommand(listDb, myConn);
string listDb2 = string.Format("SELECT DISTINCT IdSource,IdDestination FROM [Serene5_Default_v1].[tcpdump].[TCPDump] WHERE Station = {0} AND TimeStp = '{1}';", station, timestp);
SqlCommand myCommand2 = new SqlCommand(listDb2, myConn);
myConn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Serene7_Default_v1.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'TCPDump';", myConn);
//opens connection
SqlDataReader reader5 = cmd.ExecuteReader();
while (reader5.Read())
{
System.Diagnostics.Debug.WriteLine(reader5[3]);
// read 'name' column
}
reader5.Close();
SqlDataReader reader = myCommand.ExecuteReader();
while (reader.Read())
{
listeIps.Add(reader[0].ToString());
}
reader.Close();
// some stuff
return View(MVC.Views.Common.Dashboard.DashboardIndex, dashboard);
}
The reader5 returns :
IdTransmission
TimeStp
IdSource
IdDestination
PortSource
PortDestination
Protocol
ToTheRight
ToTheLeft
Station
and the error is :
Invalid column name 'Station'
Select query runs on [Serene5_Default_v1] DB, but you list table columns from [Serene7_Default_v1] DB. The table's schema in this two different DB equal?
Remove () around column list:
SELECT DISTINCT (IdSource,IdDestination)
=>
SELECT DISTINCT IdSource,IdDestination
DISTINCT can accept () only for one column.
Rextester Demo
Second thing:
string.Format("SELECT DISTINCT (IdSource,IdDestination) FROM [Serene5_Default_v1].[tcpdump].[TCPDump] WHERE Station = {0} AND TimeStp = '{1}';", station, timestp);
using string.Format could lead to SQL Injection attack. I strongly suggest to use parametrized query instead.
More info: Parameterized Queries
Related
MySqlConnection con = GetConnection();
string sql = "SELECT COUNT(ID) FROM booking WHERE DATE(Booking_Date) = CURDATE()";
string sql2 = "SELECT SUM(Fare) FROM booking WHERE DATE(Booking_Date) = CURDATE()";
MySqlCommand cmd = new MySqlCommand(sql, con);
cmd.CommandType = System.Data.CommandType.Text;
MySqlDataReader BookingToday = cmd.ExecuteReader();
while (BookingToday.Read())
{
label6.Text = BookingToday.GetValue(0).ToString();
}
This is my C# code and I want to run both the given queries to get result at once. But I don't know how to run multiple data readers on single connection or run 2 connections at once. Anyone please help me in this regard
You can use one query:
string sql = "SELECT COUNT(ID), SUM(Fare) FROM booking WHERE DATE(Booking_Date) = CURDATE()";
In this specific case: you don't need to - you can use the code shown in slaakso's answer to perform both aggregates in one query.
In the more general case:
using (var reader = cmd1.ExecuteReader())
{
// while .Read(), etc
}
using (var reader = cmd2.ExecuteReader())
{
// while .Read(), etc
}
i.e. sequentially and not overlapping (unless your provider supports the equivalent of "MARS").
You can also often issue multiple queries (select) in a single command; you use NextResult() to move between them:
using (var reader = cmd.ExecuteReader())
{
// while .Read(), etc, first grid
if (reader.NextResult())
{
// while .Read(), etc, second grid
}
}
You can't open multiple data readers simultaneously from a single connection.
If you want a single result, you can use ExecuteScalar.
var result1 = (DateTime)new MySqlCommand("select now()", con).ExecuteScalar();
Console.WriteLine(result1);
var result2 = (DateTime)new MySqlCommand("select date_add(now(), interval 10 day)", con).ExecuteScalar();
Console.WriteLine(result2);
For multi-row results, you can store them in a DataTable.
var dt1 = new DataTable();
dt1.Load(new MySqlCommand("select id from table1", con).ExecuteReader());
var dt2 = new DataTable();
dt2.Load(new MySqlCommand("select name from table1", con).ExecuteReader());
foreach(var row in dt1.AsEnumerable())
{
Console.WriteLine($"id:{row["id"]}");
}
foreach (var row in dt2.AsEnumerable())
{
Console.WriteLine($"name:{row["name"]}");
}
*sorry the title cannot be too long so I have to cut it down.
hi, I would like to get the total duration from my database table where the
dateID is 1 and the year of the datetime is 2016 using SQL Query in asp.net c#. I tried using the codes below but it does not work. It says 'Additional information: Column 'RecordsDate.StartDateTime' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.' Can anyone tell me what's wrong with my SQL Query please? Thanks a lot :)
float totalDuration = 0f;
string sQuery = "SELECT sum(Duration) AS TotalDuration FROM RecordsDate WHERE DateID='1' HAVING DATEPART(yyyy, StartDateTime) = '2016'";
SqlConnection conn = new SqlConnection(_connStr);
SqlCommand cmd = new SqlCommand(sQuery, conn);
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
totalDuration = float.Parse(dr["TotalDuration"].ToString());
}
conn.Close();
dr.Close();
Provide group by with some column name from the table:
select sum(duration) as totalduration from recordsdate
where dateid='1' having datepart(yyyy, startdatetime) = '2016'
group by ?
I have the following code:
SqlConnection c = new SqlConnection("Data Source=localhost\\sqlexpress;Initial Catalog=BookStoreDataBase1;Integrated Security=True;Pooling=False;");
c.Open();
string raf = string.Format("Select Id from Customer WHERE email='{0}'", DropDownList1.SelectedItem.Text);
SqlCommand comm2 = new SqlCommand(raf, c);
SqlDataReader r = comm2.ExecuteReader();
The object r now has the value of the query which is a row contains that the Id where email equals to random value from drop down list.
what I want is to get the exact value of that "Id" and assign it to label. please help me.
First of all your query is open to SQL Injection attack so change it like this:-
string raf = "Select Id from Customer WHERE email= #Email";
SqlCommand comm2 = new SqlCommand(raf, c);
cmoo2.Parameters.Add("#Email",SqlDbType.NVarchar,20).Value =
DropDownList1.SelectedItem.Text;
You are fetching just one value so it can be done use ExecuteScalar like this:-
labelid.Text= cmoo2.ExecuteScalar.ToString();
But If you want to use SqlDataReader object then it will return the value when you call the Read method:-
using(SqlDataReader reader= cmoo2.ExecuteReader())
{
while (reader.Read())
{
labelid.Text= reader["Id"].ToString();
}
}
There are lot of examples already in stack overflow regarding this topic you can refer any of that answers or try this
using(SqlDataReader r= cmd.ExecuteReader())
{
while (r.Read())
{
var myString = r.GetString(0); //The 0 stands for "the 0'th column", so the first column of the result.
labelid.Text=myString;
}
}
I have this legacy code :
private void conecta()
{
if (conexao.State == ConnectionState.Closed)
conexao.Open();
}
public List<string[]> get_dados_historico_verificacao_email_WEB(string email)
{
List<string[]> historicos = new List<string[]>();
conecta();
sql =
#"SELECT *
FROM historico_verificacao_email
WHERE nm_email = '" + email + #"'
ORDER BY dt_verificacao_email DESC, hr_verificacao_email DESC";
com = new SqlCommand(sql, conexao);
SqlDataReader dr = com.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read())
{
string[] dados_historico = new string[6];
dados_historico[0] = dr["nm_email"].ToString();
dados_historico[1] = dr["dt_verificacao_email"].ToString();
dados_historico[1] = dados_historico[1].Substring(0, 10);
dados_historico[2] = dr["hr_verificacao_email"].ToString();
dados_historico[3] = dr["ds_tipo_verificacao"].ToString();
sql =
#"SELECT COUNT(e.cd_historico_verificacao_email) QT
FROM emails_lidos e
WHERE e.cd_historico_verificacao_email =
'" + dr["cd_historico_verificacao_email"].ToString() + "'";
tipo_sql = "seleção";
conecta();
com2 = new SqlCommand(sql, conexao);
SqlDataReader dr3 = com2.ExecuteReader();
while (dr3.Read())
{
//quantidade de emails lidos naquela verificação
dados_historico[4] = dr3["QT"].ToString();
}
dr3.Close();
conexao.Close();
//login
dados_historico[5] = dr["cd_login_usuario"].ToString();
historicos.Add(dados_historico);
}
dr.Close();
}
else
{
dr.Close();
}
conexao.Close();
return historicos;
}
I have created two separates commands to correct the issue, but it still continues: "There is already an open DataReader associated with this Command which must be closed first".
An additional info: the same code is working in another app.
Just add the following in your connection string:
MultipleActiveResultSets=True;
The optimal solution could be to try to transform your solution into a form where you don't need to have two readers open at a time. Ideally it could be a single query. I don't have time to do that now.
If your problem is so special that you really need to have more readers open simultaneously, and your requirements allow not older than SQL Server 2005 DB backend, then the magic word is MARS (Multiple Active Result Sets). http://msdn.microsoft.com/en-us/library/ms345109%28v=SQL.90%29.aspx. Bob Vale's linked topic's solution shows how to enable it: specify MultipleActiveResultSets=true in your connection string. I just tell this as an interesting possibility, but you should rather transform your solution.
in order to avoid the mentioned SQL injection possibility, set the parameters to the SQLCommand itself instead of embedding them into the query string. The query string should only contain the references to the parameters what you pass into the SqlCommand.
You can get such a problem when you are two different commands on same connection - especially calling the second command in a loop. That is calling the second command for each record returned from the first command. If there are some 10,000 records returned by the first command, this issue will be more likely.
I used to avoid such a scenario by making it as a single command.. The first command returns all the required data and load it into a DataTable.
Note: MARS may be a solution - but it can be risky and many people dislike it.
Reference
What does "A severe error occurred on the current command. The results, if any, should be discarded." SQL Azure error mean?
Linq-To-Sql and MARS woes - A severe error occurred on the current command. The results, if any, should be discarded
Complex GROUP BY on DataTable
I suggest creating an additional connection for the second command, would solve it. Try to combine both queries in one query. Create a subquery for the count.
while (dr3.Read())
{
dados_historico[4] = dr3["QT"].ToString(); //quantidade de emails lidos naquela verificação
}
Why override the same value again and again?
if (dr3.Read())
{
dados_historico[4] = dr3["QT"].ToString(); //quantidade de emails lidos naquela verificação
}
Would be enough.
I bet the problem is being shown in this line
SqlDataReader dr3 = com2.ExecuteReader();
I suggest that you execute the first reader and do a dr.Close(); and the iterate historicos, with another loop, performing the com2.ExecuteReader().
public List<string[]> get_dados_historico_verificacao_email_WEB(string email)
{
List<string[]> historicos = new List<string[]>();
conecta();
sql = "SELECT * FROM historico_verificacao_email WHERE nm_email = '" + email + "' ORDER BY dt_verificacao_email DESC, hr_verificacao_email DESC";
com = new SqlCommand(sql, conexao);
SqlDataReader dr = com.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read())
{
string[] dados_historico = new string[6];
dados_historico[0] = dr["nm_email"].ToString();
dados_historico[1] = dr["dt_verificacao_email"].ToString();
dados_historico[1] = dados_historico[1].Substring(0, 10);
//System.Windows.Forms.MessageBox.Show(dados_historico[1]);
dados_historico[2] = dr["hr_verificacao_email"].ToString();
dados_historico[3] = dr["ds_tipo_verificacao"].ToString();
dados_historico[5] = dr["cd_login_usuario"].ToString();
historicos.Add(dados_historico);
}
dr.Close();
sql = "SELECT COUNT(e.cd_historico_verificacao_email) QT FROM emails_lidos e WHERE e.cd_historico_verificacao_email = '" + dr["cd_historico_verificacao_email"].ToString() + "'";
tipo_sql = "seleção";
com2 = new SqlCommand(sql, conexao);
for(int i = 0 ; i < historicos.Count() ; i++)
{
SqlDataReader dr3 = com2.ExecuteReader();
while (dr3.Read())
{
historicos[i][4] = dr3["QT"].ToString(); //quantidade de emails lidos naquela verificação
}
dr3.Close();
}
}
return historicos;
Add MultipleActiveResultSets=true to the provider part of your connection string. See the example below:
<add name="DbContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=dbName;Persist Security Info=True;User ID=userName;Password=password;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
Try to combine the query, it will run much faster than executing an additional query per row.
Ik don't like the string[] you're using, i would create a class for holding the information.
public List<string[]> get_dados_historico_verificacao_email_WEB(string email)
{
List<string[]> historicos = new List<string[]>();
using (SqlConnection conexao = new SqlConnection("ConnectionString"))
{
string sql =
#"SELECT *,
( SELECT COUNT(e.cd_historico_verificacao_email)
FROM emails_lidos e
WHERE e.cd_historico_verificacao_email = a.nm_email ) QT
FROM historico_verificacao_email a
WHERE nm_email = #email
ORDER BY dt_verificacao_email DESC,
hr_verificacao_email DESC";
using (SqlCommand com = new SqlCommand(sql, conexao))
{
com.Parameters.Add("email", SqlDbType.VarChar).Value = email;
SqlDataReader dr = com.ExecuteReader();
while (dr.Read())
{
string[] dados_historico = new string[6];
dados_historico[0] = dr["nm_email"].ToString();
dados_historico[1] = dr["dt_verificacao_email"].ToString();
dados_historico[1] = dados_historico[1].Substring(0, 10);
//System.Windows.Forms.MessageBox.Show(dados_historico[1]);
dados_historico[2] = dr["hr_verificacao_email"].ToString();
dados_historico[3] = dr["ds_tipo_verificacao"].ToString();
dados_historico[4] = dr["QT"].ToString();
dados_historico[5] = dr["cd_login_usuario"].ToString();
historicos.Add(dados_historico);
}
}
}
return historicos;
}
Untested, but maybee gives some idea.
I have a DataTable obtained from a SQL DataBase, like this:
using (SqlCommand cmd = new SqlCommand(query, _sqlserverDB))
{
using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
{
DataSet dataSet = new DataSet();
adapter.Fill(dataSet);
result = (dataSet != null && dataSet.Tables != null && dataSet.Tables.Count > 0) ? dataSet.Tables[0] : null;
}
}
When I try to get the DataType of each column through dataColumn.DataType , I get the C# types (Int32, Int64, String, etc).
QUESTION: How can I access the native SQL data types (varchar, nvarchar, bigint...) instead of the C# types?
I have tried dataColumn.DataType.UnderlyingSystemType and the result is the same.
Of course it is possible to take SqlDbType of a column, the answer is here on SO: link.
SqlCommand cmd = connection.CreateCommand();
cmd.CommandText = "SET FMTONLY ON; select column from table; SET FMTONLY OFF";
SqlDataReader reader = cmd.ExecuteReader();
SqlDbType type = (SqlDbType)(int)reader.GetSchemaTable().Rows[0]["ProviderType"];
You cannot because System.Data.DataTable (or DataColumn, or DataSet, or DataRow...) is a generic .NET data container which works the same way regardless on the specific database engine you loaded your data from.
this means that provided you used a .NET Connector for SQL Server, MySQL, Access, PostgreSQL or anything else, the DataTable and DataColumn classes are always the same and being ADO.NET objects are generic to work with any db engine, so the columns are typed with the .NET types as you have found out.
SqlConnection SqlCon = new SqlConnection("Data Source=(local);Database=dbname;Integrated Security=SSPI;");
SqlCon.Open();
SqlCmd = SqlCon.CreateCommand();
SqlCmd.CommandText = "select * from Tablename";
SqlDataReader SqlDr = SqlCmd.ExecuteReader();
SqlDr.Read();
int i = 0;
while (i < SqlDr.FieldCount)
{
MessageBox.Show(SqlDr.GetDataTypeName(i));
i++;
}
Another approach is to let SQL do the work for you:
SqlConnection rConn = connectToSQL(); //returns sql connection
SqlCommand SqlCmd = new SqlCommand();
SqlCmd = rConn.CreateCommand();
SqlCmd.CommandText = "SELECT ORDINAL_POSITION, " +
"COLUMN_NAME, " +
"DATA_TYPE, " +
"CHARACTER_MAXIMUM_LENGTH, " +
"IS_NULLABLE " +
"FROM INFORMATION_SCHEMA.COLUMNS " +
"WHERE TABLE_NAME = 'TableName'";
SqlDataReader SqlDr = SqlCmd.ExecuteReader();
SqlDr.Read();
while (SqlDr.Read()) {
var OrdPos = SqlDr.GetValue(0);
var ColName = SqlDr.GetValue(1);
var DataType = SqlDr.GetValue(2);
var CharMaxLen = SqlDr.GetValue(3);
var IsNullable = SqlDr.GetValue(4);
Console.WriteLine("ColName - " + ColName + " DataType - " + DataType + " CharMaxLen - " + CharMaxLen);
}
As David says ... you are in .NET so the types will be .NET types. This is a listing of type mappings from SQL Server to .Net that shows you what .NET type you will end up with for a given Sql column type .. hope this helps ..
http://msdn.microsoft.com/en-us/library/ms131092.aspx
Building upon Madhukar Krishna's answer, if you have a SQLDataReader or a MySQLDataReader object you can obtain the SQL type metadata for a given column (in the code, we obtain the metadata of column with index 1) using the following code (example working for MySQLDataReader object):
...
MySqlDataReader dr = ...
Console.WriteLine("dr.GetFieldType(1) = {0}, dr.GetName(1) = {1}, dr.GetValue(1) = {2}, dr.GetDataTypeName(1) = {3}",
dr.GetFieldType(1), dr.GetName(1), dr.GetValue(1), dr.GetDataTypeName(1));
bool b = Enum.TryParse(dr.GetDataTypeName(1), true, out System.Data.SqlDbType mySqlDbTypeEnum);
Console.WriteLine("mySqlDbTypeEnum = {0}, b = {1}", mySqlDbTypeEnum, b);
The line:
bool b = Enum.TryParse(dr.GetDataTypeName(1), true, out System.Data.SqlDbType mySqlDbTypeEnum);
is used to obtain the System.Data.SqlDbType from a String, and ignoring the letter case, e.g. if dr.GetDataTypeName(1) returns "VARCHAR" then the System.Data.SqlDbType enum value is System.Data.SqlDbType.VarChar.
Then, you can get get the size of the data type (for instance VARCHAR(15)) by inspecting the SQL columns metadata with the following code (source MSDN):
... (continuation)
DataTable schemaTable;
// Retrieve column schema into a DataTable.
schemaTable = dr.GetSchemaTable();
// For each field in the table...
foreach (DataRow myField in schemaTable.Rows)
{
// For each property of the field...
foreach (DataColumn myProperty in schemaTable.Columns)
{
// Display the field name and value.
Console.WriteLine(myProperty.ColumnName + " = " + myField[myProperty].ToString());
}
Console.WriteLine();
// Pause.
//Console.ReadLine();
}
The property ColumnSize gives the size information.
If you are using DataReader -
SqlDataReader reader = cmd.ExecuteReader();
reader.GetDataTypeName(int ordinal)
should work if you want the SQL data type of a column