INSERT SELECT and IGNORE_DUP_KEY, how can I retrieve discarded rows? - sql

Using MS-SQL, I have an INSERT ... SELECT statement that populates a table that has a unique key with IGNORE_DUP_KEY = ON.
Is there an easy way I could get the rows that were discarded because they were duplicated? (Preferably after the statement has completed)

The OUTPUT clause is the key here.
create table dbo.IDKsource (
SNumber int not null,
SText varchar(100) not null
) ;
go
insert into dbo.IDKsource
values ( 1, 'aaaaa' ), ( 2, 'bbbbb' ), ( 1, 'cccccc' ), ( 3, 'dddddd' ) ;
go
create table dbo.IDKOntarget (
SNumber int not null unique with ( ignore_dup_key = on ),
SText varchar(100) not null
) ;
go
-- The following lines must all be in one batch!
declare #RecordsWereInserted table (
SNumber int not null ,
SText varchar(100) not null
) ;
insert into dbo.IDKOntarget ( SNumber , SText )
output inserted.* into #RecordsWereInserted
select SNumber , SText
from dbo.IDKSource ;
select SNumber , SText
from dbo.IDKsource
except
select SNumber , SText
from #RecordsWereInserted ;
If you want to keep the inserted data for longer than one batch (or your version of SQL Server does not support table variables) then replace my table variable RecordsWereInserted with an actual table.
Note: My first approach was to use the INSERT statement directly with the EXCEPT but SQL Server will not allow DML statements with EXCEPT, INTERSECT or UNION.

Related

Is it possible to insert multiple values using insert in sql

Is it possible to insert in one single query multiple values into a table ? .
I have declared this table
declare global temporary table CFVariables
(
CF varchar(255)
)
with replace ;
then i inserted values into the table
INSERT INTO qtemp.CFVariables ( CF ) VALUES
('F01' ), ('T01' ), ('U01' ), ('CIP' ), ('L01' )
Is it possible to not insert the values in qtemp.CFVariables table this way ? but like In ('F01' , 'T01' , 'U01' , 'CIP' , 'L01' )
Then , i declared my second table :
declare global temporary table xVariables
(
CFC numeric(3),
CF varchar(255)
)
with replace ;
In this part i'm having a problem to insert into my table xVariables
I tried to use this to insert multiple values
INSERT INTO qtemp.xVariables ( CFC, CF ) VALUES
( 1, (select CF from qtemp.CFVariables ))
My query field because i'm inserting more then one row to the table .
How can i achieve this ?
Try
INSERT INTO qtemp.xVariables ( CFC, CF ) SELECT 1 AS CFC,CF from qtemp.CFVariables;
Try running an insert-select:
INSERT INTO qtemp.xVariables ( CFC, CF )
select 1, CF from qtemp.CFVariables
To restrict the records to be inserted, you will need to do something like this:
INSERT INTO qtemp.xVariables ( CFC, CF )
select 1, CF
from qtemp.CFVariables
where CF in ('F01' , 'T01' , 'U01' , 'CIP' , 'L01' )

Insert is not working with Select from OLD TABLE in DB2

DECLARE GLOBAL TEMPORARY TABLE
SESSION.TABLE1
(
PHYSCL_OBJ_ID BIGINT
,ID INT
)WITH REPLACE
ON COMMIT PRESERVE ROWS NOT LOGGED;
DECLARE GLOBAL TEMPORARY TABLE
SESSION.TABLE2
(
PHYSCL_OBJ_ID BIGINT
,ID INT
)WITH REPLACE
ON COMMIT PRESERVE ROWS NOT LOGGED;
INSERT INTO SESSION.TABLE1 VALUES (1,1),(2,2),(3,3);
INSERT INTO SESSION.TABLE2
(
PHYSCL_OBJ_ID
)
SELECT PHYSCL_OBJ_ID
FROM OLD TABLE
(
DELETE FROM SESSION.TABLE1 GTT WHERE GTT.PHYSCL_OBJ_ID IN (1,2)
);
INSERT INTO is not can anyone explain and help?
Below is the error message:
>[Error] Script lines: 1-7 --------------------------
DB2 SQL Error: SQLCODE=-20165, SQLSTATE=428FL, SQLERRMC=null, DRIVER=3.68.61
Try this:
WITH D AS
(
SELECT PHYSCL_OBJ_ID
FROM OLD TABLE
(
DELETE FROM SESSION.TABLE1 GTT WHERE GTT.PHYSCL_OBJ_ID IN (1,2)
)
)
SELECT COUNT(1)
FROM NEW TABLE
(
INSERT INTO SESSION.TABLE2 (PHYSCL_OBJ_ID)
SELECT PHYSCL_OBJ_ID FROM D
);

Using the identity column to add a value to a computed column

At times I need to store a temporary value to a field. I have a stored procedure that adds it using:
Insert new record first then
SELECT #Record_Value = SCOPE_IDENTITY();
UPDATE ADMIN_Publication_JSON
SET NonPubID = CAST(#Record_Value as nvarchar(20)) + '_tmp'
WHERE RecID = #Record_Value
It simply takes the identity value and adds an '_tmp' to the end. Is there a way that I can create a default value in the table that would do that automatically if I did not insert a value into that field?
The NonPubID column is just a NVARCHAR(50).
Thanks
You could write a trigger, that replaces NULL with that string upon INSERT.
CREATE TRIGGER admin_publication_json_bi
ON admin_publication_json
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE apj
SET apj.nonpubid = concat(convert(varchar(20), i.id), '_tmp')
FROM admin_publication_json apj
INNER JOIN inserted i
ON i.id = apj.id
WHERE i.nonpubid IS NULL;
END;
db<>fiddle
Downside: You cannot explicitly insert NULLs for that column, should that be desired.
Check out NewKey col below:
CREATE TABLE #Table
(
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
IDValue VARCHAR(1) ,
ModifiedDT DATETIME NULL,
NewKey AS ( CONVERT(VARCHAR(100),ID)+'_Tmp' )
)
INSERT #Table( IDValue, ModifiedDT )
SELECT 'A', GETDATE()
UNION ALL
SELECT 'Y', GETDATE() - 1
UNION ALL
SELECT 'N', GETDATE() - 5
SELECT * FROM #Table

sql procedure auto iincrement if exist

procedure [dbo].[InsertSortCode] (
#Sortcode varchar(25)
, #verbiage varchar(200) )
as
begin
SET IDENTITY_INSERT appmaster on
insert into AppMaster(MainID,SortCode) values (
(select MAX(mainid)
from AppMaster) + 1, #Sortcode )
SET IDENTITY_INSERT appmaster off
insert into Verbiage(MenueID,verbiage) values (
(select MAX(mainid)
from AppMaster), #verbiage )
the above is my stored procedure it is working perfectly fine but there is an error if there is no data in the database
the problem is with max function ,if the is no data in database
it cannot max out the id it works only if there is already some data
with its id in that databse
need to figure out how to use the exist statement on this SP so that it can work with both empty table and filled table
You can try following query:-
insert into AppMaster(MainID,SortCode) values (
(select ISNULL(MAX(mainid),0)
from AppMaster) + 1, #Sortcode )
So if MAX(mainid) is null it will return as 0 and add 1 to that.

Why SQL Server Optimizer do not use CHECK constraint definitions to find which table contains the rows?

I use SQL Server 2012 and I have a large table and I divided my table in some tables like below :
Create Table A2013
(
Id int identity(1,1),
CountA int ,
Name varchar(50),
ADate DATETIME NULL
CHECK (DATEPART(yy, ADate) = 2013)
)
Create Table A2014
(
Id int identity(1,1),
CountA int ,
Name varchar(50),
ADate DATETIME NULL
CHECK (DATEPART(yy, ADate) = 2014)
)
Insert Into A2013 Values ( 102 , 'A','20131011' )
Insert Into A2013 Values (15 , 'B' ,'20130211' )
Insert Into A2013 Values ( 54, 'C' ,'20131211' )
Insert Into A2013 Values ( 54, 'D' ,'20130611' )
Insert Into A2013 Values ( 95, 'E' ,'20130711' )
Insert Into A2013 Values (8754 , 'F' ,'20130310' )
Insert Into A2014 Values ( 102 , 'A','20141011' )
Insert Into A2014 Values (15 , 'B' ,'20140911' )
Insert Into A2014 Values ( 54, 'C' ,'20140711' )
Insert Into A2014 Values ( 54, 'D' ,'20141007' )
Insert Into A2014 Values ( 95, 'E' ,'20140411' )
Insert Into A2014 Values (8754 , 'F' ,'20140611' )
I created a partition view like below:
Create View A
As
Select * From A2013
Union
Select * From A2014
I hope SQL Optimizer use a good plan and use my CHECK constraint definitions to determine which member table contains the rows but it scan two table when run this query :
Select * From A Where A.ADate = '20140611'
I expected that SQL Optimiser do not use table A2013?!?
The CHECK CONSTRAINT expression must be sargable in order for the optimizer to eliminate the unneeded tables in the execution plan. The constraints below avoid applying a function to the column and are sargable:
CREATE TABLE dbo.A2013
(
Id int IDENTITY(1, 1)
, CountA int
, Name varchar(50)
, ADate datetime NULL
CONSTRAINT CK_A2013_ADate
CHECK ( ADate >= '20130101'
AND ADate < '20140101' )
);
CREATE TABLE dbo.A2014
(
Id int IDENTITY(1, 1)
, CountA int
, Name varchar(50)
, ADate datetime NULL
CONSTRAINT CK_A2014_ADate
CHECK ( ADate >= '20140101'
AND ADate < '20150101' )
);
The issue is not whether the expression is sargable. As far as I know, the term "sargable" applies to the use of indexes in queries. The question is whether SQL Server recognizes the where clause as matching the check constraint.
The check constraint you have is:
CHECK (DATEPART(yy, ADate) = 2014)
The where clause is:
Where A.ADate = '20140611'
The problem is that the second is not recognized as a subset of the first. You could fix this by adding redundancy:
Where A.ADate = '20140611' and DATEPART(yy, A.ADate) = 2014
Or, you could fix this by using ranges -- but be careful about data types, because data type conversion can definitely confuse the optimizer. I think the following will work:
CHECK ADate BETWEEN '2014-01-01' and '2014-12-31'
WHERE A.ADate = '2014-06-11'
(The hyphens are optional and can be dropped.)
The documentation (as far as I can tell) is not really explicit about the cause:
The SQL Server query optimizer recognizes that the search condition in
this SELECT statement references only rows in the May1998Sales and
Jun1998Sales tables. Therefore, it limits its search to those tables.
. . .
CHECK constraints are not needed for the partitioned view to return
the correct results. However, if the CHECK constraints have not been
defined, the query optimizer must search all the tables instead of
only those that cover the search condition on the partitioning column.
Without the CHECK constraints, the view operates like any other view
with UNION ALL. The query optimizer cannot make any assumptions about
the values stored in different tables and it cannot skip searching the
tables that participate in the view definition.