Table valued parameter has wrong data - c#
I have ASP .NET Web Form application with SignalR handling online notifications. Somewhere down the road of simplifying the code, I ran into an issue when stored procedure accepting whole table of connected clients as one of the input parameters for some reason inserts into result table less rows than when I just put a breakline into c# code before the query runs and launch it SQL-side myself.
It's really complicated code now spanning over multiple c# methods and sql queries, so I just want to know, why could this be an issue. I'm providing with small piece of code, that will hopefuly give you guys some ideas, cause I fresh ran out of mine, I feel like I tried everything by now to no avail.
Here is the SQL function dbo.UpdateAffectedClients I'm using in procedure
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
RETURNS
#Output TABLE
(
connectionId varchar(512),
clientId int,
numberOfNotifications int,
isPatient bit
)
AS
BEGIN
--ADDING AFFECTED CLIENTS TO OUTPUT
--med/Instrument/Order
IF(#typeId = 1)
BEGIN
--Adding doctors and patients
INSERT INTO #Output (connectionId, clientId, numberOfNotifications, isPatient)
SELECT cc.connectionId, cc.clientId, COALESCE(COUNT(n.id), 0), cc.isPatient
FROM MedOrders mo
LEFT JOIN User2ExamRoom uer on mo.examRoomId = uer.examRoomId
JOIN #ConnectedClients cc on (uer.userId = cc.clientId AND cc.isPatient = 0) OR (mo.patientId = cc.clientId AND cc.isPatient = 1)
LEFT JOIN Notifications n on n.clientId = cc.clientId AND n.isPatient = cc.isPatient
WHERE mo.id = #tableId
GROUP BY cc.isPatient, cc.clientId, cc.connectionId
END
RETURN
END
and here is the procedure
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[HandleNotificationsDBUpdate]
#ConnectedClients as SignalRConnectedClients READONLY,
#tableId int,
#typeId int
AS
BEGIN
SET NOCOUNT ON;
--QUERY
DECLARE
#affectedClients SignalRConnectedClients,
#orderId int = -1,
#meetingId int = -1,
#messageId int = -1,
#processed bit,
#cancelledByPatient bit,
#examRoomId int,
#affectedPatientId int,
#errorMessage varchar(MAX) = '',
#written bit,
#tracing varchar(2048) = ''
--Whatever happened, the row got deleted from the table in the process and info is no longer available.
IF NOT EXISTS(SELECT TOP 1* FROM MedOrders WHERE id = #tableId)
BEGIN TRY
BEGIN
SET #tracing = #tracing + 'Row no longer exists in the original table. '
--Insert to affected clients - prior to deleting from notifications, I'd lose the reference afterwards
INSERT INTO #affectedClients (connectionId, clientId, numberOfNotifications, isPatient)
SELECT cc.connectionId, cc.clientId, COUNT(n.id), cc.isPatient
FROM #ConnectedClients cc
JOIN Notifications n on cc.clientId = n.clientId AND cc.isPatient = n.isPatient
WHERE n.notificationTypeId = #typeId AND n.tableId = #tableId
GROUP BY cc.isPatient, cc.clientId, cc.connectionId
--Then delete notifications linked to it
DELETE FROM Notifications WHERE tableId = #tableId AND notificationTypeId = #typeId
END
END TRY
BEGIN CATCH
SET #errorMessage = #errorMessage + 'Error in HandleNotificationsDBUpdate procedure, "Whatever happened, the row got deleted from the table in the process and info is no longer available" section, see line ' + CAST(ERROR_LINE() as varchar(4)) + '. Error message: ' + ERROR_MESSAGE() + CHAR(13) + CHAR(10)
END CATCH
--row is still there
ELSE
BEGIN
SET #tracing = #tracing + 'Row still exists in the original table. '
--MED ORDER
IF (#typeId = 1)
BEGIN
SET #tracing = #tracing + 'Its a medOrder, '
SET #orderId = #tableId
SET #examRoomId = (SELECT examRoomId FROM MedOrders WHERE id = #orderId)
SET #processed = (SELECT CASE WHEN processed IS NULL THEN 0 ELSE 1 END FROM MedOrders WHERE id = #orderId)
SET #cancelledByPatient = (SELECT CASE WHEN cancelledByPatient IS NULL THEN 0 ELSE 1 END FROM MedOrders WHERE id = #orderId)
--not processed
IF (#processed = 0)
--PATIENT POSTED OR UPDATED ORDER
BEGIN TRY
BEGIN
SET #tracing = #tracing + 'not processed. '
--it's not already there (meaning patient didn't just update something)
IF NOT EXISTS (SELECT TOP 1 id FROM Notifications WHERE notificationTypeId = #typeId AND tableId = #tableId)
BEGIN
SET #tracing = #tracing + 'Patient is creating an order. '
--Insert row to notifications for each doctor (user) having this examRoom assigned
INSERT INTO Notifications (tableId, notificationTypeId, clientId, isPatient)
SELECT #tableId, #typeId, uer.userId, 0
FROM User2ExamRoom uer
WHERE uer.examRoomId = #examRoomId
--FUNCTION - insert into affectedClients table affected clientIds and number of their notifications
INSERT INTO #affectedClients (connectionId, clientId, numberOfNotifications, isPatient)
SELECT connectionId, clientId, numberOfNotifications, isPatient FROM dbo.UpdateAffectedClients(#ConnectedClients, #typeId, #tableId)
END
END
END TRY
BEGIN CATCH
SET #errorMessage = #errorMessage + 'Error in HandleNotificationsDBUpdate procedure, MedOrder, PATIENT POSTED OR UPDATED ORDER section, see line ' + CAST(ERROR_LINE() as varchar(4)) + '. Error message: ' + ERROR_MESSAGE() + CHAR(13) + CHAR(10)
END CATCH
--processed
ELSE
BEGIN
SET #tracing = #tracing + 'processed. '
--deleting from notifications cause its been processed
DELETE FROM Notifications
WHERE tableId = #tableId AND notificationTypeId = #typeId
--cancelled by patient
IF (#cancelledByPatient = 1)
--PATIENT REMOVED IT BEFORE APPROVAL
BEGIN TRY
BEGIN
SET #tracing = #tracing + 'Patient cancelled the order. '
--FUNCTION - insert into affectedCLients table affected clientIds and number of their notifications
INSERT INTO #affectedClients (connectionId, clientId, numberOfNotifications, isPatient)
SELECT connectionId, clientId, numberOfNotifications, isPatient FROM dbo.UpdateAffectedClients(#ConnectedClients, #typeId, #tableId)
END
END TRY
BEGIN CATCH
SET #errorMessage = #errorMessage + 'Error in HandleNotificationsDBUpdate procedure, MedOrder, PATIENT REMOVED IT BEFORE APPROVAL section, see line ' + CAST(ERROR_LINE() as varchar(4)) + '. Error message: ' + ERROR_MESSAGE() + CHAR(13) + CHAR(10)
END CATCH
--not cancelled by patient
ELSE
BEGIN
--DOCTOR PROCESSED ORDER
BEGIN TRY
BEGIN
SET #tracing = #tracing + 'Doctor processed the order. '
SET #affectedPatientId = (SELECT patientId FROM MedOrders WHERE id = #orderId)
--Insert row to notifications table for the patient that placed this order
INSERT INTO Notifications (tableId, notificationTypeId, clientId, isPatient)
VALUES (#tableId, #typeId, #affectedPatientId, 1)
--FUNCTION - Add affected clientIds and number of their notifications
INSERT INTO #affectedClients (connectionId, clientId, numberOfNotifications, isPatient)
SELECT connectionId, clientId, numberOfNotifications, isPatient FROM dbo.UpdateAffectedClients(#ConnectedClients, #typeId, #tableId)
END
END TRY
BEGIN CATCH
SET #errorMessage = #errorMessage + 'Error in HandleNotificationsDBUpdate procedure, MedOrder, DOCTOR PROCESSED ORDER section, see line ' + CAST(ERROR_LINE() as varchar(4)) + '. Error message: ' + ERROR_MESSAGE() + CHAR(13) + CHAR(10)
END CATCH
END
END
END
END
--Final selects
SET #written = CASE WHEN LEN(#errorMessage) = 0 THEN 1 ELSE 0 END
SELECT connectionId, clientId, numberOfNotifications, isPatient FROM #affectedClients
SELECT #written as written, #errorMessage as errorMessage, #tracing as tracing
END
Patient posted order section is just fine, Doctor processed order doesn't return any patients, it only returns doctors in affectedClients result. As if it somehow failed JOIN or something (I used to run it in two insert statements, but managed to turn it into single one to see if that could be the issue).
C# method:
/// <summary>
/// Handles any changes in notifications SQL side provided typeId (see NotificationTypes Table in SQL or comment in header of this function) and id of the updated row in respective table (tableId - could stand for orderId, messageId, taskId, etc.)
/// </summary>
/// <param name="typeId"></param>
/// <param name="tableId"></param>
public static Obj_NotificationDBUpdateResult HandleNotificationsDBUpdate(int typeId, int tableId)
{
//typeIds:
//1: medOrder
//2: instrumentOrder
//3: meeting
//4: message
//5: task
Obj_NotificationDBUpdateResult output = new Obj_NotificationDBUpdateResult();
List<Obj_SignalRClientsListEntry> ConnectedClients = NotificationsHub.ConnectedClients;
//preparing ConnectedClients as parametr to pass into stored procedure so I can easily link connectionId with clientId
DataTable ConnectedClientsDt = new DataTable();
ConnectedClientsDt.Columns.Add("connectionId", typeof(string));
ConnectedClientsDt.Columns.Add("clientId", typeof(Int32));
ConnectedClientsDt.Columns.Add("numberOfNotifications", typeof(Int32));
ConnectedClientsDt.Columns.Add("isPatient", typeof(bool));
foreach(Obj_SignalRClientsListEntry item in ConnectedClients)
{
DataRow dr = ConnectedClientsDt.NewRow();
dr["connectionId"] = item.connectionId;
dr["clientId"] = item.clientId;
dr["numberOfNotifications"] = item.numberOfNotifications;
dr["isPatient"] = item.isPatient;
ConnectedClientsDt.Rows.Add(dr);
}
//Query
SqlConnection conn = new SqlConnection(Data.connStr);
try
{
conn.Open();
using (SqlCommand comm = new SqlCommand("HandleNotificationsDBUpdate", conn))
{
comm.CommandType = CommandType.StoredProcedure;
comm.Parameters.AddWithValue("#ConnectedClients", ConnectedClientsDt).SqlDbType = SqlDbType.Structured;
comm.Parameters.AddWithValue("#typeId", typeId);
comm.Parameters.AddWithValue("#tableId", tableId);
using (SqlDataReader reader = comm.ExecuteReader())
{
while (reader.Read())
{
output.affectedClients.Add(new Obj_SignalRClientsListEntry()
{
connectionId = reader["connectionId"].ToString(),
clientId = Convert.ToInt32(reader["clientId"]),
isPatient = Convert.ToBoolean(reader["isPatient"]),
numberOfNotifications = Convert.ToInt32(reader["numberOfNotifications"]),
});
}
reader.NextResult();
while (reader.Read())
{
output.written = Convert.ToBoolean(reader["written"]);
output.errorMessage = reader["errorMessage"].ToString();
output.tracing = reader["tracing"].ToString();
}
}
}
}
catch (Exception ex)
{
throw (ex);
}
finally
{
conn.Close();
}
//joining the hub, providing context
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<NotificationsHub>();
//looping through each client whom we should notify
foreach (Obj_SignalRClientsListEntry item in output.affectedClients)
{
//calling clients refresh function providng this number as an input
context.Clients.Client(item.connectionId).clientRefreshNotificationsCount(item.numberOfNotifications);
}
return output;
}
When I check (right before hubContext line) the output.affectedClients, it only shows doctors. But when I put a breakline before the SQLConnection line and run the body of the procedure provided same input as came to the c# method (see debug section at the beginning in the procedure), it returns doctors and patients just fine.
I tried switching some inserts in the procedure, putting stuff in the function directly into procedure, omitting function completely, tried to comment everything but the part with inserting into affectedClients, fiddle with ansi_nulls and those up there, tried to put them before query when I run it solo, tried to remove them from the procedure before altering, nothing.
I even put the tracing output to see exactly where it went in the procedure and it all seems legit, I have no idea why would it return just doctors.
Other than that - it's pretty neatly commented, so it should give you some idea of what's going on behind the curtain, but If you need to ask, ask, I'll try my best to answer. Thanks in advance.
UPDATE
So I narrowed the problem down to this
SELECT cc.connectionId, cc.clientId, 100, cc.isPatient
FROM #ConnectedClients cc
JOIN MedOrders mo on (mo.patientId = cc.clientId AND cc.isPatient = 1)
WHERE mo.id = #tableId
that cc.isPatient = 1 just doesn't work. For some unknown reason it doesn't recognize that 1 in #ConnectedClients (bit) as bit value. Any ideas?
Ok, so I figured it out... apparently - order of columns in the SQL User defined Table Type matters. I had last two columns switched in SQL table type and when the DataTable object arrived to the procedure, it disregarded column names from C# and just took those columns from left to right and started to fill the SQL table in the same order, therefore inserting numberOfNotifications to isPatient and vice versa. Now since you cannot have value bigger than 1 in a bit column, it treated it as 0 and the query kept failing JOINs and other stuff...
I would have never guessed this to be an issue. Why do I even bother naming columns in DataTable then?
Related
Alternative of PRINT output from SQL
I am working on an MVC app with a database where I have a lot of empty columns in each table. Now if a user dynamically adds a field on form I have to check that which column is completely empty(available) and then assign it to the user added field. For that matter I searched and found the following SQL code and it works fine. declare #col varchar(255), #cmd varchar(max) DECLARE getinfo cursor for SELECT c.name as colum FROM sys.tables t JOIN sys.columns c ON t.Object_ID = c.Object_ID WHERE t.Name = 'MyTableName' OPEN getinfo FETCH NEXT FROM getinfo into #col WHILE ##FETCH_STATUS = 0 BEGIN SELECT #cmd = 'IF NOT EXISTS (SELECT * FROM MyTableName WHERE [' + #col + '] IS NOT NULL) BEGIN print ''' + #col + ''' end' EXEC(#cmd) FETCH NEXT FROM getinfo into #col END CLOSE getinfo DEALLOCATE getinfo It prints all columns in the given table having all empty rows. Now I want to get that list in my MVC Controller. Since it is printing the results so I cannot use SqlDataReader. So I found a method in answers of this question. It gives me results one by one. public ActionResult getList() { SqlConnection con = DBHelperADO.GetConnection(); SqlCommand cmd = new SqlCommand(query, con); // query = sql code mentioned above con.Open(); using (con) { con.InfoMessage += connection_InfoMessage; SqlDataReader dr = cmd.ExecuteReader(); } } void connection_InfoMessage(object sender, SqlInfoMessageEventArgs e) { var outputFromStoredProcedure = e.Message; } How to populate a List of all column names from the handler? (can't use session or static list) I don't want to use Event Handler in MVC app so is there any better option? (may be any other SQL code to give me the required result or getting the PRINT output by some other method)
UPDATED according to #AwaisMahmood suggestion You can change the print sentence by a temp table and then return it (I don't have checked the correct sintax for the quotes in the set #cmd... line, but you can get the idea) declare #col varchar(255), #cmd varchar(max) DECLARE getinfo cursor for SELECT c.name as colum FROM sys.tables t JOIN sys.columns c ON t.Object_ID = c.Object_ID WHERE t.Name = 'MyTableName' CREATE TABLE #EmptyFields { FieldName nvarchar(60) } OPEN getinfo FETCH NEXT FROM getinfo into #col WHILE ##FETCH_STATUS = 0 BEGIN SELECT #cmd = 'IF NOT EXISTS (SELECT * FROM MyTableName WHERE [' + #col + '] IS NOT NULL) BEGIN insert into #EmptyFields (FieldName) values (''' + #col + '') ' end' EXEC(#cmd) FETCH NEXT FROM getinfo into #col END CLOSE getinfo DEALLOCATE getinfo select * from #EmptyTables Then, in the client, you can use a datareader as usual to fill a List<string> or simmilar.
Why is my output parameter in stored procedure called from Entity Null?
I am calling a stored procedure and declare an output paramters: CREATE PROCEDURE dbo.usp_getIsReadyForProcess #VideoId INT , #val INT OUTPUT AS BEGIN BEGIN TRY BEGIN TRANSACTION -- LOCK ROW UNTIL END OF TRANSACTION SELECT * FROM dbo.ProcessStatus WITH (ROWLOCK, HOLDLOCK) WHERE VideoId = #VideoId And then setting the value throughout the transaction --If there is no row count IF ##ROWCOUNT = 0 BEGIN SET #val = 0 END -- If video is already in process ELSE IF #statusCode > 1 BEGIN SET #val = 0 END ...... more if blocks -- RELEASE LOCK COMMIT TRANSACTION END TRY Here is the c# code for getting the output parameter: using (var db = EntityFactory.GetInstance()) { ObjectParameter objParam = new ObjectParameter("val", typeof(int)); db.usp_getIsReadyForProcess(videoId, objParam); return (int)objParam.Value == 1; } ... But then objParam.Value is null no matter what I do. So I dig a little deeper and uncover an exception that was handled already, Message: Method may only be called on a Type for which Type.IsGenericParameter is true. What am i doing wrong? I tried type(bool, int32, string) .. nothing works
Your stored procedure returns a resultset, due to this line: SELECT * FROM dbo.ProcessStatus WITH (ROWLOCK, HOLDLOCK) WHERE VideoId = #VideoId You can either change the SQL to something like: DECLARE #RowCount INT SELECT #RowCount = COUNT(*) FROM dbo.ProcessStatus WITH (ROWLOCK, HOLDLOCK) WHERE VideoId = #VideoId Or you can capture the resultset in your code: var resultSet = db.usp_getIsReadyForProcess(videoId, objParam);
Getting specific column name of a table
I am working on a project and I got stuck with a query in which I want to get the specific column name of a table whose value is "no." I have a table with name subscription and it has four fields; id(integer), email(varchar), sms(varchar), phone(varchar) and my email, sms and phone field have values either 'yes' or 'no'. Now I want to retrieve the name of only those columns which has a value 'no'. I want to execute this query in mysql. I have tried the following query which is returning me the complete row wherever there is even one occurence of 'no': SELECT * FROM test.subscription WHERE 'no' IN (email,sms,phone); Like i have these two rows now i want to select the column name where value='no' and id=1
How about something cheap and cheerful using a case statement like: SELECT ID, 'ColName' = CASE WHEN email = 'no' THEN 'email' END, 'ColName2' = CASE WHEN sms = 'no' THEN 'sms' END, 'ColName3' = CASE WHEN phone = 'no' THEN 'phone' END FROM subscription where ID = 2
I cant put this as a comment because of less privileges, but if you are looking to search a value which is held by a table (could be any column) - you might check this SO post Using the code from the same post - DECLARE #Results TABLE(ColumnName nvarchar(370), ColumnValue nvarchar(3630)) DECLARE #SearchStr nvarchar(100) DECLARE #TableName nvarchar(256), #ColumnName nvarchar(128), #SearchStr2 nvarchar(110) SET #TableName = 'no' SET #SearchStr2 = QUOTENAME('%' + 'no' + '%','''') SET #ColumnName = '' SET #TableName = '[test].[dbo].[subscription]' WHILE (#TableName IS NOT NULL) AND (#ColumnName IS NOT NULL) BEGIN SET #ColumnName = ( SELECT MIN(QUOTENAME(COLUMN_NAME)) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = PARSENAME(#TableName, 2) AND TABLE_NAME = PARSENAME(#TableName, 1) AND DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar') AND QUOTENAME(COLUMN_NAME) > #ColumnName ) IF #ColumnName IS NOT NULL BEGIN INSERT INTO #Results EXEC ( 'SELECT ''' + #TableName + '.' + #ColumnName + ''', LEFT(' + #ColumnName + ', 3630) FROM ' + #TableName + ' (NOLOCK) ' + ' WHERE ' + #ColumnName + ' LIKE ' + #SearchStr2 ) END END SELECT ColumnName, ColumnValue FROM #Results
If i got the Question right, i think the following Query might work SELECT * FROM test.subscription WHERE 1=1 and (email = 'no' or email = 'no' or sms = 'no' ) and id=(your values)
Retrieve multiple data from stored procedure using Linq-to-SQL
I am using Silverlight and Linq-to-SQL to communicate with the database. I have a stored procedure which receives 2 parameters (PFOID and Quantity) and Userid and returns a product name. If we send multiple values like multiple pfoid's and quantity's it will return multiple product names shown as below The stored procedure looks like this.. ALTER PROCEDURE [PFO].[PFOValidateUpdateData] #PfoIDs xml, -- list of PFO ID's #UserID uniqueidentifier --The Identity of the User making the call. AS BEGIN -- SET DEFAULT BEHAVIOR SET NOCOUNT ON -- Performance: stops rows affected messages SET DEADLOCK_PRIORITY LOW -- This SP to be the Deadlock victim -- Initialise Lock-Timeout and Deadlock vars for Insert DECLARE #iLockTimeoutRetries as int DECLARE #iDeadLockRetries as int DECLARE #dtLockTimeoutSleepInterval as datetime DECLARE #dtDeadlockSleepInterval as datetime DECLARE #iErrorNumber as int SET #iLockTimeoutRetries = 0 SET #iDeadLockRetries = 0 SET #dtLockTimeoutSleepInterval = sCommon.fnLockTimeoutSleepInterval() SET #dtDeadlockSleepInterval= sCommon.fnDeadlockSleepInterval() SET #iErrorNumber = 0 -- procedure specific DECLARE #idoc as int DECLARE #IsBrightstarUser as bit RETRY: BEGIN TRY --Create Temp table to store stores! CREATE TABLE [#PFOList] ( [PFOId] nvarchar(50), [Quantity] INT ) --Create Temp table to store User stores! CREATE TABLE [#UserStoreList] ( [StoreID_XRef] nvarchar(50) ) print CONVERT(varchar(1000), #PfoIDs) --Create Document EXEC sp_xml_preparedocument #idoc OUTPUT, #PfoIDs -- Append to new list of Store records INSERT INTO [#PFOList] ( [PFOId], [Quantity] ) SELECT [PFOID],[Quantity] FROM OPENXML (#idoc, 'ArrayOfString/string',2) WITH( [PFOID] nvarchar(50),[Quantity] [INT]) Stores --WHERE [PFOId] Is Not NULL -- Clean UP exec sp_xml_removedocument #iDoc -- are we dealing with a brightstar user? SET #IsBrightstarUser = CASE WHEN exists (SELECT * FROM dbo.aspnet_UsersInRoles AS uir inner join dbo.aspnet_Roles AS roles ON uir.RoleId = roles.roleid WHERE roles.rolename = 'Brightstar Employee' and uir.userid = #userid) THEN 1 ELSE 0 END --Get User Storelist INSERT INTO [#UserStoreList] ( [StoreID_XRef] ) SELECT s.StoreId_XRef FROM PFO.UserStoreLink us(nolock) INNER JOIN PFO.Store s(nolock) ON us.StoreId=s.StoreId where UserId=#UserID --Select * from [#PFOList] --SELECT #IsBrightstarUser AS ISBrightstaruser --SELECT * from [#UserStoreList] --If BrightstarCustomer Update all the Quantities. IF #IsBrightstarUser=1 BEGIN UPDATE PFO.PFO SET IsBrightstarReviewComplete = 1 ,[ModifyingUsersID] = #UserID ,[ModifiedDate] = getdate() ,[PlannedQty] = pfol.[Quantity] ,[BrightstarReviewedQty]=pfol.[Quantity] FROM PFO.PFO as pfo INNER JOIN [#UserStoreList] as stores on pfo.StoreId_XRef=stores.StoreID_XRef INNER JOIN [#PFOList] as pfol on pfo.PFOId = pfol.PFOId WHERE #IsBrightstarUser = 1 END ELSE BEGIN --Update Non Contrained Orders UPDATE PFO.PFO SET [ModifyingUsersID] = #UserID ,[ModifiedDate] = getdate() ,[PlannedQty] = pfol.[Quantity] FROM PFO.PFO (nolock) as pfo INNER JOIN [#UserStoreList] as stores on pfo.StoreId_XRef=stores.StoreID_XRef INNER JOIN [#PFOList] as pfol on pfo.PFOId = pfol.PFOId WHERE pfo.IsBrightstarReviewComplete=1 AND IsConstraint=0 --SELECT * from PFO.PFO (nolock) where PFOId='04676723-2afb-49ff-9fa1-0131cabb407c' --Update Contrained Orders --Get Existing quantities for the User CREATE TABLE #ExistingProductQuantity ( [PfoID] nvarchar(100) ,[Product] nvarchar(255) ,[PlannedQty] INT ,[BrightstarReviewedQty] INT ) CREATE TABLE #CustProductQuantity ( [Product] nvarchar(255) ,[IsUpdatable] BIT ) INSERT INTO #ExistingProductQuantity ( [PfoID],[Product],[PlannedQty],[BrightstarReviewedQty]) SELECT PFOId,InventoryId,PlannedQty,BrightstarReviewedQty FROM PFO.PFO as pfo INNER JOIN [#UserStoreList] as stores on pfo.StoreId_XRef=stores.StoreID_XRef WHERE pfo.IsBrightstarReviewComplete=1 AND IsConstraint=1 UPDATE #ExistingProductQuantity SET [PlannedQty]=pfol.[Quantity] FROM #ExistingProductQuantity eoq INNER JOIN [#PFOList] as pfol on eoq.PFOId = pfol.PFOId INSERT INTO #CustProductQuantity ( [Product],[IsUpdatable] ) SELECT [Product], CASE WHEN SUM(PlannedQty)<=SUM(BrightstarReviewedQty) THEN 1 ELSE 0 END FROM #ExistingProductQuantity GROUP BY [Product] --SELECT * from #ExistingProductQuantity --SELECT * from #CustProductQuantity --Update the products that can be updatable UPDATE PFO.PFO SET [ModifyingUsersID] = #UserID ,[ModifiedDate] = getdate() ,[PlannedQty] = pfol.[Quantity] FROM PFO.PFO as pfo INNER JOIN #UserStoreList as stores on pfo.StoreId_XRef=stores.StoreID_XRef INNER JOIN #PFOList as pfol on pfo.PFOId = pfol.PFOId INNER JOIN #CustProductQuantity as pr on pr.Product=pfo.InventoryId WHERE pfo.IsBrightstarReviewComplete=1 AND pr.IsUpdatable=1 AND IsConstraint=1 --Return the products that are not updatabele select [Product] FROM #CustProductQuantity where [IsUpdatable]=0 END END TRY BEGIN CATCH -- Get the ErrorNumber Set #iErrorNumber = ERROR_NUMBER() --Handle Deadlock situation (Deletes, Inserts & Updates) IF #iErrorNumber = 1205 BEGIN -- If we have not made enough attempts to break the lock IF #iDeadLockRetries < sCommon.fnMaxDeadlockRetries() BEGIN -- Increment the Attempt count SET #iDeadLockRetries = #iDeadLockRetries + 1 -- Pause to allow the deadlock contention to clear WAITFOR DELAY #dtDeadlockSleepInterval GOTO RETRY END END -- Handle Lock Timeout situation (Deletes, Inserts & Updates) IF #iErrorNumber = 1222 BEGIN -- If we have not made enough attempts to break the Deadlock IF #iLockTimeoutRetries < sCommon.fnMaxLockTimeoutRetries() BEGIN -- Increment the Attempt count SET #iLockTimeoutRetries = #iLockTimeoutRetries + 1 -- Pause to allow the lock contention to clear WAITFOR DELAY #dtLockTimeoutSleepInterval GOTO RETRY END END exec Common.RethrowError END CATCH END The result is as follows.. Product 6435LVWK-360-CD819E3 NSCHI535C1097I360-4C NSCHU485C1819I360-0C Return Value 0 My Linq-to-SQL connection is like this [global::System.Data.Linq.Mapping.FunctionAttribute(Name="PFO.PFOValidateUpdateData")] public int PFOValidateUpdateData([global::System.Data.Linq.Mapping.ParameterAttribute(Name = "PfoIDs", DbType = "Xml")] System.Xml.Linq.XElement pfoIDs, [global::System.Data.Linq.Mapping.ParameterAttribute(Name = "UserID", DbType = "UniqueIdentifier")] System.Nullable<System.Guid> userID) { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), pfoIDs, userID); return ((int)(result.ReturnValue)); } I am trying to retrieve all the data from the stored procedure but the when I debugging it the return value is "o".. I would be grateful to you if you could help me retrieve all the data returned by the stored procedure... thank you very much...
If your stored procedure returns a collection of nvarchar's, then the signature of your Linq2Sql method is not correct. It should not return an int, but an ISingleResult. So the correct signature will be: public ISingleResult<string> PFOValidateUpdateData(...) { IExecuteResult result = this....; return (ISingleResult<string>)result.ReturnValue; } var products = PFOValidateUpdateData(...).ToList(); If you want to return the results from multiple SELECT's in your stored procedure, you'll have to use IMultipleResults.
Well I know this is not the right way...for time being,its working for me... I created an other table with two columns one ProductId and ID, I am inserting the values returned by the stored procedure, in the designer.cs I am returning the table, [global::System.Data.Linq.Mapping.FunctionAttribute(Name="PFO.PFOValidateUpdateData")] public ISingleResult<PFOValidData> PFOValidateUpdateData([global::System.Data.Linq.Mapping.ParameterAttribute(Name = "PfoIDs", DbType = "Xml")] System.Xml.Linq.XElement pfoIDs, [global::System.Data.Linq.Mapping.ParameterAttribute(Name = "UserID", DbType = "UniqueIdentifier")] System.Nullable<System.Guid> userID) { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), pfoIDs, userID); return ((ISingleResult<PFOValidData>)(result.ReturnValue)); } And in the Domainservice List<string> PFOValidateUpdateData(string pfoIds, Guid userID) { List<string> productIdList = new List<string>(); // Acquire the int result = this.DataContext.PFOValidateUpdateData(element, userID); foreach (var item in result) { productIdList.Add(item.ProductID); } return productIdList; To get the multiple values returned by the stored procedure.... Please let me know if there is a better way to solve this... thank you
SQL Server: Check if Child Rows Exist
I am working on a web application where there are many tables but two will suffice to illustrate my problem: User Order Let us say that the User table has a primary key "UserID", which is a foreign key in the Order table called "CreatedBy_UserID". Before deleting a User, I would like to check if the Order table has a record created by the soon-to-be deleted user. I know that a SqlException occurs if I try to delete the user but let us say that I want to check beforehand that the Order table does not have any records created by this user? Is there any SQL code which I could run which will check all foreign keys of a table if that row is being referenced? This for me is generally useful code as I could remove the option for deletion altogether if it can be detected that the user exists in these other tables. I don't want a simple query (SELECT COUNT(*) FROM Order WHERE CreatedBy_UserID == #userID) because this will not work if I create another foreign key to the Order table. Instead I want something that will traverse all foreign keys. Can this be done?
Below is code for an sp that I've used in the past to perform this task (please excuse the indenting): create proc dbo.usp_ForeignKeyCheck( #tableName varchar(100), #columnName varchar(100), #idValue int ) as begin set nocount on declare fksCursor cursor fast_forward for select tc.table_name, ccu.column_name from information_schema.table_constraints tc join information_schema.constraint_column_usage ccu on tc.constraint_name = ccu.constraint_name join information_schema.referential_constraints rc on tc.constraint_name = rc.constraint_name join information_schema.table_constraints tc2 on rc.unique_constraint_name = tc2.constraint_name join information_schema.constraint_column_usage ccu2 on tc2.constraint_name = ccu2.constraint_name where tc.constraint_type = 'Foreign Key' and tc2.table_name = #tableName and ccu2.column_name = #columnName order by tc.table_name declare #fkTableName varchar(100), #fkColumnName varchar(100), #fkFound bit, #params nvarchar(100), #sql nvarchar(500) open fksCursor fetch next from fksCursor into #fkTableName, #fkColumnName set #fkFound = 0 set #params=N'#fkFound bit output' while ##fetch_status = 0 and coalesce(#fkFound,0) <> 1 begin select #sql = 'set #fkFound = (select top 1 1 from [' + #fkTableName + '] where [' + #fkColumnName + '] = ' + cast(#idValue as varchar(10)) + ')' print #sql exec sp_executesql #sql,#params,#fkFound output fetch next from fksCursor into #fkTableName, #fkColumnName end close fksCursor deallocate fksCursor select coalesce(#fkFound,0) return 0 end This will select a value of 1 if a row has any foreign key references. The call you would need would be: exec usp_ForeignKeyCheck('User','UserID',23)
There is no clean way to iterate through all FK columns where multiple exist. You'd have to build some dynamic SQL to query the system tables and test each in turn. Personally, I wouldn't do this. I know what FKs I have: I'll test each in turn ... IF EXISTS (SELECT * FROM Order WHERE CreatedBy_UserID == #userID) RAISERROR ('User created Orders ', 16, 1) IF EXISTS (SELECT * FROM Order WHERE PackedBy_UserID == #userID) RAISERROR ('User packed Orders', 16, 1) ... You wouldn't dynamically iterate through each property of some user object and generically test each one would you? You'd have code for each property
This code will give you a list of the foreign keys which are defined for a specifit table: select distinct name from sys.objects where object_id in ( select constraint_object_id from sys.foreign_key_columns as fk where fk.Parent_object_id = (select object_id from sys.tables where name = 'tablename') )
You can use transaction to check it. I know it seems like stone ax, but it working fast and stable. private bool TestUser(string connectionString, int userID) { var result = true; using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); var command = connection.CreateCommand(); var transaction = connection.BeginTransaction(); command.Connection = connection; command.Transaction = transaction; try { command.CommandText = "DELETE User WHERE UserID = " + userID.ToString(); command.ExecuteNonQuery(); transaction.Rollback(); } catch { result = false; } } return result; }