We are working with an app, .NET, in which when you press a button a DevExpress form is opened and a SQL Server query is executed, so it can fill with data some comboboxes. Application is working fine in lots of customers, but in a particular one it´s taking more than a minute in loading the form. I can see in the performance monitor that SQL Server is taking a lot of CPU when I want to load the form.
I executed the query directly in SQL Server Management Studio, taking no more than a second, however I tried having a look at SQL Activity Monitor and what I can see here (not happening to other customers, same IO, same SQL, same everything) is this:
So the thing I can see here, that I don´t understand, is why is this query having so much executions? Why is it taking so long to retrieve data?
Here it´s the execution plan of this query:
Select *
From cuinac_pos
Where [group] in (Select [group]
From proc_groups
Where Code = 13100271)
Thank you for any help you can give me, and please if I can give any more info do not hesitate to ask.
Once again, thanks!
AFTER ADDING THE EXECUTION PLAN SUGGESTED INDEX
EXECUTION PLAN FOR QUERY
Select count(*)
From proc_groups
Where Code = 13100271
Definition of the index in proc_groups:
Example of the code:
private static void LoadDTPurchaseHerdRelation(Int32 status, Int32 herdNumber)
{
try
{
StringBuilder sb = new StringBuilder();
sb.Append(" Select gr.[group] as HerdId, gr.code as HerdNumber, bo.code as PurchaseCode");
sb.Append(" From cuinac_pos bo ");
sb.Append(" inner join proc_groups gr on bo.code=gr.code ");
if (herdNumber == 0)
{
string s1 = " Where (gr.created between '2015-12-09' And '2016-01-08') ";
sb.Append(s1);
if (status != 4)
{
string s2 = string.Format(" AND bo.purchasestatus = {0} ", status);
sb.Append(s2);
}
sb.Append(" order by bo.code ");
}
else
{
string s3 = string.Format(" Where gr.code = '{0}' ", herdNumber);
sb.Append(s3);
}
DTPurchaseHerdRelation.Clear();
using (ConnectionScope cs = new ConnectionScope())
{
SqlDataAdapter adapter = new SqlDataAdapter(sb.ToString(), (SqlConnection)cs.Connection);
adapter.Fill(DTPurchaseHerdRelation);
}
}
catch (Exception ex)
{
}
}
}
}
Execution plan for query
Select * From cuinac_pos Where [group] in (Select [group] From proc_groups Where Code = N'13100271')
Solved:
I finally got it by adding indexes suggested in the answer marked as correct, and adding in the code, in the queries which searched by nvarchar value "Code", an N before rhe value as suggested in comments by shriop. Thank you all for your effort!
For this query:
Select *
From cuinac_pos
Where [group] in (Select [group] From proc_groups Where Code = 13100271 );
The optimal indexes are proc_groups(code, group) and cuinac_pos(group). Having those indexes might help.
EDIT:
For performance, this might be better:
Select *
From cuinac_pos cp
Where exists (Select 1
From proc_groups pg
Where pg.Code = 13100271 and pg.[group] = cp.[group]
);
with an index on `proc_groups(group, code)
Whenever I read something like "fast in SSMS but slow in application" I have to think about this:
http://www.sommarskog.se/query-plan-mysteries.html
This applies especially to older DBs, which exist from SQL Server version to SQL Server version and are upgraded via scripts, and where the data reading is done through Stored Procedures.
Most of the time this behaviour is solveable with a SET ARITHABORT ON as first line of your SQL code.
You can put this into you SPs directly, or set it in your application through the Connection as default.
Good luck and happy coding
Related
I was trying to fix SQL injection for my Application. SQL query I am using to get the details is
"SELECT accountNumber, balance FROM accounts WHERE reg_id = "
+ request.getParameter("reg_id");
I know this query is vulnerable and that's why I was trying to use bind parameters to fix this but the issue is reg_id is Alphanumeric data along with "-" (hyphen as special symbol e.g.- 1abh34-dfg465-aw234)and I can't use statement.setInt() and statement.setString() method to validate reg_id.
My Question is, is there any method I can use to validate Alphanumeric data?
My java code is
String accountBalanceQuery =
"SELECT accountNumber, balance FROM accounts WHERE reg_id = "
+ request.getParameter("reg_id");
try {
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(accountBalanceQuery);
while (rs.next()) {
page.addTableRow(rs.getInt("accountNumber"), rs.getFloat("balance"));
}
} catch (SQLException e) { ... }
Or is there any other way I can fix this vulnerability without bind parameters. Any lead on this will be much appreciated.
The code I'm using deletes the last row in the database. From what I have read, once I've used ORDER BY it will set the result back to read only which makes deleterow() or anything else involving updating the database not possible.
Is there a work around so I could make the table ORDER BY Score DESC and still delete the row I need to delete?
String host = "jdbc:derby://localhost:1527/Scores";
String uName = "root";
String uPass= "root";
Connection con = DriverManager.getConnection( host, uName, uPass );
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
String SQLd = "select * from ROOT.HISCORES ORDER BY Score DESC";
ResultSet dl = stmt.executeQuery( SQLd );
dl.last();
dl.deleteRow();
}
This does not look like a good practice to me.
Instead of SELECTing data into Java and deleting it from there, just do it at the database level. Do a delete-select and skip most of the Java work.
Something like:
DELETE FROM root.hiscores
WHERE Score = (SELECT MIN(hs.Score) FROM root.hiscores hs)
I need to populate dropdown menues with a table that contains 4 million rows.
How can I do this without having it time out on the select statement?
Is need SQL Injections. ? or anything else ?
Now I tried only get top 100 rows . But my project have lot of users and lot of details in database . So i need to show all values in dropdownlist , my current code is here :
protected void SearchButton_Click(object sender, EventArgs e)
{
var search = YourSeachTextBox.Text.Trim();
if(!String.IsNullOrEmpty(search) && search.Length > 3)
{
using(SqlConnection sqlConnection = new SqlConnection("Your Connection String"))
{
var query = "SELECT TOP 100 * FROM [YourTable] WHERE UserName LIKE #Search";
SqlCommand sqlCommand = new SqlCommand(query,sqlConnection);
sqlCommand.Parameters.AddWithValue("#Search", search + "%");
}
}
}
i assume that you intend to populate drop downs with parts aof 4 million rows?
Then you do have to create indexes on those columns that help to separate them!
If you really intend to populate them with most of the contents at once, things will have to timeout for sure as your clients browsers won't get to handle this!
I'm a little in need of your help
In my web application I have this Select statement, but once I run it, it retrieves 0 data but when I try my Select statement in the database it has data in it, and my Select statement is correct, by the way my application is already published in the server.
Here's my code
string SelectStatement = "SELECT DATEDIFF(day, kg1653, GETDATE()) datenum, kg1635, (CASE WHEN kg1637 is null THEN 0 END) eis ";
string FromStatement = "FROM hsi.keygroupdata503 ";
string WhereStatement = "WHERE kg1235='" + _securityCode + "' and kg1241 is null";
_sqlDT = ConnectToDatabase(SelectStatement + FromStatement + WhereStatement);
and here's my connection string
System.Data.Odbc.OdbcConnection _odbcConn = new System.Data.Odbc.OdbcConnection();
_odbcConn.ConnectionString = "MY DATABASE CONNECTION STRING";
System.Data.Odbc.OdbcDataAdapter _odbcA = new System.Data.Odbc.OdbcDataAdapter(sqlQuery1, _odbcConn);
DataTable _odbcDt = new DataTable();
_odbcA.Fill(_odbcDt);
return _odbcDt;
Can somebody please help me with this?
Thank you so much!
When does sqlQuery1 get set to _sqlDT ... your best bet is to debug and see what the query is right on the line of it being called and copy it to run on the SQL server in case something else is updating it or _scurityCode is empty. Also if you have a test environment with similar table names, make sure you are connecting to the same live instance.
Side note, not foolproof but make sure _securityCode has a replace statement and change all single quotes to double quotes to work against SQL injection as the commentor above said.
I am encountering a strange problem when attempting to execute a DELETE query agains a SQL Server table using VB.NET, SQL Command, and Parameters.
I have the following code:
Try
sqlCommand.Transaction = transaction1
sqlCommand.Connection = conn
sqlCommand.CommandText = sqlQuery
sqlCommand.Parameters.Add("#userID", SqlDbType.Int).Value = Convert.ToInt32(userID)
sqlCommand.Parameters.Add("#groupID", SqlDbType.Int).Value = Convert.ToInt32(groupID)
''#Delete the user from the group.
MessageBox.Show("User: " + Convert.ToString(userID) + " Group: " + Convert.ToString(groupID))
MessageBox.Show("Param, UserID: " + sqlCommand.Parameters.Item(0).Value.ToString)
MessageBox.Show("Param, GroupID: " + sqlCommand.Parameters.Item(1).Value.ToString)
return_deleteUser = sqlCommand.ExecuteNonQuery()
Catch ex As Exception
transaction1.Rollback()
Dim hr As Integer = Marshal.GetHRForException(ex)
MsgBox("Removal of user from group has failed: " + ex.Message() & hr)
End Try
Which executes the following SQL Query:
Dim sqlQuery As String = "DELETE FROM MHGROUP.GROUPMEMS WHERE USERNUM =#userID AND GROUPNUM =#groupID"
My problem is that when the code executes, there is no error reported at all. I have ran SQL Profiler and the query doesn't appear in the trace list. The three messageboxes that I have added all return the correct values, and if I was to execute the SQL query against the table with the values the query succeeds. Both the userID and groupID are 3-digit integers.
Can anyone suggest why the code is not working as intended, or any further debugging that I can use to step through the code? Ideally I would love to see the completed SQL query with the parameters completed, but I haven't found out how to do this.
EDIT:
I have the following later in the code to check if the execute's all processed successfully:
If return_insertEvent > 0 And return_updateUser > 0 And return_nextSID > 0 And return_deleteUser > 0 Then
MessageBox.Show("Success")
return_removeADGroup = RemoveUserFromGroup(userID, groupName)
MessageBox.Show("Remove FS User from AD Group: " + return_removeADGroup)
transaction1.Commit()
transaction2.Commit()
transaction3.Commit()
transaction4.Commit()
returnResult = 1
Else
transaction1.Rollback()
transaction2.Rollback()
transaction3.Rollback()
transaction4.Rollback()
returnResult = 0
End If
If you require any further information please don't hesitate in contacting me.
You are missing a Transaction.Commit
Update in respone to additional info added to question:
Why do you have 4 transactions? Since their commit and rollbacks are all executed together, you only need one transaction. I suggest you use a TransactionScope
You can assign the current transaction to ADO.NET Command objects:
ADO.NET and System.Transactions
Transaction Processing in ADO.NET 2.0
I might guess that your calling proc has the values of userid and groupid backwards. If the DELETE doesn't find a matching record, it will complete successfully, but not do anything. I suggest wrapping your delete up in a stored procedure. Then you can add code to test if the parameter values are getting through correctly.
Create Procedure UserDelete
#userid int, #groupID int
As
BEGIN
Select #userid as UID, #groupID as GID INTO TESTTABLE;
DELETE FROM MHGROUP.GROUPMEMS WHERE USERNUM =#userID AND GROUPNUM =#groupID;
END
Run your code then go check the contents of TESTTABLE.
FWIW: I don't like trying to get the whole parameter declaration in one line. Too much going on for me. I like this...
Dim pUID as New Parameter("#userid", SqlDbType.Int)
pUID.Value = userid
cmd.Parameters.Add(pUID)
After some time debugging and sql tracing, I have found out that the stupid application that the DB belongs to treats the group members differently, the groups reside in a administration database, but the users membership to the group resides in another database.
Thank you to everyone above who provided there time and thoughts in assisting with the code. I have changed the code as recomended to use only two transactions and two connections (1 for the admin and sub-database). The code is much nicer now and is that bit easier to read.
Thanks again,
Matt