I use following pattern to work with SQL Server temp tables from .NET applications:
Open the connection
Create a temp table (only with ad hoc command!!!)
Write a lot of data into it via SqlBulkCopy
Perform a select/update with join to this temp table (this request can already be parameterized)
Close the connection
Using cn As New SqlConnection("Server=myServerAddress;Database=myDataBase;Trusted_Connection=True;") : cn.Open()
Using cm As New SqlCommand("create table #T1(C1 int primary key, C2 int)", cn)
cm.ExecuteNonQuery()
End Using
Using bk As New SqlBulkCopy(cn)
bk.DestinationTableName = "#T1"
bk.WriteToServer(dataToWrite)
End Using
Using cm As New SqlCommand("update a set a.C2=b.C2 from SomeTable a join #T1 b on a.C1=b.C1 where a.C3=#PC3", cn)
cm.Parameters.Add("C2", SqlDbType.Int).Value = c3Value
cm.ExecuteNonQuery()
End Using
End Using
Unfortunately, I can't explicitly influence whether the “create temp table” as ad-hoc command runs.
Although the SqlCommand source code describes this behavior in comments (// Send over SQL Batch command if we are not a stored proc and have no parameters), but nothing is specified in the documentation.
Theoretically, the behavior of SqlCommand in future .NET versions can be changed, so that "create table #T..." will be packed into sp_executesql. Using temporary tables from .NET code will become impossible and the application will become inoperable.
Do I understand the problem correctly?
Can I remain assured that in future versions of .NET will still not package parameterless queries in sp_executesql?
Related
When one executes a stored proc against MS SQL Server, the recordset comes with some type info. Clients are capable of retrieving that type info, e. g. like this (C#/.NET):
SqlCommand cmd = new SqlCommand("dbo.MyProc", conn);
SqlDataAdapter ada = new SqlDataAdapter(cmd);
ada.Fill(ds);
string ColName = ds.Tables[0].Columns[0].ColumnName;
Type ColType = ds.Tables[0].Columns[0].DataType;
What about nullability of those columns? While it's not knowable in the general case, sometimes it is - for example, when a recordset column comes straight from a table field. If SQL Server is smart enough, it can determine column nullability at least for those cases. Does SQL Server report nullability to the clients in whatever metadata it provides along with the recordset?
Specifically in the .NET client, there is AllowDBNull in retrieved column properties, but in the scenario above it's unreliable - it comes across as true both for columns that came from nullable fields, and for columns that came from nonnullable fields. Is this a limitation of the .NET client, or a shortcoming of the underlying protocol?
This is a limitation of SqlDataAdapter.
The TDS protocol does return this information, as you can see in the specification.
In turn, SqlDataReader will return this information via GetSchemaTable. But it does not make its way to a table filled using a DbDataAdapter.
You can see this with the following code
using(var conn = new SqlConnection(YourConnectionString))
{
conn.Open();
using(var comm = new SqlCommand("select 1", conn))
using(var reader=comm.ExecuteReader())
{
reader.Read();
reader.GetSchemaTable().Dump();
}
using(var comm = new SqlCommand("select case when getdate() = 1 then 1 end", conn))
using(var reader = comm.ExecuteReader())
{
reader.Read();
reader.GetSchemaTable().Dump();
}
}
You will see that the first has AllowDBNull as false and the second as true.
I would advise you in any case to avoid SqlDataAdapter, as it is only actually useful in data-binding UI scenarios. In back-end code, just use a SqlDataReader, or even better: use an ORM such as Dapper or Entity Framework, which will sort out all of this kind of thing for you.
I don't have much experience with SQL Server, I use it currently to run some simple queries, and I link to SQL tables from Access where I have all my heavy queries. My goal is to run all of my queries in Access daily and then at the end write the finished tables up to SQL where my Access front end will read them (versus keeping them in my Access backend).
I've tried messing around with the code below to try and figure out how to do this, but I'm stuck at the driver and I can't find any references on how to do this with just a single table. Let's call it "PO_STATUS_TBL"
Public Sub ADOtest()
Dim ADOConn As New ADODB.Connection
Dim ADOCom As New ADODB.Command
On Error Resume Next
ADOConn.ConnectionString = "Driver =(SQL Server);DRIVER=SQL Server;SERVER=BUSINESS_BWP;Trusted_Connection=Yes"
ADOConn.Open
Debug.Print ADOConn.State
Dim db As Database
Set db = CurrentDb
'db.Execute "INSERT INTO [ODBC;DRIVER=SQL Server;ENCSQL28\BUSINESS_BWP;DATABASE=CurrentDb].SFTransfersDB ( ID, TO ) SELECT ID,TO FROM SFTransfersDB"
End Sub
I went through this process lately.
For migration you can use this tool by Microsoft. With this tool you can either migrate a table or a query to MS-SQL - or even both. Even with relations.
Simple export with this wizard and add your one table through ODBC Connector in Access. Important is to have the correct ODBC Driver.
If you are interested I resolved this issue with Parfait's advise. By using a simple INSERT statement.
INSERT INTO Dbo_PO_STATUS_ALL_TBL Select * FROM PO_STATUS_ALL_TBL
Please pardon me for my English grammar.
I'm currently coding a system and I was wondering if you could establish a new connection after you created a database in a server.
This is the Connection String:
Dim DBCon As SqlConnection = New SQLConnection("Data Source=(localdb)\DbLocal;Integrated Security=True")
If I want to create a database I use a command - the database name is bounded after a textbox, it goes like:
Dim dbName As String = txtdbName.Text
myCommand = "CREATE database " & dbName
The database gets created, but after I start a query that creates a table - the table does not save in the created database. So with my beginner skills in VB.Net and MSSQL, I deduced it was because of my Connection String, so I tried messing with it:
Dim myConnectionString As SqlConnection = New SqlConnection("Data Source=(localdb)\DbLocal;Database=" & dbName & ";Integrated Security=True;Pooling=False")
I wrote the above code before the create a table query, but after I run it, the tables I created in the query didn't go to the database. I was wondering if there's a 'right' way to do this. I tried mixing different codes that I found online, but they produce the same result.
Edit: My create table query:
myCommand = "CREATE TABLE tblPerson (PersonID int, LastName varchar(300), FirstName varchar(300), Address varchar(300), City varchar(300))"
The way I would do this is to add a USE statement before the CREATE TABLE. So the CREATE TABLE command would look like this:
"USE " & dbName & ";
GO
CREATE TABLE ..."
EDIT: As pointed out in the comments, the GO separator cannot be used in a .NET SQL Command.
Instead one can use three-part naming to specify the database like this:
"CREATE TABLE " & dbName & ".dbo.MyTable ( ..."
Or use SMO which does allow one to use the GO separator. This technique is thoroughly described in the accepted answer to this question.
I have to retrieve the Auto_Increment primary key (id) after inserting a new row in a mdb access database.
Having mdb file in the old Access 97 version too, I cannot use "SELECT ##Identity;", because it is not supported.
Besides, in a multi users environment I do not like the idea to use Max(ID).
Actually the only solution I have, it is to use the DAO as:
Dim db As Database 'Test Database
Dim rs As Recordset 'Test Table
...
rs.AddNew
id = rs!id
but I have to add a reference to the DAO COM DLL, while I would like to have a full managed .NET code.
Do you have any suggestion how to retrieve the Auto_Increment primary key (after inserting a new row) without using DAO or Max(ID)?
Here my code to add a new row:
Using oConn As New OleDbConnection(m_ConnString)
oConn.Open()
Using cmd As New OleDbCommand(sqlInsert, oConn)
cmd.ExecuteNonQuery()
End Using
End Using
Thank you.
If there is any other mandatory field in this table that has a unique index, you may query its value after your insertion with something like DFirst("ID", "tablename", "myfield = givenvalue"). If you're running in a transaction, use a recordset (perhaps the same as for the insertion) instead of DFirst.
I am currently writing a VB .NET application where I am trying to open 1 database, create a select statement and then post the results into another database file using Microsoft Access database 2003.
The code seems to stop executing at the statement cmdJetDB.ExecuteNonQuery()
I am using the following code:
Dim conn1 As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data source=C:\Sample.mdb")
Dim conn2 As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data source=C:\db2.mdb")
conn1.Open()
conn2.Open()
Dim mySelectQuery As String
mySelectQuery = "SELECT Sample.LANE_ADDR, Sample.LANE_DT, Sample.LANE_TM, Sample.LANE_SPEED FROM (Sample) WHERE ((Sample.LANE_ADDR) = '164.909' OR (Sample.LANE_ADDR) = '164.909' AND Sample.LANE_DT BETWEEN #4/4/2003# AND #4/5/2003#)"
Dim cmdJetDB As New OleDbCommand(mySelectQuery, conn1)
cmdJetDB.ExecuteNonQuery()
Dim cmdInsert As String
cmdInsert = "Insert INTO Table1 (Sample.LANE_ADDR, Sample.LANE_TM,Sample.LANE_SPEED) VALUES ('164.909', '00:12:30' , '30' )"
Dim cmdJetDB2 As New OleDbCommand(cmdInsert, conn2)
cmdJetDB2.ExecuteNonQuery()
conn2.Close()
conn1.Close()
Question: What is it that I am not doing. I opened both connections, stated my two SQL statements, yet it is still not working. I really need to get this application working. Please Help.........
ExecuteNonQuery cannot be used to SELECT stuff from a database. You should use ExecuteReader and use the result in a loop to set the parameters of the INSERT statement and then run ExecuteNonQuery in that loop. From the code you've written, how you'd expect the values should be populated in the INSERT statement?
Here is a sugestion,
If the columns you are retriving have the same type as the columns you are inserting (Basicaly you are not making any conversion and/or transformations) do a single query that does this.
INSERT INTO TestTable2
SELECT * FROM TestTable1
You're using SELECT to try to return rows, but then calling ExecuteNonQuery(), which returns nothing. You'll want to use ExecuteReader() instead.
You'll probably get another error later because you're INSERTing into "Table1" but trying to reference fields in "Sample".
Also unrelated to the error, but you aren't doing anything with the data in the SELECT statement to use it in the INSERT statement.