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

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/

Related

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

Sql Connection Catalog changes after statement

Issue:
After running an Sql command with SqlCommand() on a database that then inserts data into another database, all following statements error with ExceptionInvalid object name.
Question:
Why is this happening?
Additional Information:
I know how to fix it by adding The Temp database name before the table on the select portion but since it is being run in the context of that database that shouldn't be necessary and is not when I run the statements individually in SQL management studio
Program Logic:
Create and fill temp database (All tables ASI_...)
In context of temp database select data and then insert it into another database (#AcuDB)
Repeat Step 2 for X queries
Insertion code:
if (TempD.State == System.Data.ConnectionState.Closed) TempD.Open();
Command = new SqlCommand(temp, TempD);
Command.CommandTimeout = 0;
Command.ExecuteNonQuery();
Sample Sql being run that errors after previous similar statement:
insert into #AcuDB..Batch (CompanyID,BranchID,
Module,BatchNbr,CreditTotal,DebitTotal,ControlTotal,CuryCreditTotal,CuryDebitTotal,CuryControlTotal,CuryInfoID,LedgerID,BatchType,Status,AutoReverse,AutoReverseCopy,OrigModule,OrigBatchNbr,DateEntered,Released,Posted,LineCntr,CuryID,ScheduleID,NoteID,CreatedByID,CreatedByScreenID,CreatedDateTime,LastModifiedByID,LastModifiedByScreenID,LastModifiedDateTime,Hold,Description,Scheduled,Voided,FinPeriodID,TranPeriodID)
select 2,
1,Module,BatchNbr,CreditTotal,DebitTotal,ControlTotal,CuryCreditTotal,CuryDebitTotal,CuryControlTotal,i.CuryInfoID,
isnull((select a.LedgerID from #AcuDB..ledger a where a.LedgerCD =
b.LedgerID),0)
[LedgerID],BatchType,Status,AutoReverse,AutoReverseCopy,OrigModule,OrigBatchNbr,
DateEntered
[DateEntered],Released,Posted,LineCntr,b.CuryID,ScheduleID,NoteID,
'B5344897-037E-4D58-B5C3-1BDFD0F47BF9' [CreatedByID], '00000000'
[CreatedByScreenID], GETDATE() [CreatedDateTime],
'B5344897-037E-4D58-B5C3-1BDFD0F47BF9' [LastModifiedByID], '00000000'
[LastModifiedByScreenID], GETDATE()
[LastModifiedDateTime],Hold,Description,Scheduled,Voided,b.FinPeriodID,TranPeriodID
from Temp..ASI_GLBatch b inner join #AcuDB..CurrencyInfo i on
i.CuryEffDate = b.DateEntered cross join #AcuDB..glsetup g where
b.companyID = #CpnyCD and b.branchID = #BranchCD
Going across databases like this is always precarious due to the way SQL will try to imply contexts. In this case, unless #AcuDB contains the fully-qualified address that includes both the database and the schema, you're going to get errors because of the way you're switching contexts around. Get a reading on what #AcuDB contains and try to run the batch in a stored procedure. Set up a separate instance to sandbox the scenario if you have to. The C# end of this is going to continue to complicate things until you cut it out for a little bit and make sure your SQL is good. After you're sure it's okay, integrate it back into the C# code and work from there.

Change returned table name from stored procedure at the SQL side

I have written a single stored procedure that returns 2 tables:
select *
from workers
select *
from orders
I call this stored procedure from my C# application and get a DataSet with two tables, and everything is working fine.
My question is how can I change the tables name at the SQL Server side so that in the C# side I will be able to access it via a name (instead of Tables[0]):
myDataSet.Tables["workers"]...
I tried to look for the answer in Google but couldn't find it. Maybe the search keywords was not sufficient.
You cannot really do anything from the server-side to influence those table names - those names only exist on the client-side, in your ADO.NET code.
What you can do is on the client-side - add table mappings - something like:
SqlDataAdapter dap = new SqlDataAdapter(YourSqlCommandHere);
dap.TableMappings.Add("Table", "workers");
dap.TableMappings.Add("Table1", "orders");
This would "rename" the Table (first result set) to workers and Table1 (second result set) to orders before you actually fill the data. So after the call to
dap.Fill(myDataSet);
you would then have myDataSet.Tables["workers"] and myDataSet.Tables["orders"] available for you to use.
The TDS Protocol documentation (Which is the protocol used to return results from SQL Server) does not mention a "resultset name". So the only way you will ever be able to access the result sets in ADO.net is by the number as mentioned in your example.

Handle multiple db updates from c# in SQL Server 2008

I like to find a way to handle multiple updates to a sql db (with one singe db roundtrip). I read about table-valued parameters in SQL Server 2008 http://www.codeproject.com/KB/database/TableValueParameters.aspx which seems really useful. But it seems I need to create both a stored procedure and a table type to use it. Is that true? Perhaps due to security? I would like to run a text query simply like this:
var sql = "INSERT INTO Note (UserId, note) SELECT * FROM #myDataTable";
var myDataTable = ... some System.Data.DataTable ...
var cmd = new System.Data.SqlClient.SqlCommand(sql, conn);
var param = cmd.Parameters.Add("#myDataTable", System.Data.SqlDbType.Structured);
param.Value=myDataTable;
cmd.ExecuteNonQuery();
So
A) do I have to create both a stored procedure and a table type to use TVP's? and
B) what alternative method is recommended to send multiple updates (and inserts) to SQL Server?
Yes, you need to create the types.
Alternatives are sending a big string sql batch or passing XML to sprocs.
The downside to big sql string batches is it can blow the sql proc cache and might cause sql to recompile - especially if the batch is unique because of input data being part of that large string. By definition each batch would be unique.
XML was the main alternative before TVPs. The one downside to XML, for at least awhile, sql azure didn't support it (that might change?) so it limits your options.
TVPs seem to be the way to do this. Our project just converted to using TVPs.
Hope that helps.

Is there any way to get the table hierarchy from a connection string in c#?

I have a current requirement to determine the table hierarchy from a sql statement within c#. For example, consider the following sql statement:
Select Table1.*, Table2.* from Table1
left join table2 on Table1.parentCol = Table2.childCol
That might return 7 columns, 3 for Table1 and 4 for table2. I need to know the column names, and ideally (though not mandatory) their types.
I have no control over what SQL Statement will be used, as this is a user entered field. In C# it's a very basic task to open a connection and create an SqlCommand using that statement. I have freedom to run the SQL into a SqlDataReader, or any other System.Data.SqlClient class if necessary, however I cannot find any combination that will return the columns, rather than the actual column values.
Is anyone able to help?
Many thanks and best regards
You cannot do what you are asking (easily).
More to the point, do not let users enter arbitrary TSQL (You will regret it at some point...).
Instead, create a 'Search' form that allows entering various params and use a parameterised query onto a view that joins all the tables/columns required.
There's no direct way. You'll need to parse names of all the tables from the sql query.
Once you have done that you'll need to write few queries on Information_Schema to get raw data for what you are looking for.
If you are on SQL Server, you may want to use Catalog View
ex-
Select * from sys.tables where [Name] = 'MyTable'

Categories