Reading/Writing DataTables to and from an OleDb Database LINQ - linq

My current project is to take information from an OleDbDatabase and .CSV files and place it all into a larger OleDbDatabase.
I have currently read in all the information I need from both .CSV files, and the OleDbDatabase into DataTables.... Where it is getting hairy is writing all of the information back to another OleDbDatabase.
Right now my current method is to do something like this:
OleDbTransaction myTransaction = null;
try
{
OleDbConnection conn = new OleDbConnection("PROVIDER=Microsoft.Jet.OLEDB.4.0;" +
"Data Source=" + Database);
conn.Open();
OleDbCommand command = conn.CreateCommand();
string strSQL;
command.Transaction = myTransaction;
strSQL = "Insert into TABLE " +
"(FirstName, LastName) values ('" +
FirstName + "', '" + LastName + "')";
command.CommandType = CommandType.Text;
command.CommandText = strSQL;
command.ExecuteNonQuery();
conn.close();
catch (Exception)
{
// IF invalid data is entered, rolls back the database
myTransaction.Rollback();
}
Of course, this is very basic and I'm using an SQL command to commit my transactions to a connection. My problem is I could do this, but I have about 200 fields that need inserted over several tables. I'm willing to do the leg work if that's the only way to go. But I feel like there is an easier method. Is there anything in LINQ that could help me out with this?

If the column names in the DataTable match exactly to the column names in the destination table, then you might be able to use a OleDbCommandBuilder (Warning: I haven't tested this yet). One area you may run into problems is if the data types of the source data table do not match those of the destination table (e.g if the source column data types are all strings).
EDIT
I revised my original code in a number of ways. First, I switched to using the Merge method on a DataTable. This allowed me to skip using the LoadDataRow in a loop.
using ( var conn = new OleDbConnection( destinationConnString ) )
{
//query off the destination table. Could also use Select Col1, Col2..
//if you were not going to insert into all columns.
const string selectSql = "Select * From [DestinationTable]";
using ( var adapter = new OleDbDataAdapter( selectSql, conn ) )
{
using ( var builder = new OleDbCommandBuilder( adapter ) )
{
conn.Open();
var destinationTable = new DataTable();
adapter.Fill( destinationTable );
//if the column names do not match exactly, then they
//will be skipped
destinationTable.Merge( sourceDataTable, true, MissingSchemaAction.Ignore );
//ensure that all rows are marked as Added.
destinationTable.AcceptChanges();
foreach ( DataRow row in destinationTable.Rows )
row.SetAdded();
builder.QuotePrefix = "[";
builder.QuoteSuffix= "]";
//forces the builder to rebuild its insert command
builder.GetInsertCommand();
adapter.Update( destinationTable );
}
}
}
ADDITION An alternate solution would be to use a framework like FileHelpers to read the CSV file and post it into your database. It does have an OleDbStorage DataLink for posting into OleDb sources. See the SqlServerStorage InsertRecord example to see how (in the end substitute OleDbStorage for SqlServerStorage).

It sounds like you have many .mdb and .csv that you need to merge into a single .mdb. This answer is running with that assumption, and that you have SQL Server available to you. If you don't, then consider downloading SQL Express.
Use SQL Server to act as the broker between your multiple datasources and your target datastore. Script each datasource as an insert into a SQL Server holding table. When all data is loaded into the holding table, perform a final push into your target Access datastore.
Consider these steps:
In SQL Server, create a holding table for the imported CSV data.
CREATE TABLE CsvImport
(CustomerID smallint,
LastName varchar(40),
BirthDate smalldatetime)
Create a stored proc whose job will be to read a given CSV filepath, and insert into a SQL Server table.
CREATE PROC ReadFromCSV
#CsvFilePath varchar(1000)
AS
BULK
INSERT CsvImport
FROM #CsvFilePath --'c:\some.csv'
WITH
(
FIELDTERMINATOR = ',', --your own specific terminators should go here
ROWTERMINATOR = '\n'
)
GO
Create a script to call this stored proc for each .csv file you have on disk. Perhaps some Excel trickery or filesystem dir piped commands can help you create these statements.
exec ReadFromCSV 'c:\1.csv
For each .mdb datasource, create a temp linked server.
DECLARE #MdbFilePath varchar(1000);
SELECT #MdbFilePath = 'C:\MyMdb1.mdb';
EXEC master.dbo.sp_addlinkedserver #server = N'MY_ACCESS_DB_', #srvproduct=N'Access', #provider=N'Microsoft.Jet.OLEDB.4.0', #datasrc=#MdbFilePath
-- grab the relevant data
--your data's now in the table...
INSERT CsvImport(CustomerID,
SELECT [CustomerID]
,[LastName]
,[BirthDate]
FROM [MY_ACCESS_DB_]...[Customers]
--remove the linked server
EXEC master.dbo.sp_dropserver #server=N'MY_ACCESS_DB_', #droplogins='droplogins'
When you're done importing data into that holding table, create a Linked Server in your SQL Server instance. This is the target datastore. SELECT the data from SQL Server into Access.
EXEC master.dbo.sp_addlinkedserver #server = N'MY_ACCESS_TARGET', #srvproduct=N'Access', #provider=N'Microsoft.Jet.OLEDB.4.0', #datasrc='C:\Target.mdb'
INSERT INTO [MY_ACCESS_TARGET]...[Customer]
([CustomerID]
,[LastName]
,[BirthDate])
SELECT Customer,
LastName,
BirthDate
FROM CsvImport

Related

Oracle XMLType - Loading from XML flat-file as chunks

Using Java 8 and Oracle 11g. Regarding loading XML data from a flat file into an Oracle XMLType field. I can make it work with this code:
private String readAllBytesJava7(String filePath) {
Files files;
Paths paths;
String content;
content = "";
try {
content = new String ( Files.readAllBytes( Paths.get(filePath) ) );
}
catch (IOException e) {
log.error(e);
}
return content;
}
pstmt = oracleConnection.prepareStatement("update MYTABLE set XML_SOURCE = ? where TRANSACTION_NO = ?");
xmlFileAsString = this.readAllBytesJava7(fileTempLocation);
xmlType = XMLType.createXML(oracleConnection, xmlFileAsString);
pstmt.setObject(1,xmlType);
pstmt.setInt(2, ataSpecHeader.id);
pstmt.executeUpdate();
But as you might surmise, that only works for small XML files... Anything too large will cause a memory exception.
What I'd like to do is load the XML file in "chunks" as described here:
https://docs.oracle.com/cd/A97335_02/apps.102/a83724/oralob2.htm
and
https://community.oracle.com/thread/4721
Those posts show how to load a BLOB/CLOB column from a flat-file by "chunks". I can make it work if the column is blob/clob, but I couldn't adapt it for an XMLType column. Most of what I found online in regards to loading an XMLType column deals with using the oracle-directory object or using sql-loader, but I won't be able to use those as my solution. Is there any kind of post/example that someone knows of for how to load an XML file into an XMLType column as "chunks"?
Additional information:
I'm trying to take what I see in the posts for blob/clob and adapt it for XMLType. Here's the issues I'm facing:
sqlXml = oracleConnection.createSQLXML();
pstmt = oracleConnection.prepareStatement("update MYTABLE set
XML_SOURCE = XMLType.createXML('<e/>') where 1=1 and TRANSACTION_NO = ?");
pstmt.setInt(1, ataSpecHeader.id);
pstmt.executeUpdate();
With blob/clob, you start out by setting the blob/clob field to "empty" (so it isn't null)... I'm not sure how to do this with XMLType... the closest I can get it just to set it to some kind of xml as shown above.
The next step is to select the blob/clob field and get the output stream on it. Something like what is shown here:
cmd = "SELECT XML_SOURCE FROM MYTABLE WHERE TRANSACTION_NO = ${ataSpecHeader.id} FOR UPDATE ";
stmt = oracleConnection.createStatement();
rset = stmt.executeQuery(cmd);
rset.next();
xmlType = ((OracleResultSet)rset).getOPAQUE(1);
//clob = ((OracleResultSet)rset).getCLOB(1);
//blob = ((OracleResultSet)rset).getBLOB(1);
clob = xmlType.getClobVal();
//sqlXml = rset.getSQLXML(1);
//outstream = sqlXml.setBinaryStream();
//outstream = blob.getBinaryOutputStream();
outstream = clob.getAsciiOutputStream();
//At this point, read the XML file in "chunks" and write it to the outstream object by doing: outstream.write
The lines that are commented-out are to show the different things I've tried. To re-state... I can make it work fine if the field in the table is a BLOB or CLOB. But I'm not sure what to do if it's an XMLType. I'd like to get an outstream handle to the XMLType field so I can write to it, as I would if it were a BLOB or CLOB. Notice for BLOB/CLOB it selects the blob/clob field with "for update" and then gets an Outstream on it so I can write to it. For XMLType, i tried getting the field to an XMLType java class and SQLXML java class, but it won't work that way. I also tried getting the field first as xmltype/sqlxml and then casting to blob/clob to then get an outstream, but it won't work either. The truth is, I'm not sure what I'm supposed to do in order to be able to write to the XMLType field as a stream/chunks.

Using Rails Update to Append to a Text Column in Postgresql

Thanks in advance for any help on this one.
I have a model in rails that includes a postgresql text column.
I want to append (i.e. mycolumn = mycolumn || newdata) data to the existing column. The sql I want to generate would look like:
update MyOjbs set mycolumn = mycolumn || newdata where id = 12;
I would rather not select the data, update the attribute and then write the new data back to the database. The text column could grow relatively large and I'd rather not read that data if I don't need to.
I DO NOT want to do this:
#myinstvar = MyObj.select(:mycolumn).find(12)
newdata = #myinstvar.mycolumn.to_s + newdata
#myinstvar.update_attribute(:mycolumn, newdata)
Do I need to do a raw sql transaction to accomplish this?
I think you could solve this problem directly writing your query using the arel gem, that's already provided with rails.
Given that you have these values:
column_id = 12
newdata = "a custom string"
you can update the table this way:
# Initialize the Table and UpdateManager objects
table = MyOjbs.arel_table
update_manager = Arel::UpdateManager.new Arel::Table.engine
update_manager.table(table)
# Compose the concat() function
concat = Arel::Nodes::NamedFunction.new 'concat', [table[:mycolumn], new_data]
concat_sql = Arel::Nodes::SqlLiteral.new concat.to_sql
# Set up the update manager
update_manager.set(
[[table[:mycolumn], concat_sql]]
).where(
table[:id].eq(column_id)
)
# Execute the update
ActiveRecord::Base.connection.execute update_manager.to_sql
This will generate a SQL string like this one:
UPDATE "MyObjs" SET "mycolumn" = concat("MyObjs"."mycolumn", 'a custom string') WHERE "MyObjs"."id" = 12"

Google Cloud SQL + RETURN_GENERATED_KEYS

I've got a table in my Google Cloud SQL database with an auto-incrementing column.
How do I execute an INSERT query via google-apps-script/JDBC and get back the value for the newly incremented column?
For example, my column is named ticket_id. I want to INSERT and have the new ticket_id value be returned in the result set.
In other words, if I have the following structure, what would I need to modify or how, so that I can do something like rs = stmt.getGeneratedKeys();
var conn = Jdbc.getCloudSqlConnection("jdbc:google:rdbms:.......
var stmt = conn.createStatement();
//build my INSERT sql statement
var sql = "insert into ......
var rs = stmt.executeUpdate(sql);
I see that there is a JDBC statement class with a member called RETURN_GENERATED_KEYS but I have so far not been smart enough to figure out how to properly manipulate that and get what I need. Is RETURN_GENERATED_KEYS a constant, is it an attribute, or how can I make use of it?
It seems like the documentation with the Apps Script JDBC service is a bit lacking. I've created an internal task item for that. Thankfully, Apps Script JDBC API follows the Java JDBC API pretty closely. The key is to get the result set back using the stmt.getGeneratedKeys() call.
I built a sample table using the animals example from the MySQL docs and this sample below works nicely against that and logs the next incremented ID.
function foo() {
var conn = Jdbc.getCloudSqlConnection("jdbc:google:rdbms://<instance>/<db>");
var stmt = conn.createStatement();
var sql = "INSERT INTO animals (name) VALUES ('dog')";
var count = stmt.executeUpdate(sql,1)//pass in any int for auto inc IDs back
var rs = stmt.getGeneratedKeys();
//if you are only expecting one row back, no need for while loop
// just do rs.next();
while(rs.next()) {
Logger.log(rs.getString(1));
}
rs.close();
stmt.close();
conn.close();
}

EntityFramework - SaveChanges not saving but SQL seen in Profiler

I have read through most every post on EF-SaveChanges and do not believe my answer lies in any of those posts.
I am using C#, .NET 4, EF 4.3.1, SQL Server 2008R2, VS 2k11 Beta, AutoMapper.
Here is my code:
using (Model.AnimalRescueEntities context = new Model.AnimalRescueEntities())
{
using (TransactionScope transaction = new TransactionScope())
{
context.Connection.Open();
//Retrieve the event
eventDB = context.Events.Single(e => e.ID == eventRegVM.EventID);
eventOrgBaseDB = context.Entity_Base.Single(b => b.ID == eventDB.Entity_Organisation.ID);
eventRegVM.Event = Mapper.Map<Model.Event, EventsViewModel>(eventDB);
eventRegVM.Event.Entity_Organisation.Entity_Base = Mapper.Map<Model.Entity_Base, Entity_BaseViewModel>(eventOrgBaseDB);
//saves Event_Registration
eventRegDB = Mapper.Map<Event_RegistrationViewModel, Model.Event_Registration>(eventRegVM);
eventRegDB.Event = eventDB;
eventRegDB.EventID = eventDB.ID;
eventRegDB.Event.Entity_Organisation = context.Entity_Organisation.Single(o => o.ID == eventOrgBaseDB.ID);
eventRegDB.Event.Entity_Organisation.Entity_Base = eventOrgBaseDB;
//Add the link between EVENT and REGISTRATION
context.Event_Registration.AddObject(eventRegDB);
int numChanges = context.SaveChanges();
var regs = context.Event_Registration.Where(r => r.ID != null).ToList();
}
}
I have SQL Profiler running in the background and when SaveChanges is called I see this SQL code (numChanges is 1):
exec sp_executesql N'declare #generated_keys table([ID] uniqueidentifier)
insert [dbo].[Event_Registration]([EventID], [DateSubmitted], [HasPaid], [PaymentMethod], [Comments], [AmountPaid])
output inserted.[ID] into #generated_keys
values (#0, #1, null, #2, null, #3)
select t.[ID]
from #generated_keys as g join [dbo].[Event_Registration] as t on g.[ID] = t.[ID]
where ##ROWCOUNT > 0',N'#0 uniqueidentifier,#1 datetime2(7),#2 int,#3 decimal(19,4)',#0='1D841F75-AEA1-4ED1-B3F0-4E3994D7FC0D',#1='2012-07-04 14:59:45.5239309',#2=0,#3=0
regs will contain three exist rows and my new row. However, I cannot run a SELECT statement in SQL Server and see my new row. Running this many times will get me the same result - three existing rows in the database and a new fourth row that will never make it to the database.
eventRegDB also contains the GUID created for the primary key, ID; I assume SQL server does this but not 100% sure of that.
I have taken the above TSQL and run it in a query window against my database - I get new rows in my Event_Registration table after that - that is how the three existing rows were created.
I see now exceptions or other errors generated and cannot find any reason this would not save to the database. Any ideas? If you want to see the schema for the SQL, how to recreate the database, the code (any or all), then ask - this is all hosted on http://animalrescue.codeplex.com/ but I haven't stored this code, yet.
You are missing transaction.Complete() so your transaction is never committed. When the using block for TransactionScope completes your transaction is rolled back.

F# LINQ type class fit in one

I am learning LINQ with F# 3.0. I want to know how to convert my old class to use new LINQ features in F# 3.0
For example, I have created a simple data table in SQL Server 2008 R2, the database name is myDatabase.
-- Create the Table1 table.
CREATE TABLE [dbo].[Table1] (
[Id] INT NOT NULL,
[TestData1] INT NOT NULL,
[TestData2] FLOAT (53) NOT NULL,
[Name] NTEXT NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
USE MyDatabase
INSERT INTO Table1 (Id, TestData1, TestData2, Name)
VALUES(1, 10, 5.5, 'Testing1');
INSERT INTO Table1 (Id, TestData1, TestData2, Name)
VALUES(2, 20, -1.2, 'Testing2');
For F# 2.0, I can create a small class to retrieve data from Table1 and return to main program.
#light
module myData
open System
open System.Collections.Generic
open System.Data
open System.Data.SqlClient
type getData(tableName: string) = class
let sqlConn = "server=(local); Integrated Security=True; Database=MyDatabase"
let connDB = new SqlConnection(sqlConn)
let sqlByFun (cmdText, unwrapping) =
use cmd = new SqlCommand(cmdText, connDB)
let reader = cmd.ExecuteReader()
seq { while reader.Read() do
yield unwrapping(reader) }
let dbTable1(tableName: string) =
let sql = "SELECT Id, TestData1, TestData2, Name FROM Table1 ORDER BY Id"
connDB.Open()
let data = sqlByFun(sqlChanges, (fun reader ->
(reader.GetInt32(0), reader.GetInt32(1),
reader.GetDecimal(2),reader.GetString(3))))
|> Seq.toArray
connDB.Close()
data
member this.getTable(table1) = dbTable1(table1)
Then in my main program: program.fs, I can use the class of myData:
#light
open myData
open System
open System.Collections.Generic
open System.Data
open System.Data.SqlClient
let db = new myData.getData("Table1")
let table1Data = db.getTable("Table1")
printfn "Done"
Now, with F# 3.0 and LINQ, I can re-write the class like this:
#light
open System
open System.Data
open System.Data.SqlClient
open System.Data.Linq
open Microsoft.FSharp.Data.TypeProviders
open Microsoft.FSharp.Linq
[<Generate>]
type dbSchema = SqlDataConnection<"Data Source=.;Initial Catalog=MyDatabase;Integrated Security=True">
let db = dbSchema.GetDataContext()
let dbTable1(tableName: string) =
let data =
query {
for row in db.Table1 do
sortBy row.Id
select (row.Id, row.TestData1, row.TestData2, row.Name)
}
data
I can see this part, the code is much shorter and cleaner, but I want this to be in a class and can be called from my main program.
How can I put this above LINQ code into a class and used by main program?
Thanks,
John
It is not entirely clear from your question what you're trying to achieve. In your samples, your new class (and also the method) take a tableName parameter, but you're always reading data from Table1 anyway.
Writing LINQ code that is parameterized by table name is not going to be easily possible, because different tables may have different columns and so you cannot use the same type to access them. If you have different tables with the same structure, then you can probably merge them into a single table and add an additional column to distinguish between the records. If you want to access different tables, then you'll need to write different query for every table.
Regarding encapsulation in a class, you can do that easily in F# 3.0:
// Global declarations for SQL type provider
[<Literal>]
let connStr = "Data Source=.;Initial Catalog=MyDatabase;Integrated Security=True"
[<Generate>]
type dbSchema = SqlDataConnection<connStr>
// Declaration of a class that exposes data access functionality
type DataAccess() =
// Note: You can pass different connection string to 'GetDataContext'
// if you want to keep connection string in config file instead of in code.
let db = dbSchema.GetDataContext()
/// Reads data from Table1
member x.LoadTable1() =
query { for row in db.Table1 do
sortBy row.Id
select (row.Id, row.TestData1, row.TestData2, row.Name) }

Resources