Calling stored procedure to insert multiple values - sql

In our application we have a multiline grids which have many records. For inserting or updating we are calling a stored procedure.
As per the current implementation the stored procedure is calling for each line in the grid. For each line it checks the existence in the table. If data is already there, it will update the table else insert new data into the table.
Instead of calling the procedure for each line, we thought create a table value parameter and pass all the grid values at the same time.
My questions are:
Is it a good approach?
How to handle the existence check (for insert or update) if I pass the values as table-valued parameter? Do I need to loop through the table and check it?
Is it better to have separate stored procedures for insert and update?
Please provide your suggestions. Thanks in advance.

1) TVP is a good approach. And a single stored proc call is more efficient with fewer calls to the Database.
2) You haven't made it clear if each row in the grid has some kind of ID column that determines if the data exists in the Table, however assuming there is, make sure that it is indexed then use INSERT INTO and UPDATE statements like this:
To add new rows:
INSERT INTO [grid_table]
SELECT * FROM [table_valued_parameter]
WHERE [id_column] NOT IN (SELECT [id_column] FROM [grid_table])
To update existing rows:
UPDATE gt
SET gt.col_A = tvp.col_A,
gt.col_B = tvp.col_B,
gt.col_C = tvp.col_C,
...
gt.col_Z = tvp.col_Z
FROM [grid_table] gt
INNER JOIN [table_valued_parameter] tvp ON gt.id_column = tvp.id_column
NB:
No need to do an IF EXISTS() or anything as the WHERE and JOIN
clauses will run the same checks,so no need to do a 'pre-check'
before running each statement.
This assumes the TVP data isthe same structure as the Table in the
database.
YOU MUST make sure the id_column is indexed.
I've use 'INNER JOIN' instead of just 'JOIN' to make the point it is an inner join
3) Using the approach above you just new one stored proc, simple and effective

It's a good approach
Any way try to put the logic through object level for iterating and checking and finally insert/update in T-SQL. This reduces overhead for RDMS as object level functionality is faster than operations in RDBMS.
Dont put too may stored procedures for each type of operation have a minimised procedures with multiple operations based on parameters you send to it.
Hope it helps!

Yes, it is a good approach. Calling procedure for each row is bad for performance. TVPs make life easier.
Yes, you can do that check in stored procedure, which should be a simple SELECT on uniqueId in most of the cases.
With this approach, yes, it is better to have both in same stored procedure.

1) Using TVP is good approach, but send only new or updated rows as TVP, no need to send entire datagrid.
2) For INSERT/UPDATE use MERGE example:
MERGE [dbo].[Contact] AS [Target]
USING #Contact AS [Source] ON [Target].[Email] = [Source].[Email]
WHEN MATCHED THEN
UPDATE SET [FirstName] = [Source].[FirstName],
[LastName] = [Source].[LastName]
WHEN NOT MATCHED THEN
INSERT ( [Email], [FirstName], [LastName] )
VALUES ( [Source].[Email], [Source].[FirstName], [Source].[LastName] );
3) For your case one stored procedure is enough.

Related

DB2 Stored Procedures- looping through values?

Okay, so I'm a novice at writing stored procedures. I'm trying to perform a function similar to a foreach() you would see in a programming language. Right now I have a temp table populated with the values I'd like to loop through. I would like to (for each value in this table) execute a SQL statement based upon that value. So, here's my pseudocode to illustrate what I'm really after here:
foreach(value in my temp table) {
SELECT * FROM TABLE WHERE column_x = value
}
No I know nothing of stored procedures so how can I get this done? Here's my script so far:
DROP TABLE SESSION.X;
CREATE GLOBAL TEMPORARY TABLE
SESSION.X (
TD_NAME CHAR(30)
);
INSERT INTO
SESSION.X
SELECT DISTINCT
TD_NAME
FROM
DBA.AFFIN_PROG_REPORT
WHERE
TD_NAME IS NOT NULL;
Any help is very much appreciated!
You need, by example, a cursor.
See the example: https://stackoverflow.com/a/4975012/3428749
See the documentation: https://msdn.microsoft.com/pt-br/library/ms180169(v=sql.120).aspx

Procedure Advice

I'm looking to boost performance on one of our processes within a production database. We have 2 sets of SPs which are by configuration settings stored within a configuration table.
An example syntax would be:
Declare #SWITCH BIT
IF #SWITCH = 1
INSERT INTO DEST_TABLE_A
SELECT VALUES
FROM SOURCE_TABLE
IF #SWITCH = 2
INSERT INTO DEST_TABLE_B
SELECT VALUES
FROM SOURCE_TABLE
Would it be better practice in this instance to move the IF logic into the WHERE clause to create a standardized instead of the logic having a conditional within it?
E.g.
INSERT INTO DEST_TABLE_A
SELECT VALUES
FROM SOURCE_TABLE
WHERE #SWITCH = 1
INSERT INTO DEST_TABLE_B
SELECT VALUES
FROM SOURCE_TABLE
WHERE #SWITCH = 2
I appreciate this might be an opinion piece but I was curious to see if anyone else has had experience with this scenario.
The second example might lead you to parameter sniffing problem . (longer explanation here)
This issue is created by the query optimizer that generates an execution plan optimized for one of the values of the switch statement (and this is the value that you send first time when you call the stored procedure).
In your case, if you call the stored procedure with #switch = 1 first time, an execution plan for this parameter is generated. Subsequent calls with #switch = 2 might take significantlty longer times to process.

How to SELECT [temp table1] = [subselect 1], [temp table2] = [subselect 2] FROM [Stored Procedure]

I have a stored procedure that returns two selects, which I use in a report.
The first select is data to display in tabular format and the second are metadata to display in the report head, like showed below:
CREATE PROCEDURE dbo. GetReport
#Input INT
AS
BEGIN
--Get #Metadata
-- #Results = f(#Metadata) … compex calculation
SELECT * FROM #Results
SELECT * FROM #Metadata
END
As the sproc calculation is quite intensive, I would like to prepare the report lines as plain data (in two tables: PrecalcResults and PrecalcMetadata) for some mostly used sproc parameters overnight.
Lather I would directly select the precalculated vaues or calculate them with the sproc according to the parameters.
For maintenance reasons I would like to use the same sproc to calculate data that would be:
1. showed in the report
2. be stored in PrecalcResults and PrecalcMetadata (with the used parameters)
If I would have single select sproc I would an approach desctibed here:
Insert results of a stored procedure into a temporary table
As I have multiselect sproc I would like to do something like above but with two tables.
In .net I would do DataSet.Tables[0] and DataSet.Tables[1]..., but I want to do it in tsql, to run it in daily job.
Is this even possible in MS SQL?
I have to apologize myself, from the answer below I can see I was not very clear.
I would like to do implement this functionality as pure TSQL.
Yes, this is possible.
It's perfectly fine to return multiple result sets from a single stored procedure as you have suggested.
Your only potential issue is the limitation of a TableAdapter being able to pull both result sets from the stored procedure, but there's a very simple work-around for that issue.

How to efficiently SELECT rows from database table based on selected set of values

I have a transaction table of 1 million rows. The table has a field name "Code" to keep customer's ID. There are about 10,000 different customer code.
I have an GUI interface allow user to render a report from transaction table. User may select arbitrary number of customers for rendering.
I use IN operator first and it works for few customers:
SELECT * FROM TRANS_TABLE WHERE CODE IN ('...', '...', '...')
I quickly run into problem if I select few thousand customers. There is limitation using IN operator.
An alternate way is create a temporary table with only one field of CODE, and inject selected customer codes into the temporary table using INSERT statement. I may then using
SELECT A.* FROM TRANS_TABLE A INNER JOIN TEMP B ON (A.CODE=B.CODE)
This works nice for huge selection. However, there is performance overhead for temporary table creation, INSERT injection and dropping of temporary table.
Do you aware of better solution to handle this situation?
If you use SQL Server 2008, the fastest way to do this is usually with a Table-Valued Parameter (TVP):
CREATE TYPE CodeTable AS TABLE
(
Code int NOT NULL PRIMARY KEY
)
DECLARE #Codes AS CodeTable
INSERT #Codes (Code) VALUES (1)
INSERT #Codes (Code) VALUES (2)
INSERT #Codes (Code) VALUES (3)
-- Snip codes
SELECT t.*
FROM #Codes c
INNER JOIN Trans_Table t
ON t.Code = c.Code
Using ADO.NET, you can populate the TVP directly from your code, so you don't need to generate all those INSERT statements - just pass in a DataTable and ADO.NET will handle the rest. So you can write a Stored Procedure like this:
CREATE PROCEDURE GetTransactions
#Codes CodeTable READONLY
AS
SELECT t.*
FROM #Codes c
INNER JOIN Trans_Table t
ON t.Code = c.Code
... and just pass in the #Codes value as a parameter.
You can generate SQL such as
SELECT * FROM TRANS_TABLE WHERE CODE IN (?,?,?,?,?,?,?,?,?,?,?)
and re-use it in a loop until you've loaded all the IDs you need. The advantage is that if you only need a few IDs your DB doesn't need to parse all those in-clauses. If many IDs is a rare case then the performance hit may not matter. If you are not worried about the SQL parsing cache then you can limit the size of the in clause to the DB's actual limit, so that sometimes you don't need a loop and other times you do.
As you have to pass the IDs somehow, IN should be the fastest way.
MSDN mentions:
Including an extremely large number of values (many thousands) in an IN clause can consume resources and return errors 8623 or 8632. To work around this problem, store the items in the IN list in a table.
If you still can use IN and the query is to slow, you could try to adjust your indexes like using some covering index for your query. Looking up random values by the clustered index can be slow, because of the random disk I/O required. A covering index could reduce that problem.
If you really pass limit of IN and you create a temporary table, I don't expect the creation of the table to be a major problem, as long as you insert the values at once (not thousands of queries of course). Choose the method with the least overhead, like one of those mentioned here:
http://blog.sqlauthority.com/2008/07/02/sql-server-2008-insert-multiple-records-using-one-insert-statement-use-of-row-constructor/
Of course, if there is some static pattern in your IDs you could select by that (like in SPs or UDFs). If you get those thousands of IDs out of your database itself, instead of passing them back and forth, you could just store them or use a subquery...
Maybe you could pass the customer codes to a stored procedure comma separated and use the split sql function mentioned here: http://www.devx.com/tips/Tip/20009.
Then declare a scalar table where you insert the splitted values in and use an IN clause.
CREATE PROCEDURE prc_dosomething (
#CustomerCodes varchar(MAX)
)
AS
DECLARE #customercodetable table(code varchar(10)) -- or whatever length you require.
SET #customercodetable = UTILfn_Split(#CustomerCodes) -- see the article above for the split function.
-- do some magic stuff here :).

Optional where clause / parameter in a SQL 2008 stored proc?

I'm writing some code that updates a table. Depending on what the user wants to do, it either updates a large set of records, or a smaller one. The delineating factor is a group ID.
The user can choose whether to update the table for all records, or just those with that groupID. I'd like to use the same stored procedure for both instances, with maybe a little logic in there to differentiate between the scenarios. (I'd prefer not to write two stored procs with 90% identical code.)
I'm no expert at stored procedures and am not sure if I can pass in optional parameters, or how to dynamically generate part of a where clause, depending on whether the groupID is there or not. Any suggestions are welcome.
Thanks!
You can use this or an "OR" contsruct
... WHERE GroupID = ISNULL(#GroupdID, GroupID)
... WHERE GroupID = #GroupdID OR #GroupdID IS NULL
create procedure MyProc (#GroupID int = null)
as
begin
update MyTable set ....
where #GroupID is null or GroupID = #GroupID
end