Insert into table if record does not already exist - sql-server

For some reason, this is giving me the "cannot insert duplicate record into table" error.
INSERT INTO [DMS].[dbo].[Deductions]
(
CustomerID,
DeductionCode,
DeductionDescription
)
SELECT b.CustomerID,
b.AdjustmentReason,
b.AdjustmentReason
FROM #CreditDebitAdjustmentDetail b
WHERE NOT EXISTS ( SELECT 1
FROM [DMS].[dbo].[Deductions]
WHERE CustomerID = b.CustomerID
AND DeductionCode = b.AdjustmentReason )
The weird thing is, I tested it as such:
DECLARE #CreditDebitAdjustmentDetail TABLE
(
CustomerID INT,
AdjustmentReason VARCHAR(50)
)
INSERT INTO #CreditDebitAdjustmentDetail
(CustomerID, AdjustmentReason)
VALUES (143, -- CustomerID - int
'024' -- AdjustmentReason - varchar(50)
)
INSERT INTO [DMS].[dbo].[Deductions]
(
CustomerID,
DeductionCode,
DeductionDescription
)
SELECT b.CustomerID,
b.AdjustmentReason,
b.AdjustmentReason
FROM #CreditDebitAdjustmentDetail b
WHERE NOT EXISTS ( SELECT 1
FROM [DMS].[dbo].[Deductions]
WHERE CustomerID = b.CustomerID
AND DeductionCode = b.AdjustmentReason )
And it DOES NOT insert into the table because the record already exists.
Am I missing something here?
EDIT - I thought I had fixed it by doing this but I'm still getting the same error:
INSERT INTO [DMS].[dbo].[Deductions]
(
CustomerID,
DeductionCode,
DeductionDescription
)
SELECT a.CustomerID,
a.AdjustmentReason,
a.AdjustmentReason
FROM #CreditDebitAdjustmentDetail a
WHERE NOT EXISTS ( SELECT 1
FROM [DMS].[dbo].[Deductions] b
WHERE a.CustomerID = b.CustomerID
AND a.AdjustmentReason = b.DeductionCode )

I figured it out, DOH!
KEYWORD ... DISTINCT -_-
INSERT INTO [DMS].[dbo].[Deductions]
(
CustomerID,
DeductionCode,
DeductionDescription
)
SELECT DISTINCT
a.CustomerID,
ISNULL(a.AdjustmentReason, 'UNKNOWN'),
ISNULL(a.AdjustmentReason, 'UNKNOWN')
FROM #CreditDebitAdjustmentDetail a
WHERE NOT EXISTS ( SELECT 1
FROM [DMS].[dbo].[Deductions] b
WHERE a.CustomerID = b.CustomerID
AND CASE a.AdjustmentReason
WHEN NULL THEN 'UNKNOWN'
WHEN '' THEN 'UNKNOWN'
END = b.DeductionCode )

Related

What is the optimal way to get only latest ID's from table in SQL

I'm trying to get only a single row per Appointment Number in a table storing a history of appointments. It works fine with a few rows but then gets slower? Is this the best way to do this kind of check and I'm just missing some indexes or is there a better way?
DECLARE #temptable TABLE
(
id INT PRIMARY KEY NOT NULL
, ApptNumber INT NOT NULL
, ApptDate DATE NOT NULL
, Notes VARCHAR(50) NULL
)
INSERT INTO #temptable VALUES (1,1,'01-DEC-2018','First Appointment')
INSERT INTO #temptable VALUES (2,1,'01-DEC-2018','')
INSERT INTO #temptable VALUES (3,1,'01-DEC-2018','Rescheduled')
INSERT INTO #temptable VALUES (4,2,'02-DEC-2018','Second Appointment')
INSERT INTO #temptable VALUES (5,2,'02-DEC-2018','Cancelled')
INSERT INTO #temptable VALUES (6,3,'03-DEC-2018','Third Appointment')
INSERT INTO #temptable VALUES (7,4,'04-DEC-2018','Fourth Appointment')
SELECT * FROM #temptable
SELECT MAX(id) FROM #temptable GROUP BY ApptNumber
SELECT tt.* FROM #temptable tt
INNER JOIN (SELECT MAX(id) [Id] FROM #temptable GROUP BY ApptNumber) appts ON appts.Id = tt.id
Solution 1:
select * from (
SELECT f1.*, row_number() over(partition by ApptNumber order by id desc ) rang FROM #temptable f1
) tmp where rang=1
Solution 2:
with tmp as (
select ApptNumber, max(ID) MaxID
from #temptable
group by ApptNumber
)
select f1.* from #temptable f1 inner join tmp f2 on f1.ID=f2.MaxID
Solution 3:
select distinct f3.* from #temptable f1
cross apply
(
select top 1 * from #temptable f2
where f1.ApptNumber=f2.ApptNumber
order by f2.ID desc
) f3
Window function
SELECT tt.*
FROM (
SELECT *, row_number() over (partition by ApptNumber order by id desc) as rn
) tt
where tt.rn = 1

Fill in Missing rows using T-SQL

I am trying to fill in missing values. A simple example is table A has 10 rows with Id's of 1 through 10.
Table B has 5 rows of 1,3,5,7,9
I need to use table A to fill in the missing even numbers from Table B.
What is the best way to go about this?
CREATE TABLE #A (
Id INT
,Val VARCHAR(10)
)
CREATE TABLE #B (
Id INT
,Val VARCHAR(10)
)
INSERT INTO #A (
Id
,Val
)
VALUES (
1
,'1'
)
INSERT INTO #A (
Id
,Val
)
VALUES (
2
,'2'
)
INSERT INTO #A (
Id
,Val
)
VALUES (
3
,'3'
)
INSERT INTO #B (
Id
,Val
)
VALUES (
1
,'1'
)
INSERT INTO #B (
Id
,Val
)
SELECT Id
,Val
FROM #A a
WHERE NOT EXISTS (
SELECT NULL
FROM #B b
WHERE b.Id = a.Id
)
AND a.Id % 2 = 0
Select * from #B
Run a select query towards table A with a for loop that compares I’d for each loop. If I’d don’t match execute an insert. If you want to run it inside DB make an SP for it otherwise create a simple console app or such.
You can use INSERT INTO ... SELECT...
INSERT INTO A
SELECT B.ID FROM B
WHERE B.ID % 2 = 0
AND B.ID NOT IN (SELECT ID FROM A)
Just make sure to SELECT the same columns that you are inserting.
You can do using not exists like below :
insert into B
select ID from A
where ID % 2 = 0 and not exists(select 1 from B where ID = A.ID)
SQL HERE

how to change effectively the In clause in SQL

we have a employee_master table and a Report_Filter Table, the Report_Filter contains different typs of filter the user enters for eg. status, employee code, nationaality etc. and this report_filter contains the user Information also. The table structure is like this Report_Filter( User_Id, Report_TYpe, Report_Code ) all are character fields. based on the users input the table will have values like
( 'User_1', 'STATUS', '01' )
( 'user_1', 'STATUS', '02' )
( 'User_1', 'EMP_CODE', 'ABC' )
( 'User_1', 'NATIONALITY', 'ALL' ) -- All means he want to see all the nationalities
now currently we are writing the query like this
Select Emp_code, Emp_Name, status, nationlity
From Empolyee_Master m
Where
('All' in ( select report_code from Report_Filter where user_id = #user_id and report_type='STATUS') or m.STATUS in ( select report_code from Report_Filter where user_id = #user_id and report_type='STATUS') ) and
or m.CATEGORY in ( select report_code from Report_Filter where user_id = #user_id and report_type='NATIONALITY') ) and
or m.emp_code in ( select report_code from Report_Filter where user_id = #user_id and report_type='EMP_CODE') )
there are other conditions also we are using, the problem is its take too much time because of the In clauses. how can we effectively change this code for improving performance
we are using MSSQL 2014
Instead of retrieving all values and then seeing if your value is part of it, you should filter by your value. Try if this helps (just one example):
replace
WHERE 'ALL' in (
select report_code from Report_Filter
where user_id = #user_id and report_type='STATUS'
)
with
WHERE EXISTS (
select 1 from Report_Filter
where user_id = #user_id and report_type='STATUS'
and report_code = 'ALL'
)
Now it would also help to include column report_code in an INDEX (you are using INDEXes, I hope?)
Update - to skip filtering in case of 'ALL', that can be done like this:
DECLARE #statusFilter VARCHAR(20) = ... -- 'ALL' or other value
SELECT ...
WHERE #statusFilter = 'ALL'
OR EXISTS (
select 1 from Report_Filter
where user_id = #user_id and report_type='STATUS'
and report_code = #statusFilter
)
You can try to do the filtering in advance:
(For your next question: Such a mockup should be done by you)
DECLARE #ReportFilter TABLE([user_id] VARCHAR(100),report_type VARCHAR(100),report_code VARCHAR(100));
INSERT INTO #ReportFilter VALUES
( 'User_1', 'STATUS', '01' )
,( 'user_1', 'STATUS', '02' )
,( 'User_1', 'EMP_CODE', 'ABC' )
,( 'User_1', 'NATIONALITY', 'ALL' );
DECLARE #Employee_Master TABLE(Emp_Code VARCHAR(100),Emp_Name VARCHAR(100), [status] VARCHAR(100), nationality VARCHAR(100));
INSERT INTO #Employee_Master VALUES
('ABC','test1','01','USA')
,('DEF','test2','99','GB')
,('ABC','test3','02','DE')
,('XYZ','test4','01','FRA');
--This is part of your query
DECLARE #user_id VARCHAR(100)='User_1';
WITH filterSTATUS AS
(
SELECT * FROM #ReportFilter WHERE [user_id]=#user_id AND report_type='STATUS'
AND NOT EXISTS(SELECT 1 FROM #ReportFilter WHERE [user_id]=#user_id AND report_type='STATUS' AND report_code='ALL')
)
,
filterEMPCODE AS
(
SELECT * FROM #ReportFilter WHERE [user_id]=#user_id AND report_type='EMP_CODE'
AND NOT EXISTS(SELECT 1 FROM #ReportFilter WHERE [user_id]=#user_id AND report_type='EMP_CODE' AND report_code='ALL')
)
,filterNATIONALITY AS
(
SELECT * FROM #ReportFilter WHERE [user_id]=#user_id AND report_type='NATIONALITY'
AND NOT EXISTS(SELECT 1 FROM #ReportFilter WHERE [user_id]=#user_id AND report_type='NATIONALITY' AND report_code='ALL')
)
SELECT *
FROM #Employee_Master AS em
WHERE (SELECT COUNT(*) FROM filterSTATUS)=0 OR em.[status] IN (SELECT x.report_code FROM filterSTATUS AS x)
AND (SELECT COUNT(*) FROM filterEMPCODE)=0 OR em.Emp_Code IN (SELECT x.report_code FROM filterEMPCODE AS x)
AND (SELECT COUNT(*) FROM filterNATIONALITY)=0 OR em.nationality IN (SELECT x.report_code FROM filterNATIONALITY AS x);
For the give set this returns
Emp_Code Emp_Name status nationality
ABC test1 01 USA
ABC test3 02 DE

Avoid referring table two times in the WHERE clause

Following is a simplified version of my database in SQL Server 2005. I need to select employees based on business units. Each employee has home department, parent department and visiting department. Based on the department, business unit can be found out.
For an employee, if the HomeDeptID = ParentDeptID, then
#SearchBusinessUnitCD should be present for the VisitingDeptID.
If HomeDeptID <> ParentDeptID, then #SearchBusinessUnitCD should be
present for the ParentDeptID.
Following query works fine. But it has scan on the #DepartmentBusinesses table two times. Is there a way to use the table #DepartmentBusinesses only once by making it as a CASE statement or similar?
DECLARE #SearchBusinessUnitCD CHAR(3)
SET #SearchBusinessUnitCD = 'B'
--IF HomeDeptID = ParentDeptID, then #SearchBusinessUnitCD should be present for the VisitingDeptID
--IF HomeDeptID <> ParentDeptID, then #SearchBusinessUnitCD should be present for the ParentDeptID
CREATE TABLE #DepartmentBusinesses (DeptID INT, BusinessUnitCD CHAR(3))
INSERT INTO #DepartmentBusinesses
SELECT 1, 'A' UNION ALL
SELECT 2, 'B'
CREATE NONCLUSTERED INDEX IX_DepartmentBusinesses_DeptIDBusinessUnitCD ON #DepartmentBusinesses (DeptID,BusinessUnitCD)
DECLARE #Employees TABLE (EmpID INT, HomeDeptID INT, ParentDeptID INT, VisitingDeptID INT)
INSERT INTO #Employees
SELECT 1, 1, 1, 2 UNION ALL
SELECT 2, 2, 1, 3
SELECT *
FROM #Employees
WHERE
(
HomeDeptID = ParentDeptID
AND
EXISTS (
SELECT 1
FROM #DepartmentBusinesses
WHERE DeptID = VisitingDeptID
AND BusinessUnitCD = #SearchBusinessUnitCD)
)
OR
(
HomeDeptID <> ParentDeptID
AND
EXISTS (
SELECT 1
FROM #DepartmentBusinesses
WHERE DeptID = ParentDeptID
AND BusinessUnitCD = #SearchBusinessUnitCD
)
)
DROP TABLE #DepartmentBusinesses
Plan
SELECT *
FROM #Employees e
WHERE EXISTS (
SELECT 1
FROM #DepartmentBusinesses t
WHERE t.BusinessUnitCD = #SearchBusinessUnitCD
AND (
(e.HomeDeptID = e.ParentDeptID AND t.DeptID = e.VisitingDeptID)
OR
(e.HomeDeptID != e.ParentDeptID AND t.DeptID = e.ParentDeptID)
)
)
You can give this a try:
SELECT e.*
FROM #Employees AS e
INNER JOIN #DepartmentBusinesses AS d
ON (d.DeptID = e.VisitingDeptID AND e.HomeDeptID = e.ParentDeptID) OR
(d.DeptID = e.ParentDeptID AND e.HomeDeptID <> e.ParentDeptID)
WHERE d.BusinessUnitCD = #SearchBusinessUnitCD

How to sort string alphabetically

I have a table which have the following data
Item
......
xzypq
abdcfe
How can I sort the string in the column and get the following result?
Item
......
pqxyz
abcdef
May be try the below link which might help http://social.technet.microsoft.com/wiki/contents/articles/19492.sort-letters-in-a-phrase-using-t-sql.aspx
/*Create sample table*/
IF OBJECT_ID('tempdb..#Text', 'U') IS NOT NULL
DROP TABLE #Test;
CREATE TABLE #Test
(
ID INT IDENTITY(1, 1) ,
Phrase VARCHAR(255)
);
/*Populate the table with sample data*/
INSERT #Test
( Phrase )
VALUES
( 'CHICAGO' ),
( 'NEW YORK' ),
( 'HOUSTON' ),
( 'SAN FRANCISCO' );
/*This is the final solution*/;
WITH base
AS ( SELECT L.[char] ,
T.ID ,
T.Phrase
FROM #Test T
CROSS APPLY ( SELECT SUBSTRING(T.Phrase, 1 + Number, 1) [char]
FROM master..spt_values
WHERE Number < DATALENGTH(T.Phrase)
AND type = 'P'
) L
)
SELECT DISTINCT
b1.Phrase ,
REPLACE(( SELECT '' + [char]
FROM base b2
WHERE b1.Phrase = b2.Phrase
ORDER BY [char]
FOR
XML PATH('')
), ' ', ' ') AS columns2
FROM base AS b1;
Using Recursive CTE also you can do this.
SELECT 'xzypq' NAME
INTO #temp
UNION ALL
SELECT 'abdcfe'
Recursive CTE
;WITH cte
AS (SELECT Cast(NAME AS VARCHAR(50)) AS st,NAME AS name1,1 AS rn
FROM #temp
UNION ALL
SELECT Cast(Substring(NAME, rn, 1) AS VARCHAR(50)),name1,rn + 1
FROM cte a
JOIN #temp b
ON a.name1 = b.NAME
AND rn < Len(a.name1) + 1)
SELECT DISTINCT (SELECT '' + st
FROM cte b
WHERE a.name1 = b.name1
AND rn <> 1
ORDER BY st
FOR XML PATH ('')) AS Ordered_String
FROM cte a
WHERE rn <> 1
Result
Ordered_String
--------------
abcdef
pqxyz

Resources