Having a stored procedure affect only the currently focused row - c#

I’m using Visual Studio with DevExpress Winforms in conjunction with Microsoft SQL Server Manager, and I’m currently trying to write a stored procedure that will activate on a button click, copying the focused row to a different table.
I’ve looked into it and haven’t been able to find a solution that does what I need it to. I would prefer not to use checkboxes if possible, but am open to it.
My current code within the stored procedure is:
CREATE PROCEDURE [dbo].[procedurename]
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS (SELECT * FROM tbl_b)
INSERT INTO tbl_b (column names here)
SELECT DISTINCT (column names here)
FROM tbl_a
END
I’ve been able to successfully call this procedure when testing the program with the button, and it functions mostly as intended, moving the data from one table to the other with no duplicates, however it moves the entire table when I only want to move a focused row.
I’m using gridView for this, and I’ve tried solutions that use DataGridView that don’t seem to work.
This is a desktop based program.

You need a parameter in your stored procedure that holds an identification of the row you want to copy
CREATE PROCEDURE [dbo].[procedurename] (#ID int)
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS (SELECT * FROM tbl_b)
INSERT INTO tbl_b (column names here)
SELECT DISTINCT (column names here)
FROM tbl_a
where tbl_A.PrimaryKeyColumn = #ID
END
In the Click event of your button you could then do something like this
var id = myGridView.GetRowCellValue(myGridView.FocusedRowHandle, "YourPrimaryKeyName");
and now you can use this id variable to pass to your stored procedure
However, you do have another problem in your stored procedure
IF NOT EXISTS (SELECT * FROM tbl_b)
This check will never allow you to do your insert once there is 1 or more rows in that table.
I think you need something like
IF NOT EXISTS (SELECT 1 FROM tbl_b where tbl_b.PrimaryKey = #ID)

Related

wpf c# sql: can i select table with aggregated values in same stored procedure?

I am not using property bindings or collections. I am exporting an Excel file to my DataGrid and simultaneously INSERT into my sql database table using a stored procedure. I aggregate the values of the columns, and add them together in the stored procedure.
The datagrid is populated with pre-calculated data, and the table is filled with POST-calculated data.
But, I want the datagrid to also be populated with the post-calculated data.
I thought I'd just do an easy select * from table after the stored procedure connection had been closed, but i am unable. How can I do this?
My stored procedure looks a bit like this (a bit shortened but):
create procedure
as
begin
insert into dbo.myTable(//list of my columns)
select column1, column2, column3.....
// do aggregations here
from tblMyTable
end
I have tried many things, how can i change this stored procedure to get the table contents after values have been aggregated/calculated
do i need to run a completely new code block in c#. im not sure how to do that with no bindings, and also, i dont want to click a new button, so i hope it can by changing this stored procedure

Cannot get a stored procedure table output into my .NET Application

I have a stored procedure that looks like this:
DECLARE #A TABLE ( TabYear int, Std decimal(18,2))
DECLARE #B TABLE ( TabYear int, Std decimal(18,2))
BEGIN
DECLARE #FinalTable TABLE (TabYear int, HoldayA decimal(18,2), HolidayB decimal(18,2))
BEGIN
INSERT INTO #A(TabYear, std)
Select ... from ...
INSERT INTO #B(TabYear, Std)
Select ... from ...
END
INSERT INTO #FinalTable(TabYear, HoldayA, HoldayB)
SELECT
A.TabYear, a.Std, U.Std
FROM
#A A
LEFT JOIN
#B U ON a.TabYear = U.TabYear
END
Select * from #FinalTable
Now I want to get that into a DATASET within my .NET application using the TableAdapter Wizard. I choose :"Use existing stored procedure" and choose the procedure (above) but as Data Column I get only Column1. It looks like the DataTableAdapter does not recognize the table. If I let the SP run on the server everything is fine. I get the table as I wish. I also checked if the wizard recognizes other SP's I am working with on the table and that works fine. But I have to admit that all the other SP's I use are straigth Select commands nothing with querying #Tables like in this SP. Could anyone help me get the table into my application. I do not have go go with the DataSet necessarily a code solution getting the data into an array or something would help me too.
You cannot achieve this using the TableAdapter Wizard because your stored procedure has no defined output because its dynamically created. You must manually construct the TableAdapter in your code.
Probably because you SELECT * from a table that's defined within the sproc, so the analyzer can't determine the schema. Either define the columns outside of the wizard or change your sproc to select specific columns (although the wizard won't be able to determine the type, so you'll still have to edit the data table schema).
You could also turn the sproc into one SELECT that uses subqueries instead of filling a table variable, but that may have been done for performance reasons...
I do not have go go with the DataSet necessarily
That's fine, but you'll still hove to map the columns of the result set to a structure in C# code at some point.
Wizards only take you so far; at some point you need to start casting your own spells...

Tracking who makes changes to SQL Server within C# application or SQL Server

I'm converting an application from Access to SQL Server 2014. One of the capabilities of this tool is to allow users to create ad-hoc SQL queries to modify delete or add data to a number of tables.
Right now in Access there is no tracking of who does what so if something gets messed up on accident, there is no way to know who it was or when it happened (it has happened enough times that it is a serious issue and one of many reasons the tool is being rewritten).
The application I'm writing is a Windows application in C#. I'm looking for ANY and all suggestions on ways this can be done without putting a huge demand on the server (processing or space). Since the users are creating their own queries I can't just add a column for user name and date (also that would only track the most recent change).
We don't need to keep the old data or even identifying exactly what was changed. Just who changed data and when they did. I want to be able to look at something (view, table or even separate database) that shows me a list of users that made a change and when they did it.
You haven't specified the SQL Server Version, anyway if you have a version >= 2008 R2 you can use Extended Events to monitor your system.
On stackoverflow you can read my answer to similar problem
You can consider to use triggers and a log table, this will work on all SQL Servers. Triggers are a bit more expensive that CDC, but if your users already are updating directly on your tables, this should not be a problem. I think this also will depend on how many tables you want to log.
I will provide you with a simple example for logging the users that has changed a table, or several tables (just add the trigger to the tables):
CREATE TABLE UserTableChangeLog
(
ChangeID INT PRIMARY KEY IDENTITY(1,1)
, TableName VARCHAR(128) NOT NULL
, SystemUser VARCHAR(256) NOT NULL DEFAULT SYSTEM_USER
, ChangeDate DATETIME NOT NULL DEFAULT GETDATE()
)
GO
CREATE TABLE TestTable
(
ID INT IDENTITY(1,1)
, Test VARCHAR(255)
)
GO
--This sql can be added for multiple tables, just change the trigger name, and the table name
CREATE TRIGGER TRG_TABLENAME_Log ON TestTable
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
--Can be used to get type of change, and wich data that was altered.
--SELECT * FROM INSERTED;
--SELECT * FROM DELETED;
DECLARE #tableName VARCHAR(255) = (SELECT OBJECT_NAME( parent_id ) FROM sys.triggers WHERE object_id = ##PROCID);
INSERT INTO UserTableChangeLog (TableName) VALUES (#tableName);
END
GO
This is how it will work:
INSERT INTO TestTable VALUES ('1001');
INSERT INTO TestTable VALUES ('2002');
INSERT INTO TestTable VALUES ('3003');
GO
UPDATE dbo.TestTable SET Test = '4004' WHERE ID = 2
GO
SELECT * FROM UserTableChangeLog

Adding stored procedures complex types in Entity Framework

I am trying to use a stored procedure in Entity Framework that returns nothing.
I did the following:
Added a function (right click on stored procedure -> add -> function import-> Complex Type -> Get column information -> create New Complex-Type)
My function name: summarySP_Result. After building the project the entity class is not generated in Generated_code (BusinessAccount.web.g.cs)
But entity classes for tables and views are all created but nor for stored procedure.
Can anybody give the idea why it is not generated entity class in BusinessAccount.web.g.cs?
Update :
Let me confirm ReturnDataFromTemTable_result entity class created in your XXXXXX.web.g.cs class.
Like :
[DataContract(Namespace="http://schemas.datacontract.org/2004/07/BizFramework.Web.Model")]
public sealed partial class ReturnDataFromTemTable_Result : Entity
{
-------------------
}
OK - here's the step-by-step way of doing this:
(1) add your stored procedure to the EDMX file (when you first create it, or later on by using Update model from database and picking that stored procedure)
(2) once you have the stored procedure in your model - use the Model Browser to add a Function Import :
(3) the next dialog that pops up is vitally important - you need to (1) define that the stored procedure returns a collection of complex types, then you need to (2) get the column info from that stored procedure to know what columns it will return, then (3) you tell Visual Studio to generate a new complex type based on that column info:
(4) once you've done that - you should now see the stored procedure in your conceptual model section in the Model Browser, and the newly generated complex type should show up there, too:
This is for Ross Brigoli
Try adding this line to the beginning of your stored procedure:
SET FMTONLY OFF
You can remove this after you have finished importing.
Source:-
Why can't Entity Framework see my Stored Procedure's column information?
If this is still unresolved, after you Add the Function Import, go to the Solution Explorer, right click your {name}.Context.tt file and do "Run Custom Tool". The method will now show up in your derived Context class.
This seems like a bug in Visual Studio 2012, which is what I am using, I haven't applied Update 1, I will try to see if that fixes it.
As Sandeep said,
EF doesn't support importing stored procedures which build result set from Dynamic queries or Temporary tables.
But you don't have to rewrite your whole SP.
Just write another one, with the same name, that returns the correct row format without using dynamic SQL or a temp table. Then use the EF SP adding function, which will now auto generate the complex type.
Edit: It's actually easier to make a comment at the top of the SP that immediately selects the desired row with all the data types specified with CASTS. When you need to import the SP into EF, just uncomment the code.
e.g.
CREATE PROCEDURE myProc ()
AS
BEGIN
-- uncomment the following row to import:
-- SELECT CAST( 0 AS int) AS column1Name, CAST( 'a' AS varchar(50)) AS clumn2name
-- comment out the SP content when you want to import it.
< proper SP content >
END
Then drop the stored proc and create the original.
Save this temporary importing SP you have made in case you need it again, though.
EF doesn't support importing stored procedures which build result set from:
Dynamic queries
Temporary tables
Re-write your stored procedure to use a table variable instead.
remember to drop the stored procudure and function import from your model before updating as it wont generate the complex type unless it also adds the stored procedure. or go to function import properties and use the get column information feature after updating your stored procedure.
create procedure [dbo].[usp_InsertOrUpdate]
/*if your table(tbl_InsertOrUpdate) as 3 columns like uniqueid,col1,col2*/
#uniqueId bigint NULL,/*if insert send value as null or 0*/
#col1 bigint null,
#col2 [varchar](500) NULL
as
begin
set nocount ON
SET FMTONLY OFF
/* for giving result which column updated(uniqueId) and is it insert or update(IsInsert)*/
declare #varResult table (uniqueId bigint ,IsInsert bit )
/*create a var table before inserting original table*/
declare #varInsertOrUpdate table (
uniqueId bigint ,
col1 [bigint] ,
col2 [varchar]
)
/*default we are feel as update only*/
insert into #varResult (uniqueId,IsInsert) values (#uniqueId,0)
/*insert into var table*/
INSERT INTO #varInsertOrUpdate (uniqueId,col1,col2)
VALUES
(#uniqueId,#col1,#col2)
/*Insert into original table with where condition without if else*/
INSERT INTO tbl_InsertOrUpdate (col1,col2)
select col1,col2 from #varInsertOrUpdate
where uniqueId!=0;
/*if its insert updating result returning table*/
update #varResult set
uniqueId=IDENT_CURRENT('tbl_InsertOrUpdate'),
IsInsert=1 where #uniqueId=0;
/*updating table where #uniqueid is null or empty*/
UPDATE tbl_InsertOrUpdate
SET col1=#col1,
col2=#col2,
WHERE uniqueId=#uniqueId and #uniqueId!=0
select * from #varResult
end
To add complex type correctly, go Model browser, right click on function, then display edit, click edit an fill the dialog box. The name of the function should be same as name of the stored procedure. Click OK button. Now function is created. Then right click on the created function and go edit again.There is a update button aside of complex type button.Update it using that update button. Now complex type is created completely.
This is my SP to Implement the multiple search
***************************************************
CREATE PROCEDURE [dbo].[uspSEARCH_POSITIONS]
#OBJ_TYPE_REQUEST varchar(2000),--'FIRST_NAME;SEARCHVALUE|LAST_NAME;SEARCHVALUE|JOB_DESCRIPTION;SEARCHVALUE'
#DELIMITER varchar(10) --'| Which seperates the col;searchvalue|col;searchvalue
AS
BEGIN
SET FMTONLY OFF
DECLARE
#lLastName varchar(100),
#lFirstName varchar(100),
#lPositionNumber varchar(20),
#lJobDescription varchar(50),
#lJobCode varchar(20),
#lOccupancyIndicator varchar(50),
#ldeleimitercolsearchval varchar(10)
SET #ldeleimitercolsearchval =';'
CREATE TABLE #TempTable (ColSearchValues VARCHAR(2000))
INSERT INTO #TempTable
SELECT * FROM [dbo].[fnSplit](#OBJ_TYPE_REQUEST,#DELIMITER)--'fname;searchvalfname|lname;searchvallname|jobcode;searchvaljobcode','|')
SELECT #lLastName=SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%last%'
SELECT #lFirstName =SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%first%'
SELECT #lPositionNumber =SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%position%'
SELECT #lJobDescription=SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%jobd%'
SELECT #lJobCode=SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%jobc%'
SELECT #lOccupancyIndicator=SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%ccupancy%'
SELECT [PS].[POSITION_NUMBER]
,[PS].[COST_CENTER]
,[PS].[JOB_CODE]
,[PS].[JOB_CODE_DESCRIPTION]
,[PS].[SITE_CODE]
,[EMP].[EMPLOYEE_ID]
,[EMP].[EIN]
,[EMP].[GRADE]
,[EMP].[LOGIN_ID]
,[EMP].[FIRST_NAME]
,[EMP].[LAST_NAME]
,LTRIM(RTRIM(ISNULL([EMP].[LAST_NAME],''))) + ',' +LTRIM(RTRIM(ISNULL([EMP].[FIRST_NAME],''))) AS [FULL_NAME]
,[EMP].[DISTRICT]
,[EMP].[SUPERVISOR_EIN]
,COUNT(*) OVER() AS TOTAL_RECORD_COUNT
FROM [DBSERVER].[dbo].[uvwPOSITION_SEARCH] PS
LEFT JOIN [DBSERVER].[dbo].[uvwEMPLOYEES] EMP
ON PS.POSITION_NUMBER=EMP.POSITION_NUMBER
WHERE
(#lLastName IS NULL OR [LAST_NAME] LIKE '%' + #lLastName + '%')
AND (#lFirstName IS NULL OR [FIRST_NAME] LIKE '%' + #lFirstName + '%')
AND (#lPositionNumber IS NULL OR [PS].[POSITION_NUMBER] LIKE '%' + #lPositionNumber + '%')
AND (#lJobDescription IS NULL OR [PS].[JOB_CODE_DESCRIPTION] LIKE '%' + #lJobDescription + '%')
AND (#lJobCode IS NULL OR [PS].[JOB_CODE] LIKE '%' + #lJobCode + '%')
AND (#lOccupancyIndicator IS NULL OR [EMP].[FILLED_VACANT] LIKE '%' + #lOccupancyIndicator + '%')
END
Now you can consume above SP in edmx using below
Adding stored procedures complex types in Entity Framework
Why can't Entity Framework see my Stored Procedure's column information?
And in case you have to update your SP below worked for me.
Updating Complex Type if Stored Procedure Updates
How Do I Get Entity Framework To Update Complex Types?
For me, Im having problems where importing my Stored Procedure into EF is not generating the Complex Entity return object (automatically). I found however, after commenting out sections of my sproc (aka stored procedure), that when I then re-imported the stored procedure back in (ie refreshed using the Get Column Information button in the Function Import Edit screen), that the Complex type could then be generated!
In short, there could be a where clause (or maybe something else) causing EF to not generate the Complex Type. Try commenting out sections of your sproc and re-importing the sproc to
UPDATE:
Further to my investigation above, I found that the reason the Complex Entity was not being generated was because my sproc was using a view (instead of a typical table). For curiosity sake, I changed the view to another table just to see what would happen, and the complex entity generated.
So, in short, it looks like Complex Entities might not generate automatically if you have a view. To try, I ripped out the view temporarily, re-import the sproc, generated the Complex Entity, then put the view back in. But now my code gives exceptions.
Will update on this later when I learn more =)
UPDATE:
Fixed the issue. Really silly mistake! The viewname that I was using was not spelled right =D. Im sort of angry that an error wasnt thrown by Sql Server when I created the sproc..... I guess that is life :) Alas, problem now fixed!
The issue of complex type not appearing may happen due to a different reason as well which is what I faced in our case. The issue was due to a syntax error in the SPROC where temp table was defined as below -
create table #temp(
col1 int,
col2 nvarchar(100),
col3 nvarchar(100), -- Notice the comma in the end
);
Surprisingly, SQL Server doesn't throw any error when you compile the sproc. Removing the comma fixed the problem for us.
In short, while some of the above solutions might work depending on the specific issue, my suggestion is to check your sproc for such syntactical errors that SQL might ignore but could be the underlying reason for this problem. Thanks.
Go to the Model Browser
If you need to modify existing function
Under the Function Imports >> Select the function to be modified >> Click Edit
You will need to update the function to refresh and you can see the columns need to be added

ChangeConflictException when updating rows with LINQ-to-SQL

I have a form which contains a data grid and a save button.
When the user clicks the save button I check for new rows by checking a specific column. If its value is 0 I insert the row to database, and if the column value is not 0 then I update that row.
I can insert correctly but when updating an exception occurs:
ChangeConflictException was unhandled,1 of 6 updates failed.
I have checked the update statement and I'm sure it's correct. What is the problem, can any one help me?
int id;
for (int i = 0; i < dgvInstructores.Rows.Count - 1; i++)
{
id = int.Parse(dgvInstructores.Rows[i].Cells["ID"].Value.toString());
if (id == 0)
{
dataClass.procInsertInstructores(name, nationalNum, tel1, tel2,
address, email);
dataClass.SubmitChanges();
}
else
{
dataClass.procUpdateInstructores(id, name, nationalNum, tel1, tel2,
address, email);
dataClass.SubmitChanges();
}
}
I'm using linq to query sql server2005 database and vs2008
the stored procedure for 'procUpdateInstructores' is :
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER proc [dbo].[procUpdateInstructores]
#ID int,
#name varchar(255),
#NationalNum varchar(25),
#tel1 varchar(15),
#tel2 varchar(15),
#address varchar(255),
#email varchar(255)
as
begin
BEGIN TRANSACTION
update dbo.Instructores
set
Name = #name , NationalNum = #NationalNum ,
tel1 = #tel1 , tel2 = #tel2 , address = #address , email = #email
where ID = #ID
IF (##ROWCOUNT > 0) AND (##ERROR = 0)
BEGIN
COMMIT TRANSACTION
END
ELSE
BEGIN
ROLLBACK TRANSACTION
END
end
In my experience, (working with .net forms and mvc with linq-to-sql) I have found that several times if the form collection contains the ID parameter of the data object then the update surely fails.
Even if the ID is the actual ID, it is still flagged as 'propertyChanged' when you bind it or update it or assign to another variable.
As such can we see the code for your stored procs? More specifically, the update proc?
The code you have posted above is fine, the exception should be coming from your stored proc.
However if you are confident that the proc is correct then perhaps look at the HTML code being used to generate the table. Some bugs might be present with respect to 0/1 on ID columns, etc.
In the absence of further information (what your SQL or C# update code looks like...) my first recommendation would be to do SubmitChanges once, outside the for loop, rather than submitting changes once per row.
It appears in this case that you are using a DataGridView (thus WinForms). I further guess that your dataClass is persisted on the form so that you loaded and bound the DataGridView from the same dataClass that you are trying to save the changes to in this example.
Assuming you are databinding the DataGridView to entities returned via LINQ to SQL, when you edit the values, you are marking the entity in question that it is needing to be updated when the next SubmitChanges is called.
In your update, you are calling dataClass.procUpdateInstructores(id, name, nationalNum, tel1, tel2, address, email); which immediately issues the stored procedure against the database, setting the new values as they have been edited. The next line is the kicker. Since your data context still thinks the object is still dirty, SubmitChanges tries to send another update statement to your database with the original values that it fetched as part of the Where clause (to check for concurrency). Since the stored proc updated those values, the Where clause can't find a matching value and thus returns a concurrency exception.
Your best bet in this case is to modify the LINQ to SQL model to use your stored procedures for updates and inserts rather than the runtime generated versions. Then in your parsing code, simply call SubmitChanges without calling procUpdateInstructores manually. If your dbml is configured correctly, it will call the stored proc rather than the dynamic update statement.
Also, FWIW, your stored proc doesn't seem to be doing anything more than the generated SQL would. Actually, LINQ to SQL would give you more functionality since you aren't doing any concurrency checking in your stored proc anyway. If you are required to use stored procs by your DBA or some security policy, you can retain them, but you may want to consider bypassing them if this is all your stored procs are doing and rely on the runtime generated SQL for updates.

Categories