Bulk Insert with Table Valued Parameter not working - sql

Need to insert multiple records into a SQL table. If there are duplicates (already inserted records) then I want to ignore them. For sending multiple records from my code to SQL, I am using table valued parameter.
Below is the query. It works during the first insertion when there are no rows in the table. On subsequent insert, no rows are added.
#tvpNewFMdata is the table valued parameter.
INSERT INTO
[dbo].[FMData]
(
[Id],
[Name],
[Path],
[CreatedDate],
[ModifiedDate]
)
SELECT
fm.Id, fm.Name, fm.Path, GETUTCDATE(), GETUTCDATE()
FROM
#tvpNewFMdata AS fm
WHERE
NOT EXISTS
(
SELECT
tbl.[Id]
FROM
[dbo].[FMdata] AS tbl
WHERE
tbl.Id = fm.Id
)
I am unable to locate as to what the cause that the first time insertion works but not the second time.
Tried with even removing the WHERE NOT EXISTS clause and it was only working in the first insertion. Subsequent insertion is not adding any rows to the table.

Is there an error, or just no rows inserted? If there are just no rows being inserted, even with the removed where clause, it sounds like you might be having problems populating the table value param. Have you watched what you are sending into SQL via a breakpoint or SQL Server Profiler?
Instead of where exists, have you tried a left join to fmData, and only inserting where the fmData row is null?
I agree with Damien, this is not bulk insert.

Sorry. My apologies. It was a mistake in my code. There was no issue in the SPROC. I was parameterizing the function and was passing the wrong table valued parameter.

Related

Is there a quick way to insert multiple rows into SQL Server when one of the values is going to be the same for each row

I'm writing a rollback script (we write them for every DB Update, JIC) to insert multiple rows back into a table. The table is made up of two IDs, a BatchID and a RegistrationID (See screenshot-Sorry for the link, this is the first question I've had to ask). As you can see one batch can have multiple registrations. Other than coding:
...VALUES (RegistrationID,BatchID1),(The Same RegistrationID, BatchID2),...(The Same RegistrationID,BatchIDN) manually, is there another way to do this?
Table view
You can use values and select:
insert into t (RegistrationID, BatchID)
select #RegistrationID, v.BatchID
from (values (#BatchID1), (#BatchID2), . . .
) v(BatchId);
Note: This assumes that your values are constants. Your values clause would not work otherwise (unless you are using a lateral join or correlated subquery).

Insert into select, target data unaffected

We have a simple query
INSERT INTO table2
SELECT *
FROM table1
WHERE condition;
I can read somewhere that to use INSERT INTO SELECT statement, the following condition must be fulfilled:
The existing records in the target table are unaffected
What does it mean?
INSERT is a SQL operations that add some new rows into your table, with not affect on the others. This is happening instead of UPDATE operations, that cand affect multiple rows from your table if you use a wrong WHERE Clause.

OUTPUT Clause in Sql Server (Transact-SQL)

I Know that OUTPUT Clause can be used in INSERT, UPDATE, DELETE, or MERGE statement. The results of an OUTPUT clause in a INSERT, UPDATE, DELETE, or MERGE statements can be stored into a target table.
But, when i run this query
select * from <Tablename> output
I didn't get any error. The query executed as like select * from tablename with out any error and with same no. of rows
So what is the exact use of output clause in select statement. If any then how it can be used?
I searched for the answer but i couldn't find a answer!!
The query in your question is in the same category of errors as the following (that I have also seen on this site)
SELECT *
FROM T1 NOLOCK
SELECT *
FROM T1
LOOP JOIN T2
ON X = Y
The first one just ends up aliasing T1 AS NOLOCK. The correct syntax for the hint would be (NOLOCK) or ideally WITH(NOLOCK).
The second one aliases T1 AS LOOP. To request a nested loops join the syntax would need to be INNER LOOP JOIN
Similarly in your question it just ends up applying the table alias of OUTPUT to your table.
None of OUTPUT, LOOP, NOLOCK are actually reversed keywords in TSQL so it is valid to use them as a table alias without needing to quote them, e.g. in square brackets.
OUTPUT clause return information about the rows affected by a statement. OUTPUT Clause is used along with INSERT, UPDATE, DELETE, or MERGE statements as you mentioned. The reason it is used is because these statements themselves just return the number of rows effected not the rows effected. Thus the usage of OUTPUT with INSERT, UPDATE, DELETE, or MERGE statements helps the user by returning actual rows effected.
SELECT statement itself returns the rows and SELECT doesn't effect any rows. Thus the usage of OUTPUT clause with SELECT is not required or supported. If you want to store the results of a SELECT statement into a target table use SELECT INTO or the standard INSERT along with the SELECT statement.
EDIT
I guess I misunderstood your question. AS #Martin Smith mentioned its is acting an alias in the SELECT statement you mentioned.
IF OBJECT_ID('tempdelete') IS NOT NULL DROP TABLE tempdelete
GO
IF OBJECT_ID('tempdb..#asd') IS NOT NULL DROP TABLE #asd
GO
CREATE TABLE tempdelete (
name NVARCHAR(100)
)
INSERT INTO tempdelete VALUES ('a'),('b'),('c')
--Creating empty temp table with the same columns as tempdelete
SELECT * INTO #asd FROM tempdelete WHERE 1 = 0
DELETE FROM tempdelete
OUTPUT deleted.* INTO #asd
SELECT * FROM #asd
This is how you can put all the deleted records in to a table. The problem with that is that you have to define the table with all the columns matching the table from which you are deleting. This is how i do it.

SCOPE_IDENTITY for multiple records

The below query inserts many records in a transaction. I want to fetch the newly created incremental identifier and use it in next INSERT statement
For a single record I can use like below
SELECT #new_emp_id= SCOPE_IDENTITY()
What about SCOPE_IDENTITY for multiple records? Or we can insert it into temp table and loop through it?
INSERT EmployeeBenifits(EmployeeId,BenifitID,StartdateTime,EndDateTime)
SELECT #new_emp_id,BenifitID,GetDate(),#PassedEndDate FROM Benifits
INSERT EmployeeBenifitDetails(EmpBenId,Desc)
SELECT EmpBenId,'Created Details' FROM #NewlyInsertedEmplBenifits
You should have a look at the OUTPUT clause:
INSERT INTO dbo.EmployeeBenefits(EmployeeId, BenefitID, StartdateTime, EndDateTime)
OUTPUT Inserted.EmployeeBenefitId, Inserted.EmployeeID, Inserted.BenefitID -- or whatever you want to return here
SELECT
#new_emp_id, BenefitID, GetDate(), #PassedEndDate
FROM
dbo.Benefits
Basically, the OUTPUT clause can return a result set of columns for each row that has been inserted (also works with the DELETE and UPDATE statements).
See MSDN documentation for more details
You can also send the OUTPUT rows to a temporary table (or table variable) to be further processed later on.

How to get last inserted row from a table?

I tried below query but results in more than one row and [SCOPE_IDENTITY] as NULL.What are the alternates?
SELECT TOP 1000
[RTID],xxx,xxx
FROM [RouteTiming]
GO
SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];
GO
SELECT ##IDENTITY AS [##IDENTITY];
GO
Depending on the server version (SQL Server 2005+) you can use the OUTPUT clause:
INSERT INTO tablename (column names)
OUTPUT --this is where you put your select statement to get returned IDs etc.
VALUES (values in here, or you can use a select statememt as per usual)
MSDN Article: OUTPUT Clause (Transact-SQL)
If you are inside a stored procedure or a function, you can use INSERTED table (there is also a DELETED table) which is stored in memory until the scope is completed.
Once you have performed your insert, you can join the inserted table just like any other as long as it is within the same scope. I believe the inserted table has been around since SQL Server 2000, but it is definitely in 2005+.
MSDN Examples: Use the inserted and deleted Tables