I've written a database generation script in SQL and want to execute it in my Adobe AIR application:
Create Table tRole (
roleID integer Primary Key
,roleName varchar(40)
);
Create Table tFile (
fileID integer Primary Key
,fileName varchar(50)
,fileDescription varchar(500)
,thumbnailID integer
,fileFormatID integer
,categoryID integer
,isFavorite boolean
,dateAdded date
,globalAccessCount integer
,lastAccessTime date
,downloadComplete boolean
,isNew boolean
,isSpotlight boolean
,duration varchar(30)
);
Create Table tCategory (
categoryID integer Primary Key
,categoryName varchar(50)
,parent_categoryID integer
);
...
I execute this in Adobe AIR using the following methods:
public static function RunSqlFromFile(fileName:String):void {
var file:File = File.applicationDirectory.resolvePath(fileName);
var stream:FileStream = new FileStream();
stream.open(file, FileMode.READ)
var strSql:String = stream.readUTFBytes(stream.bytesAvailable);
NonQuery(strSql);
}
public static function NonQuery(strSQL:String):void {
var sqlConnection:SQLConnection = new SQLConnection();
sqlConnection.open(File.applicationStorageDirectory.resolvePath(DBPATH));
var sqlStatement:SQLStatement = new SQLStatement();
sqlStatement.text = strSQL;
sqlStatement.sqlConnection = sqlConnection;
try {
sqlStatement.execute();
} catch (error:SQLError) {
Alert.show(error.toString());
}
}
No errors are generated, however only tRole exists. It seems that it only looks at the first query (up to the semicolon- if I remove it, the query fails). Is there a way to call multiple queries in one statement?
I wound up using this. It is a kind of a hack, but it actually works pretty well.
The only thing is you have to be very careful with your semicolons. : D
var strSql:String = stream.readUTFBytes(stream.bytesAvailable);
var i:Number = 0;
var strSqlSplit:Array = strSql.split(";");
for (i = 0; i < strSqlSplit.length; i++){
NonQuery(strSqlSplit[i].toString());
}
The SQLite API has a function called something like sqlite_prepare which takes one statement and prepares it for execution, essentially parsing the SQL and storing it in memory. This means that the SQL only has to be sent once to the database engine even though the statement is executed many times.
Anyway, a statement is a single SQL query, that's just the rule. The AIR SQL API doesn't allow sending raw SQL to SQLite, only single statements, and the reason is, likely, that AIR uses the sqlite_prepare function when it talks to SQLite.
What about making your delimiter something a little more complex like ";\n" which would not show up all that often. You just have to ensure when creating the file you have a line return or two in there. I end up putting two "\n\n" into the creation of my files which works well.
Related
Using Async-based Webservice and Async framework in WinRT (Win8) to get a large recordsets(1000 to 5000) from a remote Ms SQL Server.
I want to know :
1) Which is the best approach to handle to insert large recordsets into SQLite?
2) Using RollBack transaction will start all over again if there is connection error. The below method will insert whatever and I can update the data later if the records are not complete. Is this a good approach?
3) Any better way to enhance my below solution?
This foreach statement to handle
each reords in returned result which returned from Async-Based WebService:
foreach (WebServiceList _List in IList)
{
InsertNewItems(_List.No, _List.Description, _List.Unit_Price, _List.Base_Unit_of_Measure);
}
private void InsertNewItems(string ItemNo, string ItemName, decimal ItemPrice, string ItemBUoM)
{
var existingItem = (db2.Table().Where(c => c.No == ItemNo)).SingleOrDefault();
if (existingItem != null)
{
existingItem.No = ItemNo;
existingItem.Description = ItemName;
existingItem.Unit_Price = ItemPrice;
existingItem.BaseUnitofMeasure = ItemBUoM;
int success = db2.Update(existingItem);
}
else
{
int success = db2.Insert(new Item()
{
No = ItemNo,
Description = ItemName,
Unit_Price = ItemPrice,
Base_Unit_of_Measure = ItemBUoM
});
}
}
You should use RunInTransaction from sqlite-net. The documentation for it says,
Executes action within a (possibly nested) transaction by wrapping it
in a SAVEPOINT. If an exception occurs the whole transaction is rolled
back, not just the current savepoint. The exception is rethrown.
using (var db = new SQLiteConnection(DbPath))
{
db.RunInTransaction(() =>
{
db.InsertOrReplace(MyObj);
});
}
Wiki article for Transactions at GitHub
The most important performance aspect for bulk inserts is to use a single transaction. If you want to handle aborts, I suggest that you feed the data in sufficiently large parts and restart from that point on next time. An SQL transaction either finishes completely or rolls back completely, so unless the input data changes between two runs, there should be no need to do an insert-or-update.
See, for example, here for a discussion of SQLite bulk insert performance using different methods.
Let me explain you in details the scenario that I am having and the solution I am looking for.
Firstfully, I created a stored procedure that outputs simple things such as 2 tables and a message 'don't stop here'"
T-SQL:
USE [mydb]
GO
/****** Object: StoredProcedure [dbo].[BackupDatabase] Script Date: 2/26/2013 11:29:10 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create PROCEDURE [dbo].[testing]
AS
BEGIN
select 'A' firstname, 'B' lastname;
print 'dont stop here'
select 1 final
END
Up until now I used to retriew the tables in a single manner by using datarowcollection class, and my static method looked like this:
C#:
public static class DataMan
{
public static DataRowCollection SelectData(string sql)
{
SqlDataSource DS = new SqlDataSource(CS, sql);
return ((DataView)DS.Select(DataSourceSelectArguments.Empty)).ToTable().Rows;
}
public static string CS = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
}
where I can easily get what i needed like here, and locate what evver row I wanted:
DataRowCollection people = Util.SelectData("Select * from students")
But now I am planning to create a stored procedure Like I mentioned above and do somthing like this, for instance:
**DataTableCollection** people = Util.SelectData("exec dbo.Testing")
UPDATE:
so I can locate the specific table from my storedprocedure.
I have tried to use DataTable, DataSet, DataTableCollections but no success.I can't use them in proper way.
Please help me
Thank You
Although it can be used in code-behind as you're illustrating here, the SqlDataSource is more typically used in a declarative manner on an ASP.NET markup page. However, given what you've started, when calling a stored procedure, you should set the SqlCommandType to StoredProcedure, supply the name of the procedure to SelectCommand, and return a DataReader. The DataReader, in turn, supports a NextResult() method that you can call to retrieve each discrete result set your procedure provides. Here is a framework of pseudo code that tries to illustrate how you might leverage this:
// pseudo code
void stub()
{
SqlDataSource d = new SqlDataSource(*connection string*);
d.DataSourceMode = SqlDataSourceMode.DataReader;
d.SelectCommandType = SqlDataSourceCommandType.StoredProcedure;
d.SelectCommand = "dbo.Testing";
// set some parameters
d.SelectParameters.Add("Parameter1Name","Parameter1Value"); // must be tailored to your proc!!
d.SelectParameters.Add("Parameter2Name","Parameter2Value"); // must be tailored to SqlDataReader r = (SqlDataReader) d.Select();
while (r.HasRows)
{
while (r.Read())
{
// do something with each row
}
// advance to next result set
r.NextResult();
}
r.Close();
}
I've been building this project as the solo dev for a while, and while I'm comfortable in the front end and middle tier, I don't really think I'm doing the database the way I should be, and the reason why is because I simply don't really know of any other way. The way I'm currently getting data is by testing out queries in my MySQL workbench and copying and pasting the SQL as a string literal into a method that makes a call to the DB, pulls the data and hydrates my objects.
This hasn't really been a problem until recently, when I had to create a monster of a query and it got me thinking that maybe there's a better way to do this. I don't have a formal DAL separated out, so I know there's room for improvement there, but I was curious about what the correct way would be to store SQL strings. I assume there is a tool somewhere built into VS10 where I can manipulate and work with SQL as SQL instead of as a string.
You should be doing this in stored procedures. That will basically format and store your query. You set parameters that are passed in from your code, then read out the results.
Example:
The C# method:
private void SetNote()
{
const string sql = "sp_SelectControllerNoteByID";
using (var conn = MocSystem.GetMocDbConnection())
{
using (var comm = new SqlCommand(sql, conn))
{
comm.CommandType = CommandType.StoredProcedure;
comm.Parameters.Add(new SqlParameter("#ControllerNoteID", ControllerNoteId));
try
{
conn.Open();
using (var rdr = comm.ExecuteReader())
{
while (rdr.Read())
{
CommentText = rdr["NoteText"].ToString();
_commentor = new User(new Guid(rdr["NoteAuthor"].ToString()));
CommentDate = (DateTime)rdr["NoteDate"];
MocRequestId = (int)rdr["MocRequestID"];
}
}
}
catch (Exception ex)
{
HasError = true;
ErrorMessage += "\nThere was a problem building the note: " + ex.Message;
}
}
}
}
The stored procedure on the DBMS (sql server in this example):
ALTER proc [dbo].[sp_SelectControllerNoteByID]
#ControllerNoteID int
AS
SELECT
ControllerNoteID,
NoteText,
NoteDate,
NoteAuthor,
MocRequestID
FROM
ControllerNotes
WHERE
ControllerNoteID = #ControllerNoteID
So here we call the stored procedure which in this case is just a simple select statement, then we read it out into an object via ADO. Now, this way, you can modify your query without recompiling. Unless you add parameters, in which case you'll have to update those in your code as well.
I am using ASP.NET 4.0 with SQL Server 2008 R2 and PetaPoco ORM.
A Web Form consists of Tab Panels, for example:
-- Employee General Information
-- Appointment
-- Education
Each Tab Panel entry goes to a specific table in SQL Server. I have a main table, employee, with Primary Key empID. Other tables, viz., appointment, education etc. are related with empID.
I have multiple methods to Save record in respective tables:
AddGeneralInformation saves the General Information Tab Panel record.
AddAppointment saves the appointment details and so on.
The application is being used in a concurrent environment where multiple users are either inserting or updating records.
For the second and third methods to insert a new record, the methods must have the correct empID. As soon as the first method (General Information) saves the record, the empID is to be used in other methods.
The problem is that if I use:
Select max(empID)
then it won't pick the correct empID as many users are inserting records.
On solution I feel is to use another column containing SessionID and use this query:
Select max(empID) where sessionID = SessionID
Is there any more reliable way to do this?
** Edited **
protected void btnSave_Click(object sender, EventArgs e)
{
Session.Add("TaskFlag", "New");
AddUpdateEmployee();
AddUpdateAddress();
}
protected void AddUpdateEmployee()
{
var db = new PetaPoco.Database("cnWeb");
var emp = new employee(); */
using (TransactionScope scope = new TransactionScope())
{
db.BeginTransaction();
emp.deptcode = txtEmpCode.Text.TrimEnd();
emp.empname = txtEmpName.Text.TrimEnd();
emp.guardianname = txtGuardian.Text.TrimEnd();
emp.relationwithemployee = ddlRelation.Text.TrimEnd();
emp.gender = ddlGender.SelectedItem.Text;
emp.dateofbirth = Convert.ToDateTime(txtDOB.Text.TrimEnd());
if (Session["TaskFlag"].ToString() == "New")
db.Insert(emp);
else if (Session["TaskFlag"].ToString() == "Update")
db.Update<employee>("SELECT * FROM employee WHERE empid = #0", Session["EmployeeID"]);
reuse.CustomClientMessage("Record Saved", this.Page);
ClearFields();
/* Commit Transaction */
db.CompleteTransaction();
scope.Complete();
}
}
protected void AddUpdateAddress()
{
var db = new PetaPoco.Database("cnWeb");
var addr = new emp_address();
using (TransactionScope scope = new TransactionScope())
{
db.BeginTransaction();
/* Permanaent Address */
addr.perm_houseno = txtPermHouse.Text.TrimEnd();
addr.perm_street = txtPermStreet.Text.TrimEnd();
addr.perm_place = txtPermCity.Text.TrimEnd();
addr.perm_pincode = txtPermPincode.Text.TrimEnd();
addr.perm_landlinephone = txtPermLandline.Text.TrimEnd();
addr.perm_mobilephone = txtPermMobile.Text.TrimEnd();
if (Session["TaskFlag"].ToString() == "New")
db.Insert(addr);
else if (Session["TaskFlag"].ToString() == "Update")
db.Update<emp_address>("SELECT * FROM emp_address WHERE empid = #0", Session["EmployeeID"]);
/* Commit Transaction */
db.CompleteTransaction();
scope.Complete();
}
}
Best solution: make your EmpID column an INT IDENTITY in SQL Server and let the database handle the details.
Anything you dream up in code (T-SQL or C#) is most likely going to have some issues and won't work well under load - why not just use what's there, works, and is tested by hundreds of thousands of users?
Once you insert a row into a table with an INT IDENTITY column, you can fetch that value using
DECLARE #NewEmpID INT
SELECT #NewEmpID = SCOPE_IDENTITY()
or alternatively, you could use the OUTPUT clause on your INSERT statement
INSERT INTO dbo.YourTableHere(List of columns)
OUTPUT Inserted.EmpID
VALUES(list of value here)
create a new column like created_datewith datetime in the table Employee General Information and get the latest record.
Try this Procedure
create Procedure Get_LastRow
(
-- Table name to retrieve last record
#Tname Varchar(50)
)
AS
BEGIN
EXECUTE ('DECLARE GETLAST CURSOR DYNAMIC FOR SELECT * FROM ' + #Tname)
OPEN GETLAST
FETCH LAST FROM GETLAST
CLOSE GETLAST
DEALLOCATE GETLAST
END
Execute the procedure like this.
EXEC Get_LastRow 'dbo.TableA'
Just after the Insert query, you the SELECT ##IDENTITY. You field EmpID need to be a IDENTITY TO work.
I'm developing an application in AIR via Flex, but I'm not seeing where I'm going wrong with SQLite (I'm used to MySQL). Parameters work, but only in certain instances. Is this part of the built-in sanitation system against sql injection? Thanks for any help!
Works:
sqlite
"INSERT :Fields FROM Category", where the parameter is :Fields = "*"
as3
var statement:SQLStatement = new SQLStatement();
statement.connection = connection;
statement.text = "INSERT :Fields FROM Category";
statement.parameters[":Fields"] = "*";
statement.execute;
Doesn't Work (SQL syntax error at ":Table"):
sqlite
"INSERT :Fields FROM :Table", where the parameters are :Fields = "*" and :Table = "Category"
as3
var statement:SQLStatement = new SQLStatement();
statement.connection = connection;
statement.text = "INSERT :Fields FROM :Table";
statement.parameters[":Fields"] = "*";
statement.parameters[":Table"] = "Category";
statement.execute;
Generally one cannot use SQL parameters/placeholders for database identifiers (tables, columns, views, schemas, etc.) or database functions (e.g., CURRENT_DATE), but instead only for binding literal values.
With server-side support for parameterized (a.k.a. prepared) statements, the DB engine parses your query once, remembering out the peculiars of any parameters -- their types, max lengths, precisions, etc. -- that you will bind in subsequent executions of the already-parsed query. But the query cannot be properly parsed into its syntactic elements if critical bits, like database objects, are unknown.
So, one generally has to substitute table names oneself, in a stored procedure or in client code which dynamically concats/interpolates/whatevers the SQL statement to be properly executed. In any case, please remember to use your SQL API's function for quoting database identifiers, since the API won't do it for you.
Not sure if this is the same but I ran across something similar in Java. Basically you can't add a table as a parameter so you must generate the statement like so:
var statement:SQLStatement = new SQLStatement();
statement.connection = connection;
statement.text = stringUtil.substitute("INSERT :Fields FROM {0}", "Category");
statement.parameters[":Fields"] = "*";
statement.execute;
This is mostly likely not the securest solution, so you might want to some custom validation of the data before you add the table name.. so someone doesn't try to send it the table name ";drop tableName..."