Insert multiple lines with a stored procedure - sql-server

With this stored procedure (SQL Server):
ALTER PROCEDURE [dbo].[fill_table1]
#CreatedBy nvarchar(max) = NULL
AS
BEGIN
SET NOCOUNT ON;
SELECT
[IdExchangeRate] = NEWID(),
[ExchangeRateCode] = 'EUR',
[ExchangeRatePeriodStartDate] = period,
[ExchangeRatePeriodEndDate] = EOMonth(period),
[ExchangeRateValue] = B.Value,
[CurrencyCode] = A.[Currency Code],
[CreatedBy] = 'string',
[CreatedAt] = GETUTCDATE()
FROM
[TempExchangeRates] AS A
CROSS APPLY
(SELECT
period = TRY_CONVERT(date),
Value = TRY_CONVERT(float, value)
......) B
END
I want to insert this output (+100rows) into another table (table2). I tried but it inserts just one row.

Thanks very much for #Sean Lange comment. It helped me so much.
I add that before select
INSERT INTO [dbo].[table2]
([column1]..
,[columnsn]
)

Related

How to use STRING_SPLIT() in a update query?

I want to update a table, One of its fields is a comma-separated value, I want to update that table, I tried different code but not working
UPDATE [PATS].[ReportSubscription]
SET [ScheduleID] = #ScheduleID
,[ReferenceID] = #ReferenceID
,[ReferenceType] = #ReferenceType
,[Schedule] = #Schedule
,[Day] = #Day
,[Time] = #Time
,[ProjectID] = #ProjectID
,[LastSentDate] = #LastSentDate
,[UserID] = #UserID
--,[CreatedBy] = #CreatedBy
--,[CreatedDate] = #CreatedDate
,[UpdatedBy] = #UpdatedBy
,[UpdatedDate] = #UpdatedDate
WHERE ID=#ID AND #Day IN (SELECT * FROM STRING_SPLIT(#Day,','))
I think this (SELECT * FROM STRING_SPLIT(#Day,',')) should be joined with the code.
You should be selecting value from the table returned by STRING_SPLIT:
WHERE ID=#ID AND [Day] IN (SELECT value FROM STRING_SPLIT(#Day,','))
Also, you should be comparing the table's column [Day] against the CSV list, not the variable #Day.
But, it is usually not a good idea to mix CSV data with SQL databases. Consider storing your CSV list of days in a separate table if possible.
Its possible that the data in the variable has space.
You should be selecting value from the table returned by STRING_SPLIT and triming value for use in condition, for example:
DECLARE #day NVARCHAR(500) = N'12 , 25, 41,54 ,89'
IF '41' IN (SELECT RTRIM(LTRIM(value)) FROM STRING_SPLIT(#Day,','))
PRINT('Ok')
ELSE
PRINT('Not Found')
-- printed : 'Ok'
Your query would be:
UPDATE [PATS].[ReportSubscription]
SET [ScheduleID] = #ScheduleID
,[ReferenceID] = #ReferenceID
,[ReferenceType] = #ReferenceType
,[Schedule] = #Schedule
,[Day] = #Day
,[Time] = #Time
,[ProjectID] = #ProjectID
,[LastSentDate] = #LastSentDate
,[UserID] = #UserID
,[CreatedBy] = #CreatedBy
,[CreatedDate] = #CreatedDate
,[UpdatedBy] = #UpdatedBy
,[UpdatedDate] = #UpdatedDate
WHERE ID=#ID AND Day IN (SELECT value FROM STRING_SPLIT(#Day,','))
Visit https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql?view=sql-server-2017 for more info.
Note: Make sure to trim your selection for unwanted spaces accordingly!

Historical Table in SQL Server

I have two tables; the first named PAYMENT and the second is a historical table named RecordPay.
I have two triggers, the first one is for insert in order to insert into the historical tables records from Payment table.
Here is the code:
ALTER TRIGGER [dbo].[INSERT_HIST]
ON [dbo].[PAYMENT]
FOR INSERT
AS
BEGIN
DECLARE #User_op varchar(50)
DECLARE #RGNO varchar(50)
DECLARE #PAYEUR varchar(50)
DECLARE #DATESYS SMALLDATETIME
DECLARE #RG_DATE SMALLDATETIME
DECLARE #RG_Montant varchar(50)
SELECT #User_op = cbUserName
FROM cbUserSession
WHERE cbSession = ##SPID
SELECT #PAYEUR = CT_NumPayeur FROM INSERTED
SELECT #DATESYS = GETDATE()
SELECT #RG_Montant = RG_Montant FROM INSERTED
SELECT #RG_DATE = RG_DATE FROM INSERTED
SELECT #RGNO = RG_No FROM INSERTED
INSERT INTO RecordPay (RG_NO, PAYEUR, CAISSIER, Montant, DATESYS, DATECAI)
VALUES (#RGNO, #PAYEUR, #user_op, #RG_Montant, #DATESYS, #RG_DATE)
This works well, my problem when I delete a row from PAYMENT, in RecordPay the record exists, and then when I insert another row in PAYMENT I had two RG_NO whith the same number.
For example I insert a row in PAYMENT with RG_NO=1 then I deleted, and I create another row with RG_NO=2, in the recordPay (historical table) i get two lines with RG_NO=1.
Here is the trigger for delete but it does not work
ALTER TRIGGER [dbo].[DEL_HIST]
ON [dbo].[PAYMENT]
AFTER DELETE
AS
BEGIN
DECLARE #User_op varchar(50)
DECLARE #RGNO varchar(50)
DECLARE #PAYEUR varchar(50)
DECLARE #DATESYS SMALLDATETIME
DECLARE #RG_DATE SMALLDATETIME
DECLARE #RG_Montant varchar(50)
SELECT #PAYEUR = CT_NumPayeur FROM DELETED
SELECT #RG_Montant = RG_Montant FROM DELETED
SELECT #RG_DATE = RG_DATE FROM DELETED
SELECT #RGNO = RG_No FROM DELETED
DELETE FROM RECORDPAY WHERE
RG_NO=#RGNO and PAYEUR= #PAYEUR and CAISSIER=#user_op and Montant=#RG_Montant
END
Your trigger will BREAK as soon as an INSERT statement inserts more than 1 row at a time - because in that case, your trigger gets called once for the INSERT statement, and Inserted will contain multiple rows.
Which one of those 10 rows are you selecting from here??
SELECT #PAYEUR = CT_NumPayeur FROM INSERTED
SELECT #RG_Montant = RG_Montant FROM INSERTED
SELECT #RG_DATE = RG_DATE FROM INSERTED
SELECT #RGNO = RG_No FROM INSERTED
It's arbitrary and non-deterministic - and you will simply ignore all other rows in Inserted.
You need to rewrite your trigger to take this into account:
ALTER TRIGGER [dbo].[INSERT_HIST]
ON [dbo].[PAYMENT]
FOR INSERT
AS
BEGIN
DECLARE #User_op varchar(50)
SELECT #User_op = cbUserName
FROM cbUserSession
WHERE cbSession = ##SPID
-- insert a record for ALL the rows that were inserted into
-- your history table in a single, elegant, set-based statement
INSERT INTO RecordPay (RG_NO, PAYEUR, CAISSIER, Montant, DATESYS, DATECAI)
SELECT
RG_No, CT_NumPayeur, #User_op, RG_Montant, SYSDATETIME(), RG_Date
FROM
Inserted

Returning Scope_Identity from 2 insert statements simultaneously in SQL Server

I am having problem with my stored procedure:
CREATE PROCEDURE [dbo].[Project]
#Code as nvarChar(255) = null,
#Id as nvarChar(255) = null,
#Status as nvarChar(max) = null,
#Project as nvarChar(max) = null,
#ClientSystem as nvarchar(max) = null,
#UserId as bigint = 0,
#ProjectId as bigint = 0,
#ProjectDetailsId bigint = 0 Output
AS
SET NOCOUNT OFF;
IF NOT EXISTS (SELECT [Code]
FROM [dbo].[Project]
WHERE Project.Code = #Code)
BEGIN
INSERT INTO [dbo].[Project]([Code], [Id], [Status], [Project])
VALUES(#Code, #Id, #Status, #Project)
SELECT #ProjectId = SCOPE_IDENTITY()
INSERT INTO [dbo].[ProjectDetails]([FK_ProjectId], [ClientSystem], [UserId])
VALUES(#ProjectId, #ClientSystem, #UserId)
SELECT #ProjectDetailsId = SCOPE_IDENTITY()
END
ELSE
BEGIN
SELECT [ProjectId] AS 'ProjectId'
FROM [dbo].[Project]
WHERE Project.Code = #Code
END
I want to return Scope_Identity from both Insert statements and pass the values of first insert as parameter to 2nd Insert and return the Scope_Identity of 2nd Insert statement also.
I am getting error is when I get the identity of first Insert, the identity in the specific table increases 2 times like in db table it will be inserted 2 but in coding it will return 1. And that return when i pass to other insert it s giving conflict.
Solution: Instead of using SCOPE IDENTITY(), you need to make use of he OUTPUTclause of the INSERT statement, like this:
INSERT INTO [dbo].[Project]([Code], [Id], [Status], [Project])
OUTPUT inserted.ID into #ProjectID
SELECT ...
Explanation: SCOPE_IDENTITY() returns the value of the last insert, regardless where the insert takes place. So, when when another insert is running in parallel, then your call to SCOPE_IDENTITY() will return the value from the other parallel running procedure. This then leads to an error.
However, the usage of the OUTPUT clause will guarantee to return the value from the current INSERT.
Here is an interesting article regarding SCOPE_IDENTITY and parallel plans:
http://blog.sqlauthority.com/2009/03/24/sql-server-2008-scope_identity-bug-with-multi-processor-parallel-plan-and-solution/
You need use OUTPUT clause at the procedure parameter
#ProjectId as bigint = 0 output,

Triggers not working when inserting data through OPEN XML in sql ser

I have created a trigger for a asset_verification. Whenever a new record is inserted in this table, the same record is inserted in the asset_verification_history table because of this trigger.
The trigger is as follows
Create trigger [dbo].[tr_insert_after_asset_verification] on [dbo].[asset_verification]
for insert
As
Begin
declare #verification_id int
declare #id int
declare #audit_id int
declare #date date
declare #status varchar(15)
declare #remarks varchar(200)
declare #creationDate datetime
declare #modificationDate datetime
declare #updatedBy int
declare #audit_action varchar(20)
Select #verification_id = i.verification_id from inserted i
If #verification_id IS NOT NULL
Begin
Select #id = i.id from inserted i
Select #audit_id = i.audit_id from inserted i
Select #date = i.date from inserted i
Select #status = i.status from inserted i
Select #remarks = i.remarks from inserted i
Select #creationDate = i.creationDate from inserted i
Select #modificationDate = i.modificationDate from inserted i
Select #updatedBy = i.updatedBy from inserted i
set #audit_action = 'Insert Record'
INSERT INTO [dbo].[asset_verification_history]
([verification_id]
,[id]
,[audit_id]
,[date]
,[status]
,[remarks]
,[creationDate]
,[modificationDate]
,[updatedBy]
,[audit_action])
VALUES
(#verification_id
,#id
,#audit_id
,#date
,#status
,#remarks
,#creationDate
,#modificationDate
,#updatedBy
,#audit_action)
End
End
When I insert the data in the asset_verification table using a procedure in which OPEN XML is used, then this trigger works only for the first record. For the rest of the records the trigger doesn't work
The procedure is as follows
Create procedure [dbo].[usp_AddVerificationBulkData]
(
#vXML XML
)
As
Begin
DECLARE #DocHandle INT
SET NOCOUNT ON
EXEC sp_xml_preparedocument #DocHandle OUTPUT, #vXML
Update asset_verification
set
audit_id = x.AuditId,
id = x.SerialId,
date = x.VerificationDate,
status = x.Status
,remarks = x.Remarks
,creationDate = x.CreatedOn
,modificationDate = x.ModifiedOn
,updatedBy = x.ModifiedBy
From
asset_verification a
Inner Join
OpenXml(#DocHandle,'/ArrayOfAssetVerificationModel/AssetVerificationModel',2)
With(SerialId int, AuditId int, VerificationDate datetime, Status int, Remarks varchar(200), CreatedOn datetime, ModifiedOn datetime, ModifiedBy int) x
On a.audit_id = x.AuditId where a.id = x.SerialId;
INSERT INTO [dbo].[asset_verification]
([id]
,[audit_id]
,[date]
,[status]
,[remarks]
,[creationDate]
,[modificationDate]
,[updatedBy])
select SerialId,AuditId,VerificationDate,Status,Remarks,CreatedOn,ModifiedOn,ModifiedBy from OpenXml(#DocHandle,'/ArrayOfAssetVerificationModel/AssetVerificationModel',2)
With(SerialId int, AuditId int, VerificationDate datetime, Status int, Remarks varchar(200), CreatedOn datetime, ModifiedOn datetime, ModifiedBy int) x
where SerialId NOT IN (select a.id from asset_verification a where a.audit_id = x.AuditId);
End
Problem:- How to make this trigger work for every record that is inserted through Open XML ?
You've made the classic mistake of thinking that triggers fire once-per-row. They dont, it's once-per-action, so the inserted pseudo table holds all the rows affected by the action. Your trigger needs to work in a set based manner, not row based. Try this;
CREATE TRIGGER [dbo].[tr_insert_after_asset_verification] ON [dbo].[asset_verification] FOR INSERT AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[asset_verification_history]
( [verification_id]
,[id]
,[audit_id]
,[date]
,[status]
,[remarks]
,[creationDate]
,[modificationDate]
,[updatedBy]
,[audit_action]
)
SELECT i.verification_id
,i.id
,i.audit_id
,i.date
,i.status
,i.remarks
,i.creationDate
,i.modificationDate
,i.updatedBy
,'Insert Record'
FROM inserted i
WHERE i.verification_id IS NOT NULL
END
As an aside, and strictly speaking, your original trigger will log one row, not necessarily the first.

Insert a row to SQL table

I am writing a store procedure in T-SQL which inserts a row to the table, based on parameters
#UserName ,#CompanyName ,#RestName,#Desc
INSERT INTO Orders(UserId,CompanyId,RestId)
SELECT UserNames.Id,CompanyNames.Id,RestNames.Id FROM UserNames,CompanyNames,RestNames
WHERE
UserNames.Name = #UserName AND
CompanyNames.Name = #CompanyName AND
RestNames.Name = #RestName
Besides the insert to the 3 columns above,I also want to insert the #Desc value.
I tried :
INSERT INTO Orders(UserId,CompanyId,RestId,Desc)
VALUES(
(SELECT UserNames.Id,CompanyNames.Id,RestNames.Id FROM UserNames,CompanyNames,RestNames
WHERE
UserNames.Name = #UserName AND
CompanyNames.Name = #CompanyName AND
RestNames.Name = #RestName),#Desc)
Only one expression can be specified in the select list when the subquery is not introduced with EXISTSt-
It doesn`t work giving the following error:
#UserName ,#CompanyName ,#RestName,#Desc
INSERT INTO Orders(UserId,CompanyId,RestId, Desc_Column)
SELECT UserNames.Id,CompanyNames.Id,RestNames.Id , #Desc --<-- Just SELECT that variable
FROM UserNames,CompanyNames,RestNames -- in your select statement.
WHERE UserNames.Name = #UserName
AND CompanyNames.Name = #CompanyName
AND RestNames.Name = #RestName
Retrieve ID Values Inserted
DECLARE #t TABLE (ID INT); --<-- Declare a table variable
INSERT INTO Orders(UserId,CompanyId,RestId, Desc_Column)
OUTPUT Inserted.ID INTO #t --<-- use OUTPUT, get values from INSERTED Table
SELECT UserNames.Id,CompanyNames.Id,RestNames.Id , #Desc --and insert them into your table variable
FROM UserNames,CompanyNames,RestNames
WHERE UserNames.Name = #UserName
AND CompanyNames.Name = #CompanyName
AND RestNames.Name = #RestName
/*At last just simply select from that table variable to get the inserted IDs*/
SELECT * FROM #t

Resources