Retrieving table schema information using C# - c#

I want to retrieve the table schema information using C#.
I used the sp_help <table_name> query to retrieve it, which works well when I execute it in query window but fail to retrieve it from C#. I want to retrieve all table information which is necessary for mapping between two tables such as name, datatype, size, isprimary, key, etc.
I had write down following code
SqlCommand cmd = sourceCon.CreateCommand();
cmd.CommandText = string.Format("sp_help '{0}'", cmbSourceTable.SelectedItem.ToString()); //cmd.CommandType = CommandType.StoredProcedure;
sourceDataTable = new DataTable();
SqlDataReader dr = cmd.ExecuteReader();
sourceDataTable.Load(dr);
dr.Close();
It will returns only info about table that when it is created or not

Instead of using sp_help, you could try the following options:
1) use INFORMATION_SCHEMA.COLUMNS view:
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'YourTable'
2) query sys.columns
SELECT c.*, t.name
FROM sys.columns c
JOIN sys.types t ON c.user_type_id = t.user_type_id
WHERE c.object_id = OBJECT_ID('YourTable')
These are just 2 options that will return a single resultset. INFORMATION_SCHEMA.COLUMNS doesn't tell you (e.g.) if a column is an IDENTITY column, wherease the sys.columns route does. There's more information you can get from the catalog views, just depends what else you need. Here's the MSDN reference.

You might find it easier to play with the INFORMATION_SCHEMA views - you can build queries that will extract most of the data relating to the structure of and relationship between tables within a SQL Server database schema.
See here: http://msdn.microsoft.com/en-us/library/ms186778.aspx

I got a simple way to get schema of a table.
Just create a connection with any provider like SqlConnection object.
Create a command object for select query (select only top 1 record as you want only schema of table)
Execute that query which return a DataReader object.
Every DataReader object have a method called GetTableSchema() which returns DataTable object which have schema of a perticulat table.
This way you can easily get schema of any table with less effort.
Here I will provide a simple code for this
SqlConnection con=new SqlConnection("connString");
con.Open();
SqlCommand cmd= new SqlCommand("select top 0 * from Person.Address",con);
DataTable table = cmd.ExecuteReader().GetTableSchema();
Now this table object have schema information of Person.Address table.

I think sp_help returns multiple result sets so you'll need to use NextResult, there are more details here - http://support.microsoft.com/kb/311274 (the example is not sp_help specific but it should give you the general idea)

Also, you can use the GetSchema method from SqlConnection. http://msdn.microsoft.com/en-us/library/ms136367.aspx
like so:
var tableSchema = con.GetSchema(SqlClientMetaDataCollectionNames.Tables, new string[] { null, null, "[tableName]" });
The string array is a 'filter', here you can find the columns you can filter on: http://msdn.microsoft.com/en-us/library/ms254969.aspx
For other database servers and more info:
http://msdn.microsoft.com/en-us/library/kcax58fh.aspx

sp_help doesn't work in your code? maybe you need to include an execute statement: exec sp_help. or maybe the application is running under an account that doesn't have permission for sp_help.

Since .net 2.0 the SqlConnection class offers GetSchemaMethod you can use to retrieve the requested info.
http://msdn.microsoft.com/en-us/library/ms136364(v=vs.80).aspx

See this one ... it explains how
How To Retrieve Schema Information by Using GetOleDbSchemaTable and Visual C# .NET

Related

Simple import of an MS-SQL table into System.Data.DataTable, is this possible?

When checking the official documentation about System.Data.DataSet and System.Data.DataTable, I have the impression that "importing an MS-SQL table into C# classes" happens as follows:
Open the connection
Write an SQL query in an SqlCommand, containing the name of the table, the names of the columns: SELECT ProductID, SupplierID FROM dbo.Products (or SELECT *), but still the corresponding SqlDataReader must be run through for getting the values inside the table).
Isn't there some way to say:
DataTable dt_Products = new DataTable(connection, "Products")
... and this DataTable automatically has the right column names, column types, values, autoincrement properties if possible, ... (without needing to pass by SqlCommand, SqlDataReader, ...)?
Maybe what you need is this.
using (var da = new System.Data.SqlClient.SqlDataAdapter("SELECT ProductID, SupplierID FROM dbo.Products", connection))
{
da.Fill(dt_Products);
}

C# ADO.NET Loading SQL Script with Multiple Statements and Comments

I am building an application in which I will producing some reports based off the results of some SQL queries executed against a number of different databases and servers. Since I am unable to create stored procedures on each server, I have my SQL scripts saved locally, load them into my C# application and execute them against each server using ADO.NET. All of the SQL scripts are selects that return tables, however, some of them are more complicated than others and involve multiple selects into table variables that get joined on, like the super basic example below.
My question is, using ADO.NET, is it possible to assign a string of multiple SQL queries that ultimately only returns a single data table to a SqlCommand object - e.g. the two SELECT statements below comprising my complete script? Or would I have to create a transaction and execute each individual query separately as its own command?
-- First Select
SELECT *
INTO #temp
FROM Table1;
--Second Select
SELECT *
FROM Table1
JOIN #temp
ON Table1.Id = #temp.Id;
Additionally, some of my scripts have comments embedded in them like the rudimentary example above - would these need to be removed or are they effectively ignored within the string? This seems to be working with single queries, in other words the "--This is a comment" is effectively ignored.
private void button1_Click(object sender, EventArgs e)
{
string ConnectionString = "Server=server1;Database=test1;Trusted_Connection=True";
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
SqlCommand cmd = new SqlCommand("--This is a comment \n SELECT TOP 10 * FROM dbo.Tablw1;");
DataTable dt = new DataTable();
SqlDataAdapter sqlAdapt = new SqlDataAdapter(cmd.CommandText.ToString(), conn);
sqlAdapt.Fill(dt);
MessageBox.Show(dt.Rows.Count.ToString());
}
}
Yes, that is absolutely fine. Comments are ignored. It should work fine. The only thing to watch is the scopin of temporary tables - if you are used to working with stored procedures, the scope is temporary (they are removed when the stored procedure ends); with direct commands: it isn't - they are connection-specific but survive between multiple operations. If that is a problem, take a look at "table variables".
Note: technically this is up to the backend provider; assuming you are using a standard database engine, you'll be OK. If you are using something exotic, then it might be a genuine question. For example, it might not work on "Bob's homemade OneNote ADO.NET provider".
Yes, you can positively do it.
You can play with different types of collections, or with string Builder for passing queries even you can put the string variable and assign the query to it.
While the loop is running put in temp table or CTE, its totally depends on you to choose the approach. and add the data to datatable.
So if you want the entire data to be inserted or Updated or deleted then you can go for transaction,it won't be any issue.
I don't use ado.net, I use Entity Framework but I think this is more a SQL question than an ADO.NET question; Forgive me if I'm wrong. Provided you are selecting from Table1 in both queries I think you should use this query instead.
select *
from Table1 tbl1
join Table1 tbl2
on tbl1.id = tbl2.id
Actually I really don't ever see a reason you would have to move things into temp tables with options like Common Table Expressions available to you.
look up CTEs if you don't already know about them
https://www.simple-talk.com/sql/t-sql-programming/sql-server-cte-basics/

Sql query containing 2 databases

In C# I want to execute a query that use 2 different databases (One is Access for local, and other is distant and is MySQL)
I'm able to do it in VBA Access, but how I can make the same thing in C# ??
This is how I made it in Access:
Link my 2 differents table/databases in Table
In VBA:
sSQL = "INSERT INTO DB1tblClient SELECT * FROM DB2tblClient"
CurrentDb.Execute sSQL
How I can execute this SQL in C# ? (What object to use, etc... Example code if you can)
Thanks !
There are two ways to do this. One is to set up linked tables on Access and run a single query. The other is to run both queries from c# and join them with linq.
The first way is better. If you really have to do it with linq, here is some sample code:
dWConnection.Open();
dWDataAdaptor.SelectCommand = dWCommand1;
dWDataAdaptor.Fill(queryResults1);
dWDataAdaptor.SelectCommand = dWCommand2;
dWDataAdaptor.Fill(queryResults2);
dWConnection.Close();
IEnumerable<DataRow> results1 = (from events in queryResults1.AsEnumerable()
where events.Field<string>("event_code").ToString() == "A01"
|| events.Field<string>("event_code").ToString() == "ST"
select events ) as IEnumerable<DataRow>;
var results2 = from events1 in queryResults1.AsEnumerable()
join events2 in queryResults2.AsEnumerable()
on (string)events1["event_code"] equals (string)events2["event_code"]
select new
{
f1 = (string)events1["event_code"],
f2 = (string)events2["event_name"]
};
DataTable newDataTable = new DataTable();
newDataTable = results1.CopyToDataTable<DataRow>();
See why I said linked tables is better?
You should be able to run the same SQL command from any app, really. This is assuming:
You're connecting to Access from your C# app
DB1tblClient is a local Access table
DB2tblClient is a link table in Access
Given these, you might try the following:
using (OleDbConnection conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Stuff\MyAccessdb.mdb"))
{
conn.Open();
using (OleDbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "INSERT INTO DB1tblClient SELECT * FROM DB2tblClient";
cmd.ExecuteNonQuery();
}
}
You might want to check connectionstrings.com if you can't get the connection string right, and you may need to install some components (MDAC or ACE) for connections that use those providers.
Well it is not possible to run this such complex query with single statement.
Basically each query execution object initialized by particular database information,
so need two different object for each database first think.
Now 2 Object need with initialized with its own connection object.
Just fetch data by first object and insert it to another database by usin second connection object.
You need to keep following points in mind before trying this type of query
Both the databases are accessible from your code.
There is inter-connectivity between both the database.
Both the databases are available for the user that you are using to execute this query.
You need to specify the query in following format
DATABASE_NAME.SCHEMA_NAME.TABLE_NAME instead of just TABLE_NAME
EDIT
If you don't have inter-connectivity between databases you can follow following steps
Connect to Source database using one connection.
Read the data from source database into a dataset or datatable using SELECT query.
Connect to target database using a second connection.
Insert all the records one by one using a loop to TARGET Database using standard INSERT query

Visual Studio – EntityFramework -The Selected Store Procedure Returns no columns

Hi all I have written my query as follows in sql which gives me a result, this is my query
SELECT Technology,TechDescription, Technology.TechID, COUNT(Question) AS
'Totalposts' FROM Technology LEFT JOIN Question ON Question.TechID = Technology.TechID GROUP BY Technology.TechID, Technology,TechDescription
I just write it in to a stored procedure as follows
USE [newForumDB]
GO
/****** Object: StoredProcedure [dbo].[selectTechQuestions] Script Date: 01/24/2013 15:06:06 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create PROCEDURE [dbo].[selectTechQuestions]
As
SET FMTONLY OFF
Begin
SELECT Technology,TechDescription, Technology.TechID, COUNT(Question) AS
'Totalposts'
FROM Technology
LEFT JOIN Questions
ON Questions.TechID = Technologies.TechID
GROUP BY Technologies.TechID, Technology,TechDescription
End
and added this in to my model, and tried to add an function for the procedure, but I am getting the message as The selected stored procedure returns no columns can some one help me
As per Habib requested I tried in both ways i.e
1) writing the query in a string and filled the DataSet using SqlCommand and SqlDataAdapter which works fine
2) SqlCommand cmd = new SqlCommand();
cmd.CommandText = "selectTechQuestions";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = con;
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = cmd;
DataSet ds = new DataSet();
da.Fill(ds);
The second one gives me the error {"Invalid object name 'Questions'."}
Put your as
SELECT Technology AS 'Technology',TechDescription AS 'TechDescription', Technology.TechID AS 'ID', COUNT(Question) AS
'Totalposts'
FROM Technology
LEFT JOIN Questions
ON Questions.TechID = Technologies.TechID
GROUP BY Technologies.TechID, Technology,TechDescription
So, many time before when I get this type of prob, at that time I had done this.
This is silly, solution but I had come out with my prob, may be It can be help full to you....
Detail description
As all we know select statement return result as single data-set, even if its from multiple table i.e query like inner join.
When a data comes form the multiple tables in there is a possibility to over come two different column from different table with same name.
This clause does not made any problem in simple data fetching style i.e as we simply done with DataAdapter and SqlCommand, but entity framework can't handle this thing, and there fore compiler does not allow query such like which containing inner join or multiple table queries.
So to resolve this problem we just have to assign a different name fore each column as I had done here and in this way there will be not at all any problem can cause...

How to SELECT with optional columns?

I'm currently working on a c# application that grabs a bunch of data from a user specified access(.mdb) database and does a bunch of stuff with that data. A problem that I've recently come across is that some of the a database is missing a column that has existed in all of the others.
How can I do a select on a database, but gracefully fail (throw null in the data or something) when a column doesn't exist in the database?
Currently, my code looks something like this:
OleDbConnection aConnection = new
OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + FileName);
string sqlQuery = "SELECT [Table1].[Index], [Table1].[Optional Info],
[Table2].[Other Info], ....
FROM [Table1] INNER JOIN [Table2] ON [Table1].[Index]=[Table2].[Index]
ORDER BY [Table1].[Index]";
OleDbCommand aCommand = new OleDbCommand(sqlQuery, aConnection);
OleDbDataReader aReader = aCommand.ExecuteReader();
(proceed to read data in line by line, using fabulous magic numbers)
I think it's obvious that this is one of my first experiences with databases. I'm not overly concerned as long as it works, but it's stopped working for a database that does not contain the [Table1].[Optional Info] column. It's throwing an OleDbException: "No value given for one or more required parameters."
Any help would be appreciated.
I might be missing something but...
SELECT Table1.*, Table2.otherInfo
FROM ...
Should do the trick, and let the client process the result set, with an important caveat: there is no way to exclude a column from Table1 in the above.
(I am not aware of any method to "dynamically shape" -- with the viewpoint of the caller -- a SELECT except with a * in the column list as above.)
Happy coding.
The way to do that is to not use magic numbers, but to fetch the field names from the reader and use them - for example GetName etc.
Alternatively, use a mapper like "dapper" that will do this for you.
There is no way to do this in a single query: you cannot run a query that includes columns that don't exist in the source tables. When the server tries to compile the query, it will simply fail.
If you absolutely need to support different scemas, you will need different queries for each of them.
To make things even more awesome, there is no documented way to check if an Access table has a particular column on it via SQL. In SQL Server, you could query the system schema, like sys.objects or sys.columns. In Access, the MsysObjects table has the information you need but it's schema is liable to change on you without notice.
Probably the safest way to go about this is to do a single, up front check where you execute a command such as
SELECT * FROM Table1
then scan the resulting column names to see if your optional column exists; your C# code would then become:
string sqlQuery = string.Empty;
if (optionalColumnExists)
{
sqlQuery = "SELECT [Table1].[Index], [Table1].[Optional Info], -- etc."
}
else
{
sqlQuery = "SELECT [Table1].[Index], '' AS [Optional Info], -- etc."
}
There is a way to extract the table schema using OleDbDataReader.GetSchemaTable and that can be used
OleDbConnection aConnection = new
OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + FileName);
OleDbCommand aCommand = new OleDbCommand("Table1", aConnection);
aCommand.CommandType = CommandType.TableDirect;
aConnection.Open();
OleDbDataReader aReader = cmd.ExecuteReader(CommandBehavior.SchemaOnly);
DataTable schemaTable = aReader.GetSchemaTable();
aReader.Close();
aConnection.Close();
bool optionalInfoColumnExists = schemaTable.Columns.Contains("Optional Info");
Now later in the code
string sqlQuery = #"SELECT [Table1].[Index], {0}
[Table2].[Other Info], ....
FROM [Table1] INNER JOIN [Table2] ON [Table1].[Index]=[Table2].[Index]
ORDER BY [Table1].[Index]";
if (optionalInfoColumnExists)
{
sqlQuery = string.Format(sqlQuery, "[Table1].[Optional Info],");
}
else
{
sqlQuery = string.Format(sqlQuery, "");
}
and while reading use similar logic.
I don't know what kind of application this is but the optionalInfoColumnExists should be populated at the application or session start and reused throughout the life of the app i.e. don't execute the GetSchemaTable everytime a query is run on this table (assuming that the mdb won't change while the app is active).
Either way, it seems like that it is going to make the code to have "if else" just to take care of presense and absence of a column in a table.

Categories