I have a SSIS workflow where I fill a SQL Server table (260 data rows). In this SSIS workflow I have also implemented a lookup that checks whether the original row has changed. If so it should update it in the SQL Server table via a stored procedure.
This is my stored procedure:
ALTER PROCEDURE [dbo].[Update_MC_Countries]
#alphacode3char NVARCHAR(10),
#alphacode NVARCHAR(10),
#numcode NVARCHAR(10),
#german NVARCHAR(150),
#english NVARCHAR(150),
#region NVARCHAR(10),
#qmr NVARCHAR(10)
AS
BEGIN
SET NOCOUNT ON;
UPDATE [dbo].[MasterData_EDL_MC_Countries]
SET [alphacode3char] = #alphacode3char,
[alphacode] = #alphacode,
[numcode] = #numcode,
[german] = #german,
[english] = #english,
[region] = #region,
[qmr] = #qmr
I get this error:
An OLE DB record is available.
Source: "Microsoft SQL Server Native Client 11.0"
Hresult: 0x80040E2F
Description: "Violation of PRIMARY KEY constraint 'PK_MasterData_EDL_MC_Countries'. Cannot insert duplicate key in object 'dbo.MasterData_EDL_MC_Countries'. The duplicate key value is (DEU).".
Why does the code want to insert this row instead of updating it?
I don't get it. Any help is appreciated!
It's difficult to tell without seeing the dataset being updated, but perhaps you are updating a row with when there is already a row with as its primary key? I would question why you would be updating a unique key also, this would be a PK conflict. Could you show the Table Definition?
Related
I have hit a problem with SQL Server that results in it infinitely recompiling a function.
To reproduce, create a new database with the option Parameterization = Forced or execute the following on an existing DB:
ALTER DATABASE [DatabaseName] SET PARAMETERIZATION FORCED WITH NO_WAIT
Then execute the following script:
CREATE TABLE dbo.TestTable(
ID int IDENTITY(1,1) NOT NULL,
FullTextField varchar(100) NULL,
CONSTRAINT PK_TestTable PRIMARY KEY CLUSTERED
(ID ASC)
)
GO
IF NOT EXISTS(SELECT 1 FROM sysfulltextcatalogs WHERE name = 'FullTextCat')
CREATE FULLTEXT CATALOG FullTextCat;
GO
CREATE FULLTEXT INDEX ON dbo.TestTable (FullTextField) KEY INDEX PK_TestTable
ON FullTextCat
WITH
CHANGE_TRACKING AUTO
GO
CREATE OR ALTER FUNCTION dbo.fn_TestFullTextSearch(#Filter VARCHAR(8000))
RETURNS TABLE
AS
RETURN SELECT
ID,
FullTextField
FROM dbo.TestTable
WHERE CONTAINS(FullTextField, #Filter)
GO
SELECT * FROM dbo.fn_TestFullTextSearch('"a*"')
The query will never return. Running SQL Profiler to monitor SP:CacheInsert and SP:CacheRemove will show SQL server is doing this endlessly and the SQL logs will show countless "A possible infinite recompile was detected for SQLHANDLE" messages.
Setting the Parameterization = Simple works around the issue but we need this to be set to Forced for other reasons.
Has anyone come across this issue before and/or have a suggested solution?
Thanks,
Chuck
While I still experience the problem with the original code I provided, by following #Martin's approach of explicitly parameterizing the call to the function:
EXEC sys.sp_executesql N'SELECT * FROM dbo.fn_TestFullTextSearch(#Filter)', N'#Filter VARCHAR(4)', #Filter = '"a*"'
I have been able to successfully work around the problem.
I have a database creation script that sets up tables, stored procedures, views, etc. When I change the type of a column in a create table statement, I want this change to be reflected in the create stored procedures / views / etc statements that reference that table without having to go through and manually change each one.
In other words I want my stored procedures to automatically determine the column type based on another column's type on creation. I don't need this to work on a live database with data, just while I'm iterating over the design and prototyping.
Something like a TYPE_OF() in this (fictional) example:
create table Logs
(
id int identity(1, 1) primary key,
userName varchar(32),
logType int foreign key references LogType(id),
description varchar(128),
datestamp datetime
);
go
create procedure WriteLog
(
#userName TYPE_OF(Logs.userName), -- should be varchar(32),
#logType int,
#description TYPE_OF(Logs.description) -- should be varchar(128)
)
as
begin
insert into Logs
values(#userName, #logType, #description, SYSDATETIME());
end
go;
I think I remember something similar from Oracle / SQL Plus / PLSQL but I am having trouble finding it.
I'm using SQL Server Management Studio v18.4
Not sure if the TYPEOF feature you're looking for exitsts, but you could try and use a DDL Trigger to keep your procedure in sync with the column type changes.
This trigger would get fired every time a table is altered and you'd just have to parse the EVENTDATA() to see if the column types in the Logs table have changed. The body of your trigger would look something like this:
CREATE TRIGGER OnLogsChanged
ON DATABASE
FOR ALTER_TABLE
AS
BEGIN
-- 1. Parse EVENTDATA() to see if the Logs table was altered
-- 2. If it has, store the definition of the WriteLog procedure into a variable by reading it from sys.procedures
-- 3. Read the new types for the columns of the Logs table from sys.all_columns
-- 4. replace the parameter declarations in the procedure definition to match the new types in the Logs table
-- 5. alter the procedure with the new definition by building up the ALTER PROCEDURE statement as a string and executing it with sp_executesql
END
As long as the trigger stays enabled your procedure should stay in sync with the table column types.
I am currently working on getting a set of records from a view in the Oracle database and trying to insert/update them in to the table in the SQL Server table depending on a column using BizTalk.
For this I created a stored procedure:
Create PROCEDURE [dbo].[uspInsertorUpdateDepartment]
#dept_name varchar(64),
#jax_dept_id char(32)
AS
BEGIN
SET NOCOUNT ON;
IF (SELECT TOP (1) 1 FROM afm.[jax_dept]
WHERE jax_dept_id = #jax_dept_id) IS NULL
INSERT INTO afm.[jax_dept](dept_name, jax_dept_id)
VALUES (#dept_name,#jax_dept_id)
ELSE
UPDATE afm.[jax_dept]
SET dept_name = #dept_name
WHERE jax_dept_id = #jax_dept_id
END
I created the schema for the stored procedure using consume adapter service. Used them in the mapping and the orchestration. Though I was not able to use the lopping functoid in the mapping
So removed the lopping and deployed the application. And tried to run and it ran without any error but just insert the first record from the oracle view in to the SQL Server database leaving all the other records. How can this be approached so the entire set of records from the oracle is inserted/updated in to SQL Server database.
Here I converted the separate update and insert into one merge statement:
Create PROCEDURE [dbo].[uspInsertorUpdateDepartment]
#dept_name varchar(64),
#jax_dept_id char(32)
AS
BEGIN
SET NOCOUNT ON;
merge afm.[jax_dept] as target
using (select #dept_name as dept_name, #jax_dept_id as jax_dept_id) as source
on source.jax_dept_id = target.jax_dept_id
when matched then
update target
SET dept_name = #dept_name
when not matched then
insert (dept_name, jax_dept_id)
values (#dept_name,#jax_dept_id)
;
END
Use table type as a parameter for the SP, instead of passing individually. We can
use looping functoid if we use User Defined Table value as a parameter.
CREATE TYPE dbo.SampleType AS TABLE
(
dept_name varchar(64) not null,
jax_dept_id char(32) not null
)
---
Create PROCEDURE [dbo].[uspInsertorUpdateDepartment]
#TVP dbo.SampleType READONLY
AS
BEGIN
SET NOCOUNT ON;
--your insert or update query
For more infor on how to use table value parameter check out this link:-
https://learn.microsoft.com/en-us/sql/relational-databases/tables/use-table-valued-parameters-database-engine
I am dealing with this problem: I would like to have autogenerated identity in my table which is of type int
But, I would like to be able to explicitly set the identity. Now the real challenge is that this stuff is going through Entity Framework. I have my database with a IDENTITY(1,1) column, and IDENTITY_INSERT set to ON.
And whenever the Id is 0 (not specified) in newly created object, it inserts the very same 0. Any help appreciated, except offers to reconsider architecture (I will do that in any other case if this attempt fails).
And all this must work either on SQL CE, and SQL Server.
If you tell EF the primary key is database generated then it will not pass the id to the insert sql. You need to pass the ID so go with DatabaseGenerated.None.
But you want it to be an IDENTITY, so make it one in a migration script. You could change the CREATETABLE statement, adding identity: true to the column specification, or you can modify the table by running sql using the Sql() method
Now you need to modify the actual sql run during insert. The only way to do that is configure your model to use stored procedures then modify the sql generated in the Up migration for the insert procedures:
CREATE PROCEDURE [dbo].[My_Insert]
#Id int,
--ETC
AS
BEGIN
IF(Id > 0) SET IDENTITY_INSERT ON
INSERT --ETC
IF(Id > 0) THEN BEGIN
SET IDENTITY_INSERT OFF
SELECT Id
ELSE
SELECT SCOPE_IDENTITY() AS Id
END
END
I have three tables and three stored procedures respectively to insert/update records in these tables. The first table has a primary key column, RecNo, which is auto-generated.
Around 1000 users are entering records in these tables simultaneously from different geographic locations. I am noticing that sometimes inserts in the second or third table get missed even when inserts were successfully done and no warning was generated.
I want to know how the auto-generated primary key column handles concurrent issues. Do I need to set isolation level to SERIALIZABLE on top of each stored procedure?
I am using SQL Server 2008 R2 Express with default isolation level, i.e., READ COMMITTED.
One of my stored procedure looks like:
ALTER PROCEDURE [dbo].[pheSch_CreateOrUpdateTubewellDetails]
-- Add the parameters for the stored procedure here
#TwTaskFlag nvarchar(6),
#TwParameterID bigint,
#SerialNumber bigint,
#TotalNum int,
#TwType nvarchar(50),
#Depth nvarchar(60),
#Diameter nvarchar(60),
#WaterCapacity nvarchar(60),
#PS nvarchar(15),
#PSNum int,
#PSType nvarchar(60),
#Remarks nvarchar(80)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
BEGIN
UPDATE tw_details
SET
TotalNum = #TotalNum,
TwType = #TwType,
Depth = #Depth,
Diameter = #Diameter,
WaterCapacity = #WaterCapacity,
PS = #PS,
PSNum = #PSNum,
PSType = #PSType,
Remarks = #Remarks
WHERE twpid = #TwParameterID;
END
END
You need not change the isolation level,
the Identity column is well suited for concurrent inserts.
IF
you have no any Triggers attached to the table - then show all the details
BUT
you noticed the INSERTS - i do not see any of them here