How do I get sqldatareader to show the data in the fields? - c#

I have an sql server table located on my website (remote). The table is called table1 and contains a bunch of fields. My goal here is to read all the fields of table1 into an array to iterate.
Here is my attempt:
private static void ShowFields()
{
using (SqlConnection connection = new SqlConnection(connectionstring))
{
connection.Open();
SqlCommand command = new SqlCommand("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='table1'", connection);
SqlDataReader reader = command.ExecuteReader();
//connection.Close();
int colCount = reader.FieldCount;
while (reader.Read())
{
for (int i = 0; i < colCount; i++)
{
Console.WriteLine(reader[i]);
}
}
}
}
this nearly works, but it shows all the properties of the table, rather than the the data in the fields---e.g., varchar, 50 dao, table etc.
http://i.imgur.com/2bsgMBC.png

If I understand correctly you want the actual data IN the table, however you're querying INFORMATION_SCHEMA which gives you data ABOUT the tables/columns/whatever...
So just query the table like so:
SELECT * FROM table1
I don't know the column names in your table but if you want just some of the columns to show you can replace the * with a column list:
SELECT col1, col2, col3 FROM table1
where col1, col2 and col3 are just the names of the columns.
Is this what you were going for, or am I way off the mark here?

Related

How to import only non existing row from csv and update existing rows to SQL Database C#

With the help from the sylvan.data.csv package i'm now able to apply the scheme from the sql table to the csv. Yet the following problem arises where i want to check if a row from the csv exists in the sql database. If it does, it needs to be updated and if it does not only the non existing rows need to be imported. But with a bulkcopy this is not possible.
I've got the following code:
static void LoadTableCsv(SqlConnection conn, string tableName, string csvFile)
{
// read the column schema of the target table
var cmd = conn.CreateCommand();
conn.Open();
cmd.CommandText = $"select top 0 * from {tableName}"; // beware of sql injection
var reader = cmd.ExecuteReader();
var colSchema = reader.GetColumnSchema();
reader.Close();
// apply the column schema to the csv reader.
var csvSchema = new CsvSchema(colSchema);
var csvOpts = new CsvDataReaderOptions { Schema = csvSchema };
using var csv = CsvDataReader.Create(csvFile, csvOpts);
// Initialize SqlCommand for checking if the record already exists.
using var checkCommand = new SqlCommand("SELECT COUNT(*) FROM {tablename} WHERE TicketID = #value", conn);
checkCommand.Parameters.Add("#value", SqlDbType.Int, 10, "TicketID");
using var bulkCopy = new SqlBulkCopy(conn);
bulkCopy.DestinationTableName = tableName;
bulkCopy.EnableStreaming = true;
// Iterate through the records in the CSV file.
while (csv.Read())
{
// Set the value of the "#value" parameter
checkCommand.Parameters["#value"].Value = csv["TicketID"].ToString();
// Execute the check command to see if the record already exists.
var checkResult = (int)checkCommand.ExecuteScalar();
if (checkResult == 0)
{
// The record does not exist, write it to the SQL database using SqlBulkCopy.
bulkCopy.WriteToServer(new[] { csv });
}
else
{
// The record already exists, update it using an UPDATE statement.
using var updateCommand = new SqlCommand("UPDATE {tablename} SET Column1 = #col1, Column2 = #col2, Column3 = #col3, Column4 = #col4, Column5 = #col5, Column6 = #col6 WHERE TicketID = #value", conn);
// Add parameters for each column you want to update, using the names and types of the columns in the target table.
updateCommand.Parameters.Add("#col1", SqlDbType.Int, 10, "TicketID");
updateCommand.Parameters.Add("#col2", SqlDbType.NVarChar, 50, "TicketTitle");
updateCommand.Parameters.Add("#col3", SqlDbType.NVarChar, 50, "TicketStatus");
updateCommand.Parameters.Add("#col4", SqlDbType.NVarChar, 50, "CustomerName");
updateCommand.Parameters.Add("#col5", SqlDbType.NVarChar, 50, "TechnicianFullName");
updateCommand.Parameters.Add("#col6", SqlDbType.DateTime, 50, "TicketResolvedDate");
updateCommand.Parameters.Add("#value", SqlDbType.Int, 10, "TicketID");
// Set the values of the parameters to the values in the current row of the CSV file.
updateCommand.Parameters["#col1"].Value = int.Parse(csv["TicketID"].ToString());
updateCommand.Parameters["#col2"].Value = csv["TicketTitle"].ToString();
updateCommand.Parameters["#col3"].Value = csv["TicketStatus"].ToString();
updateCommand.Parameters["#col4"].Value = csv["CustomerName"].ToString();
updateCommand.Parameters["#col5"].Value = csv["TechnicianFullName"].ToString();
updateCommand.Parameters["#col6"].Value = DateTime.Parse(csv["TicketResolvedDate"].ToString());
updateCommand.Parameters["#value"].Value = int.Parse(csv["TicketID"].ToString());
// Execute the update command.
updateCommand.ExecuteNonQuery();
}
}
conn.Close();
}
But this gives me an error cause the bulkcopy can't read only one datarow.
Siggermannen's comment was the correct suggestion. You should bulk load all of the data into a temp table, then use SQL command(s) to merge the data from the temp table into the destination table. Ideally, you'd do this with a T-Sql Merge Statement. You could also use separate update and insert statements. This requires knowledge of the table columns to create the commands to merge the data. You can do this dynamically, by reading querying the INFORMATION_SCHEMA for the tables to determine the columns and keys, and using that to dynamically construct the merge statements. Or, if you know the schema at compile time, you can hard-code the statements which will be significantly easier to develop and test.
using Sylvan.Data.Csv;
using System.Data.SqlClient;
static void LoadTableCsv(SqlConnection conn, string tableName, string csvFile)
{
// read the column schema of the target table
var cmd = conn.CreateCommand();
cmd.CommandText = $"select top 0 * from {tableName}"; // beware of sql injection
var reader = cmd.ExecuteReader();
var colSchema = reader.GetColumnSchema();
reader.Close();
// create a temp table to load the data into
// using the destination table as a template
cmd.CommandText = $"select top 0 * into #load from {tableName}";
cmd.ExecuteNonQuery();
// apply the column schema to the csv reader.
var csvSchema = new CsvSchema(colSchema);
var csvOpts = new CsvDataReaderOptions { Schema = csvSchema };
using var csv = CsvDataReader.Create(csvFile, csvOpts);
// push *all* data into the temp table
using var bulkCopy = new SqlBulkCopy(conn);
bulkCopy.DestinationTableName = "#load";
bulkCopy.EnableStreaming = true;
bulkCopy.WriteToServer(csv);
// use sql commands to "MERGE" the data into the destination table
cmd.CommandText = $"""
insert into {tableName}
select * from #load l
where not exists (select * from {tableName} d where d.Id = l.Id)
""";
cmd.ExecuteNonQuery();
}
Doing insert/update statements in a loop is what you're trying to avoid. This produces "chatty" communication with the database where performance becomes dominated by overhead of each operation. Instead, you want to keep your operations as "chunky" as possble, which SqlBulkCopy and MERGE will provide.

MySQL trigger + insert

i'm trying to get my current id from a table pk to insert on another table fk, my next try is set a trigger inside database.
CREATE PROCEDURE `INSERIR CODIGO DISPENSACAO` ()
CREATE TRIGGER `productInsert`
BEFORE INSERT ON `produtos_disp`
FOR EACH ROW
BEGIN
set NEW.ID_PRODISP = (select max(ID)
from dispensacao p
);
END
what I want to set max id from dispensacao table which is going to be inserted from auto_increment on insert to it, on my fk codigo_disp for every row.
Managed to get max id using a combobox as descending order.
used
MySqlConnection connection = new MySqlConnection("connectionString");
string selectQuery = "select ID from dispensacao ORDER BY id DESC LIMIT 1";
connection.Open();
MySqlCommand command = new MySqlCommand(selectQuery, connection);
MySqlDataReader reader = command.ExecuteReader();
DataTable dt2 = new DataTable();
dt2.Load(reader);
Cmbid.DisplayMember = "ID";
Cmbid.DataSource = dt2;
this return the max id from the table and all you need to do is make it invisible so user won't change as it is a combobox

Fetch column schema from tables of mysql datatable

I want to fetch column schema with full details from Mysql Database.
I am having two tables where Customer table schema is like as
And another table Orders table schema is like this
I want to fetch these column schema with respect to my join query which is
using (MySqlConnection myConnection = new MySqlConnection("Server=xxxx;Uid=is;Pwd=password;Database=mydatabse"))
{
using (MySqlCommand cmd = new MySqlCommand("SELECT a.CustomerID, a.PostalCode, b.Freight, b.ShippedDate
FROM orders b, customers a
WHERE a.CustomerID = b.CustomerID Limit 5;", myConnection))
{
myConnection.Open();
using (MySqlDataReader mdr = cmd.ExecuteReader())
{
DataSet ds = new DataSet();
ds.Tables.Add("Customers");
mdr.Read();
DataRow dr;
dt.Columns.Add("ColName");
dt.Columns.Add("ColType");
for (int k = 0; k <= mdr.FieldCount - 1; k++)
{
dr = dt.NewRow();
dr["ColName"] = mdr.GetName(k);
dr["ColType"] = mdr.GetDataTypeName(k);
dt.Rows.Add(dr);
}
}
}
}
I need the full detail schema of the selected columns which I have used in join query.
I have used GetDataTypeName() to retrieve column type but it only returns datatype.
I found this query
select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'nsightsmysqldata' AND TABLE_NAME = 'customers';
which returns all detail relating to that table but I need only those column details which I had used in join query above.
Please provide me possible solution.
It turns out that you can do that with the
connection object
Well at least it worked for me in powershell.
$conn.GetSchema("Columns") will return every column schema in the current database as a DataTable

C#: Find tables involved in SQL query

Given a SQL query variable, i.e.,
string mySQLQuery = "SELECT TableA.Field1, TableA.Field2,..., TableB.Field1, TableB.Field2,.... FROM TableA LEFT OUTER JOIN TableB ON TableA.Field1 = TableB.Field1"
Is there any straight way I can extract the fields and the table names within the query in two lists? so:
List "Fields":
All fields From table A, table B (and others I could add by joining) with their table prefix (even if there were only one table in a simple 'SELECT * FROM TableA', I'd still need the 'TableA.' prefix).
All fields From table B with their table prefix, by adding them to the list in the usual fieldList.Add() way through looping.
List "Tables":
All tables involved in the query in the usual tablesList.Add() way through looping.
My first approach would be to make a lot of substrings and comparisons, i.e., finding the FROM, then trimming left, the substring until the first blank space, then the JOIN, then trimming, then the first substring until the space..., but that doesn't seem the right way.
REEDIT
I know I can get all the fields from INFORMATION_SCHEMA.COLUMNS with all the properties (that comes later), but the problem is that for that query I need the tables to be known. My steps should be:
The query "SELECT [fields] FROM [tables]" comes from a Multiline Textbox so I can write a SQL Query to fetch the fields I'd like. I take the string by txtMyQuery.Text property.
Find the field in the SELECT query, and find what table belongs to in the FROM clause.
Store the field like [Table].[Field]in a string list List strFields = new List() by the strFields.Add() method;
Then, iterate through the list in a way like:
for (int i = 0; i < fieldList.Count; i++)
{
string mySqlQuery = "SELECT Table_Name, Column_Name, Data_Type FROM INFORMATION_SCHEMA.COLUMNS
WHERE (COLUMN_NAME + "." + TABLE_NAME) ='" + fieldList[i] + "'";
//Commit query, get results in a gridview, etc.
}
Sure,
Tables:
SELECT TABLE_NAME FROM information_schema.TABLES
Fields:
SELECT * FROM information_schema.COLUMNS WHERE TABLE_NAME = N'Your Table'
Ok, after a while, I found SOME way to make this happen... I will work this out as I improve the solution (i.e., now it doesn't work if we use * selections like 'SELECT * FROM TableA', doesn't support aliasing, and all fields in the SELECT should be [table].[field], but will give an idea of what I'm trying to achieve):
This way, I write an SQL statement in a textbox, I pass it onto a new form to check the actual results of the query (if I needed, by clicking on a button "Preview"), and I populate a datagridview with the SCHEMA data I wanted to retrieve.
Thanks to all for your support!
private void btnQuery_Click(object sender, EventArgs e)
{
string strSql = this.txtQuery.Text;
DataTable dt = new DataTable();
String conStr = "Data Source=(LocalDB)\\v11.0;AttachDbFilename=|DataDirectory|\\TestDB.mdf;Integrated Security=True;Connect Timeout=30";
using (SqlConnection conn = new SqlConnection(conStr))
{
//with the call to strSQLSchema, we get the table involved in the query, to retrieve the fields and properties
SqlCommand cmd = new SqlCommand(strSQLSchema(strSql), conn);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
try
{
adapter.Fill(dt);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
this.dgvColumns.DataSource = dt;
}
private void btnPreview_Click(object sender, EventArgs e)
{
//We must pass the sql query to preview
string strSql = this.txtQuery.Text;
SQLQueryDataPreview qp = new SQLQueryDataPreview(strSql);
qp.Show();
}
private string strSQLSchema(string sqlQuery)
{
//we cut the "SELECT " start
sqlQuery = sqlQuery.ToUpper();
sqlQuery = sqlQuery.Substring(7);
//we take all the fields until the FROM
int myIndex = sqlQuery.IndexOf("FROM");
sqlQuery = sqlQuery.Substring(0, myIndex);
sqlQuery = sqlQuery.Trim();
sqlQuery = sqlQuery.Replace(" ", string.Empty);
sqlQuery = sqlQuery.Replace("\r\n", string.Empty);
//Here we add all fields to a list... so far, "*" is not allowed, and all fields should be written [Table].[Name]
string[] myFields = sqlQuery.Split(new char[] {' ', ','});
List <string> myTables = new List<string>();
//We will use this WHERE to find the fields in the SCHEMA. This WHERE first sentence helps to construct a valid where
//and avoid problems with the 'OR' clause in each loop.
string myWhere = "TABLE_NAME + '.' + COLUMN_NAME = ''";
for (int i = 0; i < myFields.Count(); i++)
{
//here we take the table prefix and add it to an array
int tableIndex = myFields[i].IndexOf(".");
if (tableIndex != -1)
{
myTables.Add(myFields[i].Substring(0, tableIndex));
myWhere += "OR (TABLE_NAME + '.' + COLUMN_NAME = '" + myFields[i] + "')";
}
}
//this is a List where we keep the tables derivated from names. We just copy the list generated before with a DISTINCT to eliminate duplicates.
myTables = myTables.Distinct().ToList();
string schema = "SELECT Table_Name, Column_Name, Data_Type FROM INFORMATION_SCHEMA.COLUMNS WHERE " + myWhere;
return schema;
}
}

How to get column names of table at runtime in C#?

Lets say I have a table in SQLServer named MyTable
ID FirstName LastName
1 Harry Dan
2 Maria Vicente
3 Joe Martin
Now if I have to insert any data in table, I will simply fire Insert Query like this
INSERT INTO MyTable (ID, FirstName, LastName) VALUES (4, Smith, Dan);
But what if I don't know the column names beforehand, I only know table name. Then is there a way to get the column name of table at runtime?
You can use sql-
SELECT name FROM sys.columns WHERE object_id = OBJECT_ID('TABLE_NAME')
Or you can query for SELECT TOP 0 * FROM TableName. Then, you can get the columns:
using(var reader = command.ExecuteReader())
{
reader.Read();
var table = reader.GetSchemaTable();
foreach (DataColumn column in table.Columns)
{
Console.WriteLine(column.ColumnName);
}
}
Another option using pure C# / .NET code:
First a helper method, that here returns a simple list of column names. Using a DataTable to hold table schema information, means that other information can also be retreived for each column, fx. if it is an AutoIncreament column etc.
private IEnumerable<string> GetColumnNames(string conStr, string tableName)
{
var result = new List<string>();
using (var sqlCon = new SqlConnection(conStr))
{
sqlCon.Open();
var sqlCmd = sqlCon.CreateCommand();
sqlCmd.CommandText = "select * from " + tableName + " where 1=0"; // No data wanted, only schema
sqlCmd.CommandType = CommandType.Text;
var sqlDR = sqlCmd.ExecuteReader();
var dataTable = sqlDR.GetSchemaTable();
foreach (DataRow row in dataTable.Rows) result.Add(row.Field<string>("ColumnName"));
}
return result;
}
The method can be called as:
var sortedNames = GetColumnNames("Data Source=localhost;Initial Catalog=OF2E;Integrated Security=SSPI", "Articles").OrderBy(x => x);
foreach (var columnName in sortedNames) Console.WriteLine(columnName);
Simply by this Query :
SELECT * FROM sys.columns
WHERE object_id = OBJECT_ID('dbo.[table_name]')
OR This Query :
SELECT COLUMN_NAME
FROM information_schema.columns
WHERE TABLE_NAME = [table_name]
ORDER BY COLUMN_NAME
var ColName = "";
var model = new AttributeMappingSource().GetModel(typeof(DataClassesDataContext));
foreach (var mt in model.GetTables())
{
if (mt.TableName == "dbo.Table_Name")
{
foreach (var dm in mt.RowType.DataMembers)
{
ColName = dm.MappedName + ", ";
Response.Write(ColName);
}
}
}
This question was answered before in various threads
check:
How can I get column names from a table in SQL Server?
How can I get column names from a table in Oracle?
How do I list all the columns in a table?

Categories

Resources