Multi-threading with SQL - what table hints to use for Select? - sql-server

I have a C# app that is multi-threaded. The application's purpose is to find an available proxy server stored in a SQL server table to connect to.
The table contains 200 proxies. The maximum concurrent proxy connections allowed is 5.
The table has the following columns:
(ProxyId int, IpAddress varchar, IsSocks bit, ConnectStatus tinyint, LastConnected datetime)
ProxyId is a identity column, ConnectStatus has 3 values
0: available (updated by C# app)
1: being connected to (updated inside the sp below)
2: connected (updated by C# app)
I have the following stored procedure defined which will be called from the C# app:
Create Proc GetNextAvailableProxy
AS
Declare #proxyId int, #ipAddress varchar(20), #isSocks bit
if (Select Count(*) From Proxies Where ConnectStatus > 0) < 5 Begin
Select Top 1 #proxyId = Id, #ipAddress= IpAddress, #isSocks = IsSocks
From Proxies with (updlock, readpast)
Where ConnectStatus = 0
Order By IsNull(LastConnected, '1970-01-01')
Update Proxies Set ConnectStatus = 1
Where Id = #proxyId
Select #proxyId ProxyId, #ipAddress IpAddress, #isSocks IsSocks;
return;
End
Select Null ProxyId, Null IpAddress, Null IsSocks
GO
The above sp gets the next available proxy, and if there are 5 proxies being connected to or connected, the C# app receives Null for proxyId and wait for 2 seconds before attempting again.
The C# app will set the ConnectStatus = 2 when the proxy connection is established successfully, and ConnectStatus = 0 once the work is done.
My question: is my sp written correctly with the right choice of table hint? I don't know what table hint I should place on the Select Count(*) statement.

Related

Access Listbox query preventing SQL Server Update Query

I'm developing an Access application and a SQL Server backend simultaneously. I have a Form with a listbox which, when a record is double clicked, opens an unbound form and loads data into it based on the record selected. When changes are made in this second form, a button initiates a pass through query that executes a stored procedure updating the details of the record in the base table in SQL Server.
Here's the thing. As long as Form1 (with the listbox) is open, the stored procedure times out without running. If I close that form, it takes less than a second. It behaves this way when run from Access, when run from management studio, and when run in management studio as a query with hard values (not a sproc with parameters).
The row source for the listbox is a linked table that references a View in SQL Server. The query within the view is a recursive common table expression of two different tables, one of which is the table being edited by the sproc. I've set the view to read only. Is there another setting that I can do to help here?
Here's the stored procedure:
PROCEDURE [dbo].[spSalesPlanUpdate]
#Salesyear numeric(4,0),
#ItemNumber varchar(20),
#Baseline int,
#Speculation int,
#Comments varchar(max)
AS
declare #SY numeric(4,0),
#ItN varchar (20),
#BL int,
#SPL int,
#CmT varchar(max)
set #SY = #Salesyear
set #ItN = #ItemNumber
set #BL = #Baseline
set #SPL = #Speculation
set #CmT = #Comments
BEGIN
SET NOCOUNT ON;
update SalesPlan
set Baseline = #BL
,Speculation = #SPL
,DateModified = getdate()
,Comments = #CmT
where SalesYear = #SY and ItemNumber = #ItN
END
I used both parameters and local variables because at first I was thinking it might be about parameter sniffing.
Here's the view the listbox is queried from:
view [dbo].[vwSalesPlan] as
with cte
as
(
select Item, year(getdate()) as SY
from vwItemsAndLiners il
union all
select ial.Item,
(cte.SY + 1)
From vwItemsAndLiners ial join cte on ial.Item = cte.Item
Where SY < (year(getdate())+ial.YearsFromProp)
)
select sp.ItemNumber, ial.Variety, ial.Size, ial.PerTray, sp.SalesYear, sp.SalesYear - ial.YearsFromProp as PropYear,
sp.SalesYear - ial.YearsFromProduction as ProductionYear,
sp.Baseline, sp.Speculation,
CEILING((CAST(SP.BASELINE AS NUMERIC (12,2)) + CAST(SP.SPECULATION AS numeric(12,2)))/IAL.PerTray)*IAL.PerTray as Total ,
sp.DateModified, ial.Segment ,'Entered' as [Status], sp.Comments
From SalesPlan sp inner join vwItemsAndLiners ial on sp.ItemNumber = ial.Item
Where ial.status = 'Sell'
union
select cte.Item, ial.Variety, ial.Size, ial.PerTray, SY, cte.sy - ial.YearsFromProp as PropYear,
cte.SY - ial.YearsFromProduction as ProductionYear,'', '', 0, null, ial.Segment , 'Not Entered', null
from cte inner join vwItemsAndLiners ial on cte.Item = ial.Item
where cte.Item not in (select ItemNumber from SalesPlan where salesplan.SalesYear = CTE.SY) and ial.Status = 'Sell'
with check option
Table being updated: SalesPlan
View that the listbox is queried from: vwSalesPlan
I realize that there's a lot of stuff here. Really, I'm just hoping this generates some ideas of why a form being open would lock the original table from an update query. Thanks!
I tried:
Indexing the views in SQL Server that provide the rowsource for the listbox, but because they contain CTE's they cannot be indexed.
lstbox.recordset.movefirst then lstbox.recordset.movelast to force access to read the entire list, but whenever the list was filtered or requeried it would throw an error saying the recordset object had changed and the movefirst command was invalid.
So I wrote this sub:
Private Sub readtheData()
Dim i As Integer
i = Me.lstSalesPlan.ListCount
End Sub
Simply forcing it to count the records every time the form was loaded or the query behind the listbox was filtered forced access to release the lock. Hope this helps somebody down the road!

How to determine who performed DROP/DELETE on Sql Server database objects?

There is always a need to find out details, either intentionally Or mistakenly someone executed DROP/DELETE command on any of following SQL Server database objects.
DROPPED - Table from your database
DROPPED - Stored Procedure from your database
DELETED - Rows from your database table
Q. Is there TSQL available to find db user who performed DELETE/DROP?
Q. What kind of permissions are needed for user to find out these details?
Did you check this ?
Right click on database.
Go to as shown in image :
Solution 2 :
This query gives alot of useful information for a database(apply filter as required) :
DECLARE #filename VARCHAR(255)
SELECT #FileName = SUBSTRING(path, 0, LEN(path)-CHARINDEX('\', REVERSE(path))+1) + '\Log.trc'
FROM sys.traces
WHERE is_default = 1;
SELECT gt.HostName,
gt.ApplicationName,
gt.NTUserName,
gt.NTDomainName,
gt.LoginName,
--gt.SPID,
-- gt.EventClass,
te.Name AS EventName,
--gt.EventSubClass,
-- gt.TEXTData,
gt.StartTime,
gt.EndTime,
gt.ObjectName,
gt.DatabaseName,
gt.FileName,
gt.IsSystem
FROM [fn_trace_gettable](#filename, DEFAULT) gt
JOIN sys.trace_events te ON gt.EventClass = te.trace_event_id
WHERE EventClass in (164) --AND gt.EventSubClass = 2
ORDER BY StartTime DESC;

An unexpected method call was made. Ensure that the XML is well formed. The stack trace of the method call was : Void WriteFullEndElement()

Using BizTalk I am trying to insert/update the table in the SQL Server database using the stored procedure. I have created a stored procedure and the Table Type like below
CREATE TYPE dbo.dept_TT AS TABLE
(
dept_name varchar(64),
jax_dept_id char(32)
)
GO
CREATE PROCEDURE [dbo].[uspInsertorUpdateDept]
#dept_TT dept_TT READONLY
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION;
UPDATE dep
SET dep.dept_name = dtt.dept_name,
dep.jax_dept_id = dtt.jax_dept_id
FROM [afm].[jax_dept] dep
INNER JOIN #dept_TT dtt ON dep.jax_dept_id = dtt.jax_dept_id
INSERT INTO [afm].[jax_dept](dept_name, jax_dept_id )
SELECT dtt.dept_name, dtt.jax_dept_id
FROM #dept_TT dtt
WHERE NOT EXISTS (SELECT 1
FROM [afm].[jax_dept]
WHERE jax_dept_id = dtt.jax_dept_id)
COMMIT TRANSACTION;
END;
When I execute the stored produre in the SQL Server management studio it insert/updates the records as expected. I am consuming this storedprocedure in the biztalk application and tried to run the application it throws error like
The adapter failed to transmit message going to send port "WcfSendPort_SqlAdapterBinding_Procedures_dbo_Custom_Dep" with URL "mssql://". It will be retransmitted after the retry interval specified for this Send Port. Details:"Microsoft.ServiceModel.Channels.Common.XmlReaderParsingException: An unexpected method call was made. Ensure that the XML is well formed. The stack trace of the method call was : Void WriteFullEndElement().
I enabled the tracking and tried seeing the XML that is sent to the send port and it looks good like below.
<?xml version="1.0" encoding="utf-8"?>
<ns0:uspInsertorUpdateDept xmlns:ns0="http://schemas.microsoft.com/Sql/2008/05/Procedures/dbo" xmlns:ns4="http://schemas.datacontract.org/2004/07/System.Data" xmlns:ns3="http://schemas.microsoft.com/Sql/2008/05/Types/TableTypes/dbo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:dept_TT>
<ns3:dept_TT>
<ns3:dept_name>lab1</ns3:dept_name>
<ns3:jax_dept_id>RRI</ns3:jax_dept_id>
</ns3:dept_TT>
<ns3:dept_TT>
<ns3:dept_name>lab2</ns3:dept_name>
<ns3:jax_dept_id>RAFAC</ns3:jax_dept_id>
</ns3:dept_TT>
</ns0:dept_TT>
</ns0:uspInsertorUpdateDept>
Xml generated for the stored procedure in the VS
<ns0:uspInsertorUpdateDept xmlns:ns0="http://schemas.microsoft.com/Sql/2008/05/Procedures/dbo">
<ns0:dept_TT>
<ns1:dept_TT xmlns:ns1="http://schemas.microsoft.com/Sql/2008/05/Types/TableTypes/dbo">
<ns1:dept_name>dept_namedept_namedept_namedept_namedept_namedept_namedept_named</ns1:dept_name>
<ns1:jax_dept_id>jax_dept_idjax_dept_idjax_dept_i</ns1:jax_dept_id>
</ns1:dept_TT>
<ns1:dept_TT xmlns:ns1="http://schemas.microsoft.com/Sql/2008/05/Types/TableTypes/dbo">\
<ns1:dept_name>dept_namedept_namedept_namedept_namedept_namedept_namedept_named</ns1:dept_name>
<ns1:jax_dept_id>jax_dept_idjax_dept_idjax_dept_i</ns1:jax_dept_id>
</ns1:dept_TT>
<ns1:dept_TT xmlns:ns1="http://schemas.microsoft.com/Sql/2008/05/Types/TableTypes/dbo">
<ns1:dept_name>dept_namedept_namedept_namedept_namedept_namedept_namedept_named</ns1:dept_name>
<ns1:jax_dept_id>jax_dept_idjax_dept_idjax_dept_i</ns1:jax_dept_id>
</ns1:dept_TT>
</ns0:dept_TT>
</ns0:uspInsertorUpdateDept>
Not sure what am I missing here. Any help is greatly appreciated

Query involving 4 tables

I'm stuck on an SQL query so I thought maybe an SQL MVP/GOD could find this here with small luck.
I'm using SQL Server 2008 and here's a description of my tables:
Tables - columns
NodesCustomProperties - NodeID / NodeZone
Application - NodeID / ID
Component - ApplicationID / Name
CurrentComponentStatus: ApplicationID / Data
I'd like to fetch the SUM of CurrentComponentStatus.Data when Component.Name is like 'HTTP%: Bytes Transferred Between Proxy and Servers' for the same ApplicatioID and filter these results when NodesCustomProperties.NodeZone = 'one particular zone'
Research and testing have lead me here so far:
SELECT
SUM(
CASE
WHEN [dbo].[APM_CurrentComponentStatus].StatisticData IS NOT NULL
THEN [dbo].[APM_CurrentComponentStatus].StatisticData
ELSE 0
END) AS 'Data'
FROM
[dbo].[APM_CurrentComponentStatus]
LEFT JOIN [dbo].[APM_Application]
ON [dbo].[APM_Application].ID = [dbo].[APM_CurrentComponentStatus].ApplicationID
WHERE
[dbo].[APM_Application].NodeID IN (
SELECT [dbo].[NodesCustomProperties].NodeID
FROM [dbo].[NodesCustomProperties]
WHERE [dbo].[NodesCustomProperties].NodeZone = 'one particular zone')
GROUP BY [dbo].[APM_CurrentComponentStatus].ApplicationID
HAVING [dbo].[APM_CurrentComponentStatus].ApplicationID
IN (
SELECT [dbo].[APM_Component].ApplicationID
FROM [dbo].[APM_Component]
WHERE [dbo].[APM_Component].Name LIKE 'HTTP%: Bytes Transferred Between Proxy and Servers')
This query actually works (Hooray !) but there's too few results so that's still not it. (Awwww !)

Service Broker messages start to get hung up after about a day

I have an application that is using the Service Broker is SQL 2008. About once a day the database's performance starts take a noticeable hit and I have determined that this is because of the Service Broker. If I hard reset all broker connections using the following commands:
ALTER DATABASE [RegencyEnterprise] SET OFFLINE WITH ROLLBACK IMMEDIATE
ALTER DATABASE [RegencyEnterprise] SET ONLINE
Then the performance returns to normal until about the next day. I have also noticed that when performance is poor, running the following query returns a large number (around 1000 currently) of conversations that are stuck in the STARTED_OUTBOUND state:
SELECT * FROM sys.conversation_endpoints
Also, the following queries don't return any entries in them:
SELECT * FROM sys.dm_qn_subscriptions
SELECT * FROM sys.transmission_queue
Performance seems to be alright where there are plenty of items returned by this query. The only time when there are problems are when there are connections that are STARTED_OUTBOUND that stay stuck in this state.
The only configuration I have done to the Service Broker on my SQL Server 2008 instance was to run the following command:
ALTER DATABASE RegencyEnterprise SET ENABLE_BROKER
Digging through the SQL error log, I have found this entry over 1000 times as well:
07/11/2013 01:00:02,spid27s,Unknown,The query notification dialog on conversation handle '{6DFE46F5-25E9-E211-8DC8-00221994D6E9}.' closed due to the following error: '<?xml version="1.0"?><Error xmlns="http://schemas.microsoft.com/SQL/ServiceBroker/Error"><Code>-8490</Code><Description>Cannot find the remote service &apos;SqlQueryNotificationService-cb4e7a77-58f3-4f93-95c1-261954d3385a&apos; because it does not exist.</Description></Error>'.
I also see this error a dozen or so times throughout the log, though I believe I can fix this just by creating a master key in the database:
06/26/2013 14:25:01,spid116,Unknown,Service Broker needs to access the master key in the database '<Database name>'. Error code:26. The master key has to exist and the service master key encryption is required.
I am thinking the number of these errors may be related to the number of conversations that are staying stuck in the queue. Here is the C# code I am using to subscribe to the query notifications:
private void EstablishSqlConnection(
String storedProcedureName,
IEnumerable<SqlParameter> parameters,
Action sqlQueryOperation,
String serviceCallName,
Int32 timeout,
params MultipleResult[] results)
{
SqlConnection storeConnection = (SqlConnection) ((EntityConnection) ObjectContext.Connection).StoreConnection;
try
{
using (SqlCommand command = storeConnection.CreateCommand())
{
command.Connection = storeConnection;
storeConnection.Open();
SqlParameter[] sqlParameters = parameters.ToArray();
command.CommandText = storedProcedureName;
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddRange(sqlParameters);
if (sqlQueryOperation != null)
{
// Register a sql dependency with the SQL query.
SqlDependency sqlDependency = new SqlDependency(command, null, timeout);
sqlDependency.OnChange += OnSqlDependencyNotification;
}
using (DbDataReader reader = command.ExecuteReader())
{
results.ForEach(result => result.MapResults(this, reader));
}
}
}
finally
{
storeConnection.Close();
}
}
Here is how I handle the notification:
public static void OnSqlDependencyNotification(object sender, SqlNotificationEventArgs e)
{
if (e.Info == SqlNotificationInfo.Invalid)
{
// If we failed to register the SqlDependency, log an error
<Error is loged here...>
// If we get here, we are not in a valid state to requeue the sqldependency. However,
// we are on an async thread and should NOT throw an exception. Instead we just return
// here, as we have already logged the error to the database.
return;
}
// If we are able to find and remove the listener, invoke the query operation to re-run the query.
<Handle notification here...>
}
Does anyone know what can cause the broker's connections to get in this state? Or what tools I could use to go about trying to figure out what is causing this? I currently only have a single web server that is registering to its notifications, so my scenario is not overly complex.
UPDATE:
Ok, so I have determined from this post that the error "Cannot find the remote service ... because it does not exist" is due to SqlDependency not cleaning up after itself properly. The broker is still trying to send notifications to my application after the service has ended. So now, it sounds like I just have to find a way to clear out whatever it is not properly cleaning up when my app starts before calling SqlDependency.Start(), but I have not found a way to do this other than my original method above, which takes the database offline and is not acceptable. Does anyone know know to clean this up?
I have found an acceptable approach to solving this issue. First, I migrated my code away from SqlDependency and I am now using SqlNotificationRequest instead. Doing this prevents Broker Queues and Services from being created/destroyed at unexpected times.
Even with this however, when my application exits there are still a few conversations that don't get marked as closed because the original endpoint that setup the notification is no longer there. Therefore, each time my server re-initializes my code I am clearing out existing conversations.
This adjustment has reduced the number of connections that I have on a daily bases from over 1000 and having to manually kill them, to having a max of about 20 at all times. I highly recommend using SqlNotificationRequest instead of SqlDependency.
I have found a way to clear out the conversations that are stuck. I retrieve all of the generated SqlDependency queues that still exist and iterate over the conversations that don't belong to any of these and end those conversations. Below is the code:
SET NOCOUNT OFF;
DECLARE #handle UniqueIdentifier
DECLARE #count INT = 0
-- Retrieve orphaned conversation handles that belong to auto-generated SqlDependency queues and iterate over each of them
DECLARE handleCursor CURSOR
FOR
SELECT [conversation_handle]
FROM sys.conversation_endpoints WITH(NOLOCK)
WHERE
far_service COLLATE SQL_Latin1_General_CP1_CI_AS like 'SqlQueryNotificationService-%' COLLATE SQL_Latin1_General_CP1_CI_AS AND
far_service COLLATE SQL_Latin1_General_CP1_CI_AS NOT IN (SELECT name COLLATE SQL_Latin1_General_CP1_CI_AS FROM sys.service_queues)
DECLARE #Rows INT
SELECT #Rows = COUNT(*) FROM sys.conversation_endpoints WITH(NOLOCK)
WHERE
far_service COLLATE SQL_Latin1_General_CP1_CI_AS like 'SqlQueryNotificationService-%' COLLATE SQL_Latin1_General_CP1_CI_AS AND
far_service COLLATE SQL_Latin1_General_CP1_CI_AS NOT IN (SELECT name COLLATE SQL_Latin1_General_CP1_CI_AS FROM sys.service_queues)
WHILE #ROWS>0
BEGIN
OPEN handleCursor
FETCH NEXT FROM handleCursor
INTO #handle
BEGIN TRANSACTION
WHILE ##FETCH_STATUS = 0
BEGIN
-- End the conversation and clean up any remaining references to it
END CONVERSATION #handle WITH CLEANUP
-- Move to the next item
FETCH NEXT FROM handleCursor INTO #handle
SET #count= #count+1
END
COMMIT TRANSACTION
print #count
CLOSE handleCursor;
IF #count > 100000
BEGIN
BREAK;
END
SELECT #Rows = COUNT(*) FROM sys.conversation_endpoints WITH(NOLOCK)
WHERE
far_service COLLATE SQL_Latin1_General_CP1_CI_AS like 'SqlQueryNotificationService-%' COLLATE SQL_Latin1_General_CP1_CI_AS AND
far_service COLLATE SQL_Latin1_General_CP1_CI_AS NOT IN (SELECT name COLLATE SQL_Latin1_General_CP1_CI_AS FROM sys.service_queues)
END
DEALLOCATE handleCursor;
Started Outbound means 'SQL Server processed a BEGIN CONVERSATION for this conversation, but no messages have yet been sent.' (from Books Online)
It looks like you are creating conversations that are not then being used, so they never get closed.
Not entirely sure why that would be causing a degradation in performance though.

Resources