I am doing a simple select with a date filter on it with a months range where only 32 records are present however its taking 15 seconds to query and return the data I am using sage 50 as you can probally tell and c#. I am using odbc to create the query the seem speeds can be found if i use the odbc query tool.
This is for a stright forward select and it should not be taking that long to return the data through odbc.
String SQL = string.Format("SELECT 'ORDER_NUMBER', 'ORDER_OR_QUOTE',
'ANALYSIS_1','ACCOUNT_REF','ORDER_DATE','NAME',
'COURIER_NUMBER','COURIER_NAME','CUST_TEL_NUMBER'
,'DESPATCH_DATE','ACCOUNT_REF', 'DEL_NAME', 'DEL_ADDRESS_1',
'DEL_ADDRESS_2', 'DEL_ADDRESS_3', 'DEL_ADDRESS_4', 'DEL_ADDRESS_5',
'INVOICE_NUMBER','ORDER_DATE','INVOICE_NUMBER_NUMERIC',
'CONTACT_NAME','CONSIGNMENT', 'NOTES_1', 'ITEMS_NET'
,'ITEMS_GROSS','QUOTE_STATUS' FROM SALES_ORDER WHERE ORDER_DATE
='{0}' and ORDER_DATE <='{1}'", fromD, toD);
public List<SalesOrders> GetSalesOrders()
{
List<SalesOrders> _salesOrdersList = new List<SalesOrders>();
try
{
string sageDsn = ConfigurationManager.AppSettings["SageDSN"];
string sageUsername = ConfigurationManager.AppSettings["SageUsername"];
string sagePassword = ConfigurationManager.AppSettings["SagePassword"];
//int totalRecords = GetSalesOrdersount();
int counter = 0;
//using (var connection = new OdbcConnection("DSN=SageLine50v24;Uid=Manager;Pwd=;"))
using (var connection = new OdbcConnection(String.Format("DSN={0};Uid={1};Pwd={2};", sageDsn, sageUsername, sagePassword)))
{
connection.Open();
//string sql = string.Format(getInvoiceSql, customerCode, DateTime.Today.AddMonths(-1).ToString("yyyy-MM-dd"));
string fromD = dtpFrom.Value.ToString("yyyy-MM-dd");
string toD = dtpTo.Value.ToString("yyyy-MM-dd");
String SQL = string.Format("SELECT 'ORDER_NUMBER', 'ORDER_OR_QUOTE', 'ANALYSIS_1','ACCOUNT_REF','ORDER_DATE','NAME', 'COURIER_NUMBER','COURIER_NAME','CUST_TEL_NUMBER' ,'DESPATCH_DATE','ACCOUNT_REF', 'DEL_NAME', 'DEL_ADDRESS_1', 'DEL_ADDRESS_2', 'DEL_ADDRESS_3', 'DEL_ADDRESS_4', 'DEL_ADDRESS_5', 'INVOICE_NUMBER','ORDER_DATE','INVOICE_NUMBER_NUMERIC', 'CONTACT_NAME','CONSIGNMENT', 'NOTES_1', 'ITEMS_NET' ,'ITEMS_GROSS','QUOTE_STATUS' FROM SALES_ORDER WHERE ORDER_DATE >='{0}' and ORDER_DATE <='{1}'", fromD, toD);
using (var command = new OdbcCommand(SQL, connection))
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
counter++;
backgroundWorker1.ReportProgress(counter);
var salesOrders = new SalesOrders();
salesOrders.ACCOUNT_REF = Convert.ToString(reader["ACCOUNT_REF"]);
salesOrders.RecordIdentifier = "";
salesOrders.ShipmmentId = Convert.ToString(reader["ORDER_NUMBER"]);
salesOrders.OrderDate = Convert.ToDateTime(reader["ORDER_DATE"]);
salesOrders.OrderNumber = Convert.ToString(reader["ORDER_NUMBER"]);
salesOrders.Company = "";
salesOrders.Carrier = Convert.ToString(reader["COURIER_NUMBER"]);
salesOrders.CarrierService = Convert.ToString(reader["COURIER_NAME"]);
salesOrders.CustomerName = Convert.ToString(reader["NAME"]);
salesOrders.ShipToAddress1 = Convert.ToString(reader["DEL_ADDRESS_1"]);
salesOrders.ShipToAddress2 = Convert.ToString(reader["DEL_ADDRESS_2"]);
salesOrders.ShipToAddress3 = Convert.ToString(reader["DEL_ADDRESS_3"]);
salesOrders.ShipToAddress4 = Convert.ToString(reader["DEL_ADDRESS_4"]);
salesOrders.ShipToAddress5 = Convert.ToString(reader["DEL_ADDRESS_5"]);
salesOrders.ShiptoAttention = Convert.ToString(reader["DEL_NAME"]);
salesOrders.ShiptoPhoneNo = Convert.ToString(reader["CUST_TEL_NUMBER"]);
salesOrders.Country = Convert.ToString(reader["ANALYSIS_1"]);
salesOrders.ShiptoEmail = "";
salesOrders.MakeAddressDefault = "Y";
bool isProcessed = _sqlManager.hasbeenProcessed(salesOrders.OrderNumber);
if (isProcessed == true)
salesOrders.Exported = true;
_salesOrdersList.Add(salesOrders);
}
}
}
}
return _salesOrdersList.OrderByDescending(o => o.OrderDate).ToList();
}
don't use {0}, {1} for embedding values in strings... ADD via Parameters
String SQL =
#"SELECT
ORDER_NUMBER,
ORDER_OR_QUOTE,
ANALYSIS_1,
ACCOUNT_REF,
ORDER_DATE,
`NAME`,
COURIER_NUMBER,'
OURIER_NAME,
CUST_TEL_NUMBER,
DESPATCH_DATE,
ACCOUNT_REF,
DEL_NAME,
DEL_ADDRESS_1,
DEL_ADDRESS_2,
DEL_ADDRESS_3,
DEL_ADDRESS_4,
DEL_ADDRESS_5,
INVOICE_NUMBER,
ORDER_DATE,
INVOICE_NUMBER_NUMERIC,
CONTACT_NAME,
CONSIGNMENT,
NOTES_1,
ITEMS_NET,
ITEMS_GROSS,
QUOTE_STATUS
FROM
SALES_ORDER
WHERE
ORDER_DATE >= ?
and ORDER_DATE <= ?
ORDER BY
ORDER_DATE DESC";
using (var command = new OdbcCommand(SQL, connection))
{
// assuming fields are actually date data types fields
command.Parameters.Add( "parmFromDate", fromD );
command.Parameters.Add( "parmToDate", toD );
The "?" in the query are place-holders for the parameter values which are handled by the ODBC process. The Parameters being added within the using() portion are added in the same ordinal position as their respective place-holder parts. I just assigned the parameter name to give context to whoever is looking at it after.
The query itself SHOULD be very quick depending on the date range you are pulling. Even added the SQL Order by descending order so it is pre-pulled down in the order you intended it too.
Related
var builder = new SqlBuilder();
var sqlQuery = #"Select * From xxxxTable /**where**/";
var template = builder.AddTemplate(sqlQuery);
builder.Where("xxx1= #xxx1", new { xxx1= xxx1});
builder.Where("convert(date,xxx2) < convert(date,GETDATE())"); // compare with sql server current date is Okay!
builder.Where("convert(date,xxx3) < convert(date,#xxx3)", new {xxx3 = xxx3}); // compare with parameter date is not okay!
using (var connection = Db.GetConnection(xx_ConnectionString_xx))
{
return connection.Query<xxModel>(template.RawSql,template.Parameters).FirstOrDefault();
}
Any idea for SqlBuilder usage for comparing datetime column value.
Thank you ....
I have different input parameters to enter and all those parameters need to enter in different tables. Therefore I use 3 different stored procedures. From each stored procedure, i need to use the output parameter of the former stored procedure as an input parameter.
Those are the 3 stored procedures that I use
Stored procedure 1 = Levlotnr:
create procedure Levlotnr
(#lotleverancier nvarchar (256),
#leverancier nvarchar (256),
#newinsertedlevID int output)
as
insert into leveranciersLotnr
values (#lotleverancier,
(select leveranciersID
from Leverancier
where LeveranciersNaam = #leverancier) )
select ##IDENTITY as newinsertedlevID
Stored procedure 2 = LotIN:
create procedure LotIN
(#datumIN datetime,
#hoeveelIN decimal,
#grondstofIN nvarchar(256),
#newinsertedLotnrINID int output,
#lotnlevID int)
as
insert into LotnummerIN
values (#datumIN, #hoeveelIN,
(select GrondstofID
from Grondstoffen
where Grondstofomschrijving = #grondstofIN),
#lotnlevID)
select ##IDENTITY as newinsertedLotnrIN
Stored procedure 3 = StockIN:
create procedure StockIN
(#omschrstockIN nvarchar (256),
#lotnrIN int)
as
update StockPlaatsIN
set LotnummerINID = #lotnrIN
where StockINOmschrijving = #omschrstockIN
And this is a code that I already wrote
public void Nieuweontvangst (DateTime datumIN, string leverancier,
string levlotnr, double hoeveelheidIN,
string eenheidIN, string grondstofIN,
string stockplaatsIN, int lotnrlevID, int lotnrINID)
{
var manager = new LotnummersDBManager();
using (var conLotnummers = manager.Getconnection())
{
conLotnummers.Open();
using (var traNieuweOntvangst =
conLotnummers.BeginTransaction(IsolationLevel.ReadCommitted))
{//begin traNieuweOntvangst
//first transaction: the output parameter #newinsertedlevID
//just needs to be used in the second transaction, but not be displayed
//------------------
using (var comlevlotnrs = conLotnummers.CreateCommand())
{
comlevlotnrs.Transaction = traNieuweOntvangst;
comlevlotnrs.CommandType = CommandType.StoredProcedure;
comlevlotnrs.CommandText = "Levlotnr"; //name of first
stored procedure
var parlotleverancier = comlevlotnrs.CreateParameter();
parlotleverancier.ParameterName = "#Lotleverancier";
parlotleverancier.Value = levlotnr;
comlevlotnrs.Parameters.Add(parlotleverancier);
var parleverancier = comlevlotnrs.CreateParameter();
parleverancier.ParameterName = "#leverancier";
parleverancier.Value = leverancier;
comlevlotnrs.Parameters.Add(parleverancier);
var parlotlevID = comlevlotnrs.CreateParameter();
parlotlevID.ParameterName = "#newinsertedlevID";
parlotlevID.DbType = DbType.Int32;
parlotlevID.Direction = ParameterDirection.Output;
comlevlotnrs.Parameters.Add(parlotlevID);
}
using (var comLotnrsIN = conLotnummers.CreateCommand())
{// second transaction= output parameter #newinsertedlevID
// should be used here where now stands #lotnrlevIN.
// THIS IS WHERE I STRUGGLE
// also here I get an output parameter
// #newinsertedLotnrINID only to be used in the 3rd
// transaction, not to be displayed.
comLotnrsIN.Transaction = traNieuweOntvangst;
comLotnrsIN.CommandType = CommandType.StoredProcedure;
comLotnrsIN.CommandText = "LotnrIN";
var pardatumIN = comLotnrsIN.CreateParameter();
pardatumIN.ParameterName = "#datumIN";
pardatumIN.Value = datumIN;
comLotnrsIN.Parameters.Add(pardatumIN);
var parhoeveelIN = comLotnrsIN.CreateParameter();
parhoeveelIN.ParameterName = "#hoeveelIN";
parhoeveelIN.Value = hoeveelheidIN;
comLotnrsIN.Parameters.Add(parhoeveelIN);
var pargrondstofIN = comLotnrsIN.CreateParameter();
pargrondstofIN.ParameterName = "#grondstofIN";
pargrondstofIN.Value = grondstofIN;
comLotnrsIN.Parameters.Add(pargrondstofIN);
var parlotnrlevIN = comLotnrsIN.CreateParameter();
parlotnrlevIN.ParameterName = "#lotnrlevIN";
parlotnrlevIN.Value = lotnrlevID;
comLotnrsIN.Parameters.Add(parlotnrlevIN);
var parLotIN = comLotnrsIN.CreateParameter();
parLotIN.ParameterName = "#newinsertedLotnrINID";
parLotIN.DbType = DbType.Int32;
parLotIN.Direction = ParameterDirection.Output;
comLotnrsIN.Parameters.Add(parLotIN);
}
using (var comStockeren = conLotnummers.CreateCommand())
{
//Third transaction
// I need to use the output parameter from 2nd transaction
// #newinsertedLotnrINID where you see now #lotnrINID.
//THIS IS THE SAME STRUGGLE AS 2ND TRANSACTION
comStockeren.Transaction = traNieuweOntvangst;
comStockeren.CommandType = CommandType.StoredProcedure;
comStockeren.CommandText = "StockIN";
var parlotIN = comStockeren.CreateParameter();
parlotIN.ParameterName = "#lotnrINID";
parlotIN.Value = lotnrINID;
var paromschrStockIN = comStockeren.CreateParameter();
paromschrStockIN.ParameterName = "#omschrstockIN";
paromschrStockIN.Value = stockplaatsIN;
comStockeren.Parameters.Add(paromschrStockIN);
}
traNieuweOntvangst.Commit();
}
}
}
I am trying to delimit a file path and populate it into multiple database columns.
So if the string were C:\Engineering\Structural\CAD\Baghouse.dwg then it would populate 8 database columns, 5 with values and 3 with "".
DIR01 | C:
DIR02 | Engineering
DIR03 | Structural
DIR04 | CAD
DIR05 | Baghouse.dwg
DIR06 |
DIR07 |
DIR08 |
I can easily delimit the file path using Path.DirectorySeparatorChar, and when I debug and look in the Locals box the array looks perfect.
What I can't figure out is how to access each element of the array and put them into separate columns.
private void cmdDelimitFilePath_Click(object sender, EventArgs e)
{
string SqlCmd;
string ScannedPath = String.Empty;
string DIR01 = String.Empty;
string DIR02 = String.Empty;
string DIR03 = String.Empty;
string DIR04 = String.Empty;
string DIR05 = String.Empty;
string DIR06 = String.Empty;
string DIR07 = String.Empty;
string DIR08 = String.Empty;
DataTable dt = new DataTable("DirectoryAnalysis");
SqlConnectionStringBuilder ConnStrBuilder = new SqlConnectionStringBuilder();
try
{
ConnStrBuilder.DataSource = txtServer.Text;
ConnStrBuilder.InitialCatalog = txtSourceSchema.Text;
ConnStrBuilder.Password = txtPassword.Text;
ConnStrBuilder.UserID = txtUser.Text;
//this connects to the database and creates the new fields
using (DbConnection connexx = new SqlConnection(ConnStrBuilder.ConnectionString))
{
connexx.Open();
using (DbCommand command = new SqlCommand("ALTER TABLE [DirectoryAnalysis] ADD [DIR01] varchar(100), [DIR02] varchar(100), [DIR03] varchar(100), [DIR04] varchar(100), [DIR05] varchar(100), [DIR06] varchar(100), [DIR07] varchar(100), [DIR08] varchar(100)"))
{
command.Connection = connexx;
command.ExecuteNonQuery();
}
}
// this connects to the database and populates the new fields
using (SqlConnection Conn = new SqlConnection(ConnStrBuilder.ConnectionString))
{
Conn.Open();
SqlCmd = "SELECT [DA_Id], [ScannedPath], [DIR01], [DIR02], [DIR03], [DIR04], [DIR05], [DIR06], [DIR07], [DIR08] FROM [DirectoryAnalysis]";
using (SqlDataAdapter da = new SqlDataAdapter(SqlCmd, Conn))
{
da.Fill(dt);
foreach (DataRow dr in dt.Rows)
{
ScannedPath = Convert.ToString(dr["ScannedPath"]);
//This returns each individual folder in the directories array.
string[] directories = ScannedPath.Split(Path.DirectorySeparatorChar);
//You can get the number of folders returned like this:
int folderCount = directories.Length;
// everything works perfectly up to here...
foreach (string part in directories)
{
// how to access elements of the array?
//this is as close as I have been...
DIR01 = Convert.ToString(part[0]);
dr["DIR01"] = DIR01;
DIR02 = Convert.ToString(part[1]);
dr["DIR02"] = DIR02;
DIR03 = Convert.ToString(part[2]);
dr["DIR03"] = DIR03;
// and repeat through 8 if this would work
}
}
MessageBox.Show("DirectoryAnalysis has been updated.", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
catch (Exception Ex)
{
MessageBox.Show(Ex.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
finally
{
this.Cursor = Cursors.Default;
}
}
IF i understand correctly, the problem is the following:
You need access to all the elements of the array "directories" at the same time. However, you LOSE it by doing:
foreach (string part in directories)
because "part" is the current element, and it's difficult(ish) to take the previous n elements.
Hence, i think the fix is:
Stop using the foreach loop and access each element of the array like this:
dir1 = directories[0]
dir2 = directories[1]
and so on.
Like this, you can also use them directly in your sql insert statement.
Hope this helps!
What about something like this:
string[] StrArr = filePath.Split('\');
.
for (int i = 0; i < StrArr.length - 1; i++)
{
//Run this SQL command:
String.Format("UPDATE [table] (DIR{0}) VALUES ({1})", i + 1, StrArr[i])
}
Split string to Array
loop through array with a for loop
Update database with the values
Following code to insert records, first I check to make sure the email address already exists in the DB if it does I return alerting the user otherwise I insert a new record.
using (var sqlCon = new SqlConnection(Context.ReturnDatabaseConnection()))
{
sqlCon.Open();
var emailExists = sqlCon.Query<UserProfile>(#"SELECT UserId FROM User_Profile WHERE EmailAddress = #EmailAddress",
new { EmailAddress = userRegister.EmailAddress.Trim() }).FirstOrDefault();
if (emailExists == null) // No profile exists with the email passed in, so insert the new user.
{
var userProfileEntity = new UserProfileEntity
{
UniqueId = Guid.NewGuid(),
Firstname = userRegister.Firstname,
Surname = userRegister.Surname,
EmailAddress = userRegister.EmailAddress,
Username = CreateUsername(userRegister.Firstname),
Password = EncryptPassword(userRegister.Password),
AcceptedTerms = true,
AcceptedTermsDate = System.DateTime.Now,
AccountActive = true,
CurrentlyOnline = true,
ClosedAccountDate = null,
JoinedDate = System.DateTime.Now
};
userProfile.UserId = SqlMapperExtensions.Insert(sqlCon, userProfileEntity); // Call the Dapper Extension method to insert the new record
userProfile.Firstname = userRegister.Firstname;
userProfile.Username = userProfile.Username;
userProfile.EmailAddress = userProfile.EmailAddress;
Registration.SendWelcomeEmail(userRegister.EmailAddress, userRegister.Firstname); // Send welcome email to new user.
}
}
This line calls the Dapper Extension Class
userProfile.UserId = SqlMapperExtensions.Insert(sqlCon, userProfileEntity); // Call the Dapper Extension method to insert the new record
Calls the following and it is here that I get the error
ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized
public static long Insert<T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
{
using (var tx = connection.BeginTransaction())
{
var type = typeof(T);
var name = GetTableName(type);
var sb = new StringBuilder(null);
sb.AppendFormat("insert into {0} (", name);
var allProperties = TypePropertiesCache(type);
var keyProperties = KeyPropertiesCache(type);
for (var i = 0; i < allProperties.Count(); i++)
{
var property = allProperties.ElementAt(i);
if (keyProperties.Contains(property)) continue;
sb.Append(property.Name);
if (i < allProperties.Count() - 1)
sb.Append(", ");
}
sb.Append(") values (");
for (var i = 0; i < allProperties.Count(); i++)
{
var property = allProperties.ElementAt(i);
if (keyProperties.Contains(property)) continue;
sb.AppendFormat("#{0}", property.Name);
if (i < allProperties.Count() - 1)
sb.Append(", ");
}
sb.Append(") ");
// here is where the error occures vvvvvvvv
connection.Execute(sb.ToString(), entityToInsert, transaction: transaction, commandTimeout: commandTimeout);
//NOTE: would prefer to use IDENT_CURRENT('tablename') or IDENT_SCOPE but these are not available on SQLCE
var r = connection.Query("select ##IDENTITY id");
tx.Commit();
return (int)r.First().id;
}
}
Question is how do I fix this, I have googled and tried solutions but with no avail.
Thanks
I have a table which contains around 500 Million records. I am reading the data from table and storing those in a Dictionary.
EDIT: I am loading the data into dictionary because these data needs to be compared with another volume of data coming from an indexing server.
My code is as below:
public static void GetDetailsFromDB()
{
string sqlStr = "SELECT ID, Name ,Age, email ,DOB ,Address ,Affiliation ,Interest ,Homepage FROM Author WITH (NOLOCK) ORDER BY ID";
SqlCommand cmd = new SqlCommand(sqlStr, _con);
cmd.CommandTimeout = 0;
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
//Author Class
Author author = new Author();
author.id = Convert.ToInt32(reader["ID"].ToString());
author.Name = reader["Name"].ToString().Trim();
author.age = Convert.ToInt32(reader["Age"].ToString());
author.email = reader["email"].ToString().Trim();
author.DOB = reader["DOB"].ToString().Trim();
author.Address = reader["Address"].ToString().Trim();
author.Affiliation = reader["Affiliation"].ToString().Trim();
author.Homepage = reader["Homepage"].ToString().Trim();
string interests = reader["Interest"].ToString().Trim();
author.interest = interests.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()).ToList();
if (!AuthorDict.ContainsKey(author.id))
{
AuthorDict.Add(author.id, author);
}
if (AuthorDict.Count % 1000000 == 0)
{
Console.WriteLine("{0}M author loaded.", AuthorDict.Count / 1000000);
}
}
}
}
This process is taking long time to read and store all 500 Million records from DB. Also, the RAM usage is very high.
Can this be optimized ? also, can the running time be decreased ? any help is appreciated.
If I hold my nose I can come up with the following optimisations:
Store the ordinal positions of your fields in local variables and reference the fields in your reader using these ordinal variables.
Do not call ToString on the reader and convert - just get the value out in the correct type.
Check for the existence of the author id in the AuthorDict as soon as you have the id. Don't even create the Author instance if you don't need it.
using (SqlDataReader reader = cmd.ExecuteReader())
{
var idOrdinal = reader.GetOrdinal("ID");
//extract other ordinal positions and store here
while (reader.Read())
{
var id = reader.GetInt32(idOrdinal);
if (!AuthorDict.ContainsKey(id))
{
Author author = new Author();
author.id = reader.GetInt32(idOrdinal);
...
}
}
}