I have the following statement:
insert into forecast_entry.user_role_xref
( user_master_id ,
role_id ,
created_date ,
created_by
)
values
( 276 , -- user_master_id - int
101 , -- role_id - int
getdate() , -- created_date - datetime
'MICHAELSK' -- created_by - varchar(20)
)
I need to generate a row for role_id 101-355 (so the same statement above, except repeated with the role_id incrementing). What would be the best way to do this? To get the job done I'm intending on writing a quick C# application that will have a loop but I'm sure this isn't the best way and hope to learn something here to avoid having to do that in future (as I'm sure this kind of scenario is common).
Here is what I use, just modify as needed. Here, I add a bunch of sequence numbers to a table using a loop variable:
USE MyDB
GO
DECLARE #MyCounter as INT
SET #MyCounter = 1 -- to use this multiple times you can just
-- change the starting number and run again
-- if you do not want duplicate numbers
WHILE #MyCounter < 1000 -- any value you want
BEGIN
INSERT INTO [MyDB].[dbo].[MyTable]
([NumberField])
VALUES
(#MyCounter) -- insert counter value into table
set #MyCounter = #MyCounter + 1; -- increment counter
END
You should make use of numbers table and if you don't have one you can use master..spt_values like this:
insert into forecast_entry.user_role_xref
( user_master_id ,
role_id ,
created_date ,
created_by
)
select 276, -- user_master_id - int
number, -- role_id - int
getdate() , -- created_date - datetime
'MICHAELSK' -- created_by - varchar(20)
from master..spt_values
where type = 'P' and
number between 101 and 355
In my opinion, the best way is to create stored procedure. In stored procedure you should make a loop, which would insert data into table. From your C# application you open a connection to DB, call once a stored procedure and close a connection.
On SQL you get best perfomance working with big amount of data.
Here is an example
Instead of looping query Create a DataTable and create a stored procedure with User Defined Table Type
CREATE TYPE dtl AS TABLE
(
user_master_id INT ,
role_id INT,
created_date DATETIME,
created_by varchar(20)
)
And Stored procedure
CREATE PROCEDURE SPNAME
#dtl dtl READONLY
AS
INSERT INTO forecast_entry.user_role_xref
( user_master_id ,
role_id ,
created_date ,
created_by
)
SELECT
user_master_id ,
role_id ,
created_date ,
created_by
FROM #dtl
Pass DatatTable for #dtl parameter of stored procedure which contains the proper data between 101-255
if you create a loop in c# it will send same query again and again to database which is not a good idea.you rather create sp and loop there. as suggested by sham
Related
I'm writing a stored procedure to insert data from a form into two tables. One table has an autonumbered identity field. I need to insert the data into that table, find the newly created autonumber, and use that number to insert data into another table. So, to boil it down, I have a one-to-many link between the two tables and I need to make sure the identity field gets inserted.
Is this code the best way to do something like this, or am I missing something obvious?
CREATE PROCEDURE [dbo].[sp_Insert_CRT]
(
#TRACKING_ID int,
#CUST_NUM int,
#TRACKING_ITEM_ID int,
#STATEMENT_NUM nvarchar (200) = null,
#AMOUNT numeric (15, 2),
#BBL_ADJUSTED int = NULL,
#PAID_VS_BILLED int = NULL,
#ADJUSTMENT_TYPE int = NULL,
#ENTERED_BY nvarchar (10) = NULL,
#ENTERED_DATE date = NULL,
#AA_STATUS int = NULL
)
AS
BEGIN
-- Insert data into CRT_Main, where Tracking_ID is an autonumber field
INSERT into tbl_CRT_Main
(
-- TRACKING_ID
CUST_NUM
,TRACKING_ITEM_ID
,STATEMENT_NUM
,AMOUNT
)
VALUES
(
-- #TRACKING_ID
#CUST_NUM
,#TRACKING_ITEM_ID
,#STATEMENT_NUM
,#AMOUNT
)
-- Find the newly generated autonumber, and use it in another table
BEGIN TRANSACTION
DECLARE #TrackID int;
SELECT #TrackID = coalesce((select max(TRACKING_ID) from tbl_CRT_Main), 1)
COMMIT
INSERT into tbl_CRT_Admin_Adjustment
(
TRACKING_ID
,BBL_ADJUSTED
,PAID_VS_BILLED
,[ADJUSTMENT_TYPE]
,[ENTERED_BY]
,[ENTERED_DATE]
,AA_STATUS
)
VALUES
(
#TrackID
,#BBL_ADJUSTED
,#PAID_VS_BILLED
,#ADJUSTMENT_TYPE
,#ENTERED_BY
,#ENTERED_DATE
,#AA_STATUS
)
END
SELECT #TrackID = coalesce((select max(TRACKING_ID) from tbl_CRT_Main), 1)
No, don't do this. This will get you the maximum value of TRACKING_ID yes, but that doesn't mean that's the value that was created for your INSERT. If multiple INSERT statements were being run by different connections then very likely you would get the wrong value.
Instead, use SCOPE_IDENTITY to get the value:
SET #TrackID = SCOPE_IDENTITY();
Also, there is no need to wrap the above in an explicit transaction like you have with your SELECT MAX(). Instead, most likely, the entire batch in the procedure should be inside it's own explicit transaction, with a TRY...CATCH so that you can ROLLBACK the whole batch in the event of an error.
I have a very simple stored procedure which currently works perfectly when both parameters are sent values from form inputs.
However, I need to figure out what to do for IN_NUMBER if the value is empty because that column in the destination table is set to be nullable. It seems like the procedure itself is simply failing because it's waiting for a value.
What should I change?
IN parameters:
IN_NAME
IN_NUMBER
Routine:
P1 : BEGIN ATOMIC
INSERT INTO schema . postings
( name
, postNumber)
VALUES
( IN_NAME
, IN_NUMBER) ;
END P1
Example:
create table postings (name varchar(100), postNumber int) in userspace1#
create or replace procedure postings (
in_name varchar(100)
, in_number int
)
P1 : BEGIN ATOMIC
INSERT INTO postings
( name
, postNumber)
VALUES
( IN_NAME
, IN_NUMBER) ;
END P1#
call postings('myname', null)#
select * from postings#
NAME POSTNUMBER
---- ----------
myname <null>
There is no any problem here as you see.
What db2 error do you have exactly on a case similar to this?
If you want to handle NULL and replace it with some other value, use NVL(IN_NUMBER, 0) - you can exchange 0 for any other number of course (I'm assuming this is an integer).
Using 2008 R2, I have created a User-Defined Table Type as below.
IF TYPE_ID('ttypRPStats') IS NOT NULL
DROP TYPE dbo.ttypRPStats
CREATE TYPE dbo.ttypRPStats AS TABLE
(
RowNum INT NOT NULL IDENTITY (1,1) PRIMARY KEY CLUSTERED
, RP_Name VARCHAR(100)
, RPQualified_Name VARCHAR(200)
, RP_Type VARCHAR(100)
, RPCreatedDate DATETIME
, RPLast_ModifiedDate DATETIME
, RPActive BIT NOT NULL DEFAULT 0
)
GO
The declaration and use of a table variable works fine:
DECLARE #RPStats dbo.ttypRPStats
INSERT INTO #RPStats
(
RP_Name
, RPQualified_Name
, RP_Type
, RPCreatedDate
, RPLast_ModifiedDate
, RPActive
)
VALUES
(
'Name' -- RP_Name
, 'Longer.Name' -- RPQualified_Name
, 'TheirType' -- RP_Type
, '2004/07/04' -- RPCreatedDate
, '2013/05/01' -- RPLast_ModifiedDate
, 1 -- RPActive
)
select * from #RPStats
But what I'd like to be able to do is create a "real" table using the table type. I've read and re-read the CREATE TABLE command definition, and didn't detect a way to do this; and have conducted a great deal of research, but have not yet stumbled across a valid syntax. E.g. the following constructs do not work:
CREATE TABLE RPStats AS ttypRPStats
CREATE TABLE RPStats (ttypRPStats)
Is there a valid syntax to create a table using a table type?
I know I could send it all to a table variable first, then SELECT ... INTO the "real" table,
but that would be the undesirable option of last resort.
Thanks for your time.
I am created an online staff rota application. I have a stored procedure for each day of the week with an auto increment primary key for the Rota table. Each day within the selected week will have the same rota ID as a foreign key.
I have no problem with the first selected day but the following days return NULL for the foreign key RotaID as I don't know how to pass the RotaID into the other stored procedures to ensure the RotaID remains the same for the 7 days of the weekly Rota.
CREATE PROCEDURE [usp_RotaDay1]
#Week_Begin datetime,
#Week_End datetime,
#1ShiftDate datetime,
#1Day nchar (10),
AS
BEGIN
SET NOCOUNT ON;
DECLARE #RotaID int
IF NOT EXISTS
(
SELECT * FROM Rota
WHERE Week_Begin = #Week_Begin
AND
Week_End = #Week_End
)
BEGIN
INSERT INTO Rota
(
[Week_Begin],
[Week_End]
)
VALUES
(
#Week_Begin,
#Week_End
)
END
SELECT #RotaID = SCOPE_IDENTITY()
IF NOT EXISTS
(
SELECT * FROM DayShift
WHERE ShiftDate = #1ShiftDate
)
BEGIN
INSERT INTO DayShift
(
[RotaID],
[ShiftDate],
[Day]
)
VALUES
(
#RotaID,
#1ShiftDate,
#1Day
)
END
SET NOCOUNT OFF;
END
As you can see I have the RotaID declared in the Rota table and then passed as the foreign key in the DayShift table.
I would like to know if it is possible to pass this through to my other stored procedures which are similar to this one.
I'm not sure exactly what you are trying to do, but if you want to return the #RotaID parameter value to the calling program for use in a future procedure call, you can specify the parameter as OUTPUT:
CREATE PROCEDURE [usp_RotaDay1]
(
#Week_Begin datetime,
#Week_End datetime,
#1ShiftDate datetime,
#1Day nchar (10),
#RotaID int OUTPUT
)
AS
...
In each of your secondary stored procedures, you need to retrieve the primary key id using a select statement. You know weekbegin and weekend for all your secondary procedures right? so you know which row to pull to retrieve the id. Hope I made sense.
In SQL what is the best method to query a filtered data set?
I imagined two solutions and I would like to know what are the advantages and incovenients one and the other.
Solution 1
I create one unique procedure with my filter in parameters
CREATE PROCEDURE [dbo].[usp_GetByFilter]
(
-- Pagination
#p_Offset int,
#p_FetchNext int,
-- Filters
#p_Param1 nvarchar(255),
#p_param2 uniqueidentifier,
#p_param3 uniqueidentifier
)
Solution 2
I create a procedure by parameter
CREATE PROCEDURE [dbo].[usp_GetByParam1]
(
-- Pagination
#p_Offset int,
#p_FetchNext int,
-- Filters
#p_Param1 nvarchar(255)
)
CREATE PROCEDURE [dbo].[usp_GetByParam2]
(
-- Pagination
#p_Offset int,
#p_FetchNext int,
-- Filters
#p_param2 uniqueidentifier
)
CREATE PROCEDURE [dbo].[usp_GetByParam3]
(
-- Pagination
#p_Offset int,
#p_FetchNext int,
-- Filters
#p_param3 uniqueidentifier
)
Solution 3
Another way?
I think Solution 1 is the best one: it allows you to filter using one or more parameters: you can set a default value for your params, or pass null values when you do not want to filter by a certain parameter. Then the filter query could be written in this way:
SELECT
--your output
FROM
Table t
WHERE
--some conditions AND
( #p_Param1 is null OR t.column1 = #p_Param1 ) AND
( #p_Param2 is null OR t.column2 = #p_Param2 ) AND
( #p_Param3 is null OR t.column3 = #p_Param3 )
Solution 2 would require a lot of new procedures if you wanted to add more filter options or, for example, filter by parameters 2 and 3 at the same time.