How to use different sub queries with SQL MERGE - sql

Can we pass different sub queries in SQL MERGE for inserting and updating data?
MERGE TableA AS SOURCE
USING TbaleB AS TARGET
ON (SOURCE.ID=TARGET.ID)
WHEN NOT MATCHED BY TARGET
THEN INSERT(COLUMN1, COLUMN2,COLUMN3 )
SELECT * FROM TableC
WHEN MATCHED
THEN UPDATE COLUMN1=SOURCE.Column1, COLUMN2=SOURCE.Column2,COLUMN3=SOURCE.Column3
FROM Table4
INNER JOIN INNER JOIN Table5
ON Table4.ID=Table5.ID
WHERE Table5.ID=1
OUTPUT $action, inserted.*;
Is there any other option to do this?
EDIT:
Using below script to create tables, insert data,
CREATE TABLE TableA([ID] [int] IDENTITY(1,1) NOT NULL,
ColumnA [nvarchar](50) NULL,
ColumnB [nvarchar](50) NULL,
ColumnC [nvarchar](50) NULL,
) ON [PRIMARY]
CREATE TABLE TableB([ID] [int] IDENTITY(1,1) NOT NULL,
ColumnA [nvarchar](50) NULL,
ColumnB [nvarchar](50) NULL,
ColumnC [nvarchar](50) NULL,
) ON [PRIMARY]
Insert into TableA values('A','1','W')
Insert into TableA values('A','2','X')
Insert into TableA values('B','1','Y')
Insert into TableB values('A','1','U')--To be update
Insert into TableB values('B','2','N')--New row to insert
Used below query to insert:
SELECT Source.* INTO #tempTable
FROM (SELECT row_number() OVER ( PARTITION BY ColumnA,ColumnB ORDER BY ColumnA,ColumnB ) Row_ID, * --using row_number to remove duplicate rows
FROM TableB) Source
LEFT OUTER JOIN TableA Target
ON Source.ColumnA = Target.ColumnA AND Source.ColumnB = Target.ColumnB
WHERE Row_ID=1 AND Target.ColumnB IS NULL AND Target.ColumnB IS NULL
ALTER TABLE #tempTable drop column Row_ID, ID
INSERT INTO TableA(ColumnA,ColumnB ,ColumnC)
select * from #tempTable
Used below query to update existing record:
UPDATE Target SET Target.ColumnC =Source.ColumnC
FROM TableA Target
INNER JOIN TableB AS Source
ON Source.ColumnA = Target.ColumnA AND Target.ColumnB = SOURCE.ColumnB
And I need the output as
ID ColumnA ColumnB ColumnC
4 A 1 U
5 A 2 X
6 B 1 Y
7 B 2 N
It is working fine, but for performance perpose, can we do the same by using MERGE?

Try the following:
WITH S AS
(
SELECT
B.ColumnA, B.ColumnB, B.ColumnC
FROM
(
SELECT
B.ColumnA, B.ColumnB, B.ColumnC,
ROW_NUMBER() OVER (PARTITION BY B.ColumnA, B.ColumnB ORDER BY B.ColumnA, B.ColumnB) AS RowId
FROM TableB B
) B
WHERE
B.RowId = 1
)
MERGE
TableA T
USING
S ON T.ColumnA = S.ColumnA AND T.ColumnB = S.ColumnB
WHEN NOT MATCHED BY TARGET
THEN INSERT (ColumnA, ColumnB, ColumnC)
VALUES (S.ColumnA, S.ColumnB, S.ColumnC)
WHEN MATCHED AND
(S.ColumnC <> T.ColumnC OR S.ColumnC IS NULL AND T.ColumnC IS NOT NULL OR S.ColumnC IS NOT NULL AND T.ColumnC IS NULL)
THEN UPDATE SET T.ColumnC = S.ColumnC;

Related

MS SQL: alter indexed view by including additional columns from new table

I need to update existing MS SQL indexed view by including additional columns values from newly created table.
Indexed view:
CREATE OR ALTER VIEW [dbo].[MySelectionInfo]
WITH schemabinding
AS
SELECT C.Id id0,
C.Code Code1,
C.Name Name2,
C.ProgramLevel Level3,
C.Department Department4,
C.City City10,
C.STATE State11,
C.StartDate StartDate12,
C.Deadline Deadline13,
B.ID Table_B_ID,
A.Id Table_A_ID
FROM dbo.Table_A A
INNER JOIN dbo.Table_B B ON A.id = B.Table_A_Id
INNER JOIN dbo.Table_C C ON C.Table_B_Id = B.Id
New table:
CREATE TABLE [dbo].[Table_D] (
[Id] [int] IDENTITY (1, 1) PRIMARY KEY NOT NULL,
[ModelName] [varchar](max) NOT NULL,
[Table_C_Id] [int] NOT NULL,
[AttributeValue] [varchar](max) NOT NULL,
[CreatedDate] [datetime] NOT NULL,
[UpdatedDate] [datetime] NOT NULL,
CONSTRAINT FK_Table_C_Id FOREIGN KEY (Table_C_Id) REFERENCES some_schema.dbo.[Table_C] (Id)
ON DELETE CASCADE
ON UPDATE CASCADE
)
Data in the new table:
I want to include only some of the ModelName column values as a column names and AttributeValue as values in the select * from [dbo].[MySelectionInfo] result set:
I can achieve the desired result using the PIVOT function:
CREATE OR ALTER VIEW [dbo].[MySelectionInfo]
WITH schemabinding
AS
SELECT C.Id id0,
C.Code Code1,
C.Name Name2,
C.StartDate StartDate12,
C.Deadline Deadline13,
B.ID Table_B_ID,
A.Id Table_A_ID
FROM dbo.Table_A A
INNER JOIN dbo.Table_B B ON A.id = B.Table_A_Id
INNER JOIN dbo.Table_C C ON C.Table_B_Id = B.Id
LEFT JOIN (SELECT PivotTable.Table_C_Id,
PivotTable.attribute1,
PivotTable.attribute2,
PivotTable.attribute3
FROM (SELECT Table_D.Table_C_Id,
Table_D.ModelName,
Table_D.AttributeValue
FROM dbo.Table_C
INNER JOIN dbo.Table_D
ON Table_C.Id = Table_D.Table_C_Id) AS sourceTable
PIVOT (
Max(AttributeValue) FOR ModelName IN (attribute1, attribute2, attribute3)
) AS PivotTable) dbo.Table_D D ON D.Table_C_Id = C.Id
But, after running the SQL statement above, I am not be able to create the clustered index for the view, because LEFT JOIN, PIVOT, MAX are prohibited to be used in the indexed views.
Question: Is there any other solutions to achieve the desired result and still have an existing view as an Indexed view?
if your data allow it, you could cross tab with case statements instead of PIVOTing:
/*
drop view dbo.mytestinfoview;
go
drop table dbo.Table1;
go
--*/
create table dbo.Table1
(
id int,
ModelName varchar(20),
AttributeValue int
);
insert into dbo.Table1(id, ModelName, AttributeValue)
select distinct o.object_id, concat('attribute', v.id), o.attributevalue
from
(
select
object_id ,
abs(checksum(newid())) % 2 as attributevalue
from sys.objects
) as o
cross apply (values(1), (2), (3), (4)) as v(id)
go
create or alter view dbo.mytestinfoview
with schemabinding
as
select id, count_big(*) as thecounter,
sum(isnull(case ModelName when 'attribute1' then AttributeValue end, 0)) as attribute1,
sum(isnull(case ModelName when 'attribute2' then AttributeValue else 0 end, 0)) as attribute2,
sum(isnull(case ModelName when 'attribute3' then AttributeValue else 0 end, 0)) as attribute3
from dbo.Table1
group by id
go
create unique clustered index idx_v1 on dbo.mytestinfoview(id);
go
select * from dbo.mytestinfoview;
go

How to capture columns from joined tables using output clause?

I am updating a table called tableA by joining tableB and tableC at the same time I am capturing updated records into temp table using output clause from tableA . Now I want capture columns from table B for the updated data but output clause isn't allowing the same.
Eg:
Update SLC Set SLC.Datascrublevel = C.Datascrublevel
OUTPUT [Deleted].Systemcode,
[Deleted].Systemkey,
[Deleted].datascrublevel,
[Inserted].datascrublevel
INTO #TEMP1
FROM TABLEA SLC with(nolock)
INNER JOIN TABLEB SC ON SC.SystemCode = SLC.SystemCode
INNER JOIN TABLEC SL ON SL.SystemCode = SLC.SystemCode and SLC.SystemKey = SL.Systemkey
INNER JOIN #TEMP C ON SLC.Datascrublevel <> C.DataScrubLevel AND C.Systemcode = SLC.SystemCode and C.Systemkey = SLC.SystemKey
Now I want columns from tableB to capture into temp table using output clause. Please provide your advise if there are any alternative ways.
Just Like you have given it as [deleted].[Column Name] and [Inserted].[Column Name] add one more column as [SC].[Column Name]
Example :
IF OBJECT_ID('TempDb..#TABLEA') IS NOT NULL
DROP TABLE #TABLEA
IF OBJECT_ID('TempDb..#TABLEB') IS NOT NULL
DROP TABLE #TABLEB
IF OBJECT_ID('TempDb..#TABLEC') IS NOT NULL
DROP TABLE #TABLEC
IF OBJECT_ID('TempDb..#TABLED') IS NOT NULL
DROP TABLE #TABLED
CREATE TABLE #TABLEA
(
SeqNo INT IDENTITY(1,1),
MyDate DATE
)
CREATE TABLE #TABLEB
(
SeqNo INT IDENTITY(1,1),
FullName VARCHAR(20)
)
CREATE TABLE #TABLEC
(
SeqNo INT IDENTITY(1,1),
FullName VARCHAR(20),
MyDate DATE
)
CREATE TABLE #TABLED
(
SeqNo INT,
MyDate DATE,
FullName VARCHAR(20)
)
INSERT INTO #TABLEA
(
MyDate
)
SELECT GETDATE()
UNION
SELECT GETDATE()+1
UNION
SELECT GETDATE()-1
INSERT INTO #TABLEB
(
FullName
)
VALUES('A'),('B'),('C')
INSERT INTO #TABLEC
(
FullName
)
VALUES('A'),('B'),('C')
UPDATE C
SET MyDate = A.MyDate
OUTPUT
deleted.SeqNo,
deleted.MyDate,
B.FullName
INTO #TABLED
FROM #TABLEC C
INNER JOIN #TABLEB B
ON C.FullName = B.FullName
INNER JOIN #TABLEA A
ON A.SeqNo = B.SeqNo
SELECT * FROM #TABLED

SQL Server SQL Statement - Updating record

I have a data as below:
I need to update Matching_id and Matching_Type by using column id, region, company, dept, subdept and amountsepend. The logic is:
Sum AmountSepend by Region, Company, Dept and SubDept. If the sum amount is 0 then Matching_Type is 'Match' and Matching_id is the combination of the id for the matched record else 'Not Match' and Matching_id is the id. **SUM means the total sum of all records for same criteria regardless the AmountSepend is positive or negative.
Another important criteria is if the transaction is single record, meaning the total count by grouping by Region, Company, Dept and SubDept is 1 then Matching type is Not Match and Matching_UID is id regardless the AmountSepend is 0 or positive/negative value. Example id 8.
Below is the output:
Here the table and data script
CREATE TABLE [dbo].[StackoverflowQuest](
[id] [int] NOT NULL,
[Region] [varchar](50) NULL,
[Company] [varchar](50) NULL,
[Dept] [varchar](50) NULL,
[SubDept] [varchar](50) NULL,
[AmountSepend] [float] NULL,
[Matching_id] [varchar](100) NULL,
[Matching_Type] [varchar](100) NULL
) ON [PRIMARY]
How could I achieved such result ? Any help/hint would be appreciate
CREATE TABLE #Table(Id INT,Region VARCHAR(100),Company INT,Dept INT,SubDept
INT,AmtSpend INT,MatchingId VARCHAR(100),MatchingType VARCHAR(100))
INSERT INTO #Table(Id ,Region ,Company , Dept ,SubDept ,AmtSpend )
SELECT 1,'NAM',12378,1,NULL,900 UNION ALL
SELECT 2,'NAM',12378,1,NULL,-900 UNION ALL
SELECT 3,'NAM',12370,1,23,1000 UNION ALL
SELECT 4,'ASA',1234,9,12,5000 UNION ALL
SELECT 5,'NAM',12370,1,23,-1000 UNION ALL
SELECT 6,'ASA',1234,9,12,800 UNION ALL
SELECT 7,'ASA',1234,9,12,-600 UNION ALL
SELECT 8,'ASA',12311,6,NULL,200
UPDATE #Table SET MatchingId = MatchIds,MatchingType = 'Match'
FROM
(
SELECT T2.Company,STUFF( ( SELECT ',' + CAST(T3.Id AS VARCHAR) FROM #Table
T3 WHERE T2.Company = T3.Company FOR XML PATH('')),1,1,'') MatchIds
FROM #Table T2
JOIN
(
SELECT T1.Company Company,SUM(T1.AmtSpend) Total
FROM #Table T1
GROUP BY T1.Company
HAVING SUM(T1.AmtSpend) = 0
)A ON A.Company = T2.Company
GROUP BY T2.Company
) A
WHERE A.Company = #Table.Company
UPDATE #Table SET MatchingId = CAST(Id AS VARCHAR),MatchingType = 'Not
Match' WHERE ISNULL(MatchingId,'') = ''
SELECT * FROM #Table

How to deal with Violation of PRIMARY KEY constraint error

I am having a real problem with my stored procedure with this script:
INSERT INTO #tr_TxnDetails
SELECT
b.pid,
b.etc
FROM tbl_SomeTableA as a
JOIN tbl_SomeTableB as b ON a.etc = b.etc
AND a.SomeColumn = b.SomeColumn
-- This is throwing error: Violation of PRIMARY KEY constraint. Cannot insert duplicate key in object 'dbo.tr_TxnDetails'.
INSERT INTO tr_TxnDetails
([id], [etc])
SELECT a.[id],
a.[etc]
FROM #tr_TxnDetails as a
WHERE not exists (select 1 from tr_TxnDetails as b where a.[id] = b.[id]);
How do I make sure the during the INSERT INTO statement to tr_TxnDetails it is not inserting a row with the same primary key: pid ?
INSERT INTO #tr_TxnDetails
SELECT
b.pid,
b.etc
FROM tbl_SomeTableA as a
JOIN tbl_SomeTableB as b ON a.etc = b.etc
AND a.SomeColumn = b.SomeColumn
WHERE b.pid NOT IN (select distinct id from tr_TxnDetails) --<<--
INSERT INTO tr_TxnDetails
([id], [etc])
SELECT a.[id],
a.[etc]
FROM #tr_TxnDetails as a
I think that your first INSERT ... SELECT statement is producing duplicates and then these duplicates are causing primary key errors in your second select. Your WHERE EXISTS clause only guards against inserting a duplicate that is a duplicate of an existing row.
I will come to your query later, but just to show you can cause this error quite simply with the following set of statements:
create table TableA
(
Pid INT PRIMARY KEY,
etc INT
);
INSERT INTO TableA
SELECT 1, 0
UNION
SELECT 1, 2
and here is the error:
Violation of PRIMARY KEY constraint 'PK__TableA__C57059387F60ED59'. Cannot insert duplicate key in object 'dbo.TableA'.: INSERT INTO TableA SELECT 1, 0 UNION SELECT 1, 2
Now back to your query, the simple re-write is to ensure that the query only returns DISTINCT rows:
INSERT INTO #tr_TxnDetails
SELECT DISTINCT
b.pid,
b.etc
FROM tbl_SomeTableA as a
JOIN tbl_SomeTableB as b ON a.etc = b.etc
AND a.SomeColumn = b.SomeColumn
INSERT INTO tr_TxnDetails
([id], [etc])
SELECT a.[id],
a.[etc]
FROM #tr_TxnDetails as a
WHERE not exists (select 1 from tr_TxnDetails as b where a.[id] = b.[id]);
This should do the trick for you.
One further point is that in your example you should do away with the temporary table step unless there is a good reason for it, such as some other processing between those two statements. Here is the rewritten query:
INSERT INTO tr_TxnDetails
SELECT DISTINCT
b.pid,
b.etc
FROM tbl_SomeTableA as a
JOIN tbl_SomeTableB as b ON a.etc = b.etc
AND a.SomeColumn = b.SomeColumn
WHERE not exists (
select 1
from tr_TxnDetails as c
where a.[id] = C.[id]
);
DECLARE #ChoiceID INT
SET #ChoiceID = (SELECT MAX([CHOICE_ID]) FROM BI_QUESTION_CHOICE) -- FOR SOMETABLE.ID
INSERT BI_QUESTION_CHOICE
(
[choice_id],
[choice_descr],
[sequence],
[question_id],
[is_correct],
[created_by],
[created_dt],
[modified_by],
[modified_dt]
)
(SELECT #ChoiceID+ROW_NUMBER() OVER (ORDER BY #ChoiceID),
pref.value('(ChoiceText/text())[1]', 'varchar(50)'),
pref.value('(Sequence/text())[1]', 'varchar(50)') ,
#QuestionID,
pref.value('(IsCorrect/text())[1]', 'bit'),
'mbathini',
GETDATE(),
'mbathini',
GETDATE()
FROM #xmlstring.nodes('/ArrayOfBI_QA_ChoiceEntity/BI_QA_ChoiceEntity') AS Responses(pref))

How to INSERT using an inverse JOIN on multiple keys?

How do I do an inverse join with more than one key column?
In this baby-toy SqlServer example, I have the following
CREATE TABLE [dbo].[CarList](
[myID] [int] IDENTITY(1,1) NOT NULL,
[CarColour] [varchar](32) NOT NULL,
[CarName] [varchar](128) NOT NULL,
[CarCompany] [varchar](32) NOT NULL,
CONSTRAINT [PK_CarList] PRIMARY KEY CLUSTERED(
[myID] ASC,
[CarColour] ASC,
[CarName] ASC,
[CarCompany] ASC
)
)
GO
INSERT INTO CarList (CarColour, CarName, CarCompany)
VALUES('blue', 'Abe', 'Ford')
Elsewhere in the DB I have a table like
CREATE TABLE [dbo].[NewCars](
[CarColour] [varchar](32) NOT NULL,
[CarName] [varchar](128) NOT NULL,
[CarCompany] [varchar](32) NOT NULL,
)
GO
INSERT INTO NewCars (CarColour, CarName, CarCompany)
SELECT 'blue', 'Abe', 'Ford'
UNION ALL
SELECT 'blue', 'Abe', 'GM'
UNION ALL
SELECT 'blue', 'Betty', 'Ford'
UNION ALL
SELECT 'green', 'Abe', 'Honda'
Now I want to insert cars I don't already have in the CarList table
Something like...
INSERT INTO CarList ( CarColour, CarName, CarCompany)
SELECT DISTINCT new.CarColour, new.CarName, new.CarCompany
FROM NewCars new, CarList old
WHERE new.CarColour <> old.CarColour
AND new.CarName <> old.CarName
AND new.CarCompany <> old.CarCompany
Which doesn't work because the "blue', 'Betty', 'Ford' row will get filtered out...
If this were just a single ID of some kind it would be really easy
INSERT INTO myTable (myID, param1, param2, etc)
SELECT param1, param2, etc
FROM someOtherTable new
WHERE new.myID NOT IN (SELECT myID FROM myTable)
For reasons I don't really want to get into, I cannot remove rows from NewCars that match CarList. I also need to do this in one pass if possible.
[edit]
Thanks guys!
thanks for DDL and DML
Here is one way
INSERT INTO CarList ( CarColour, CarName, CarCompany)
SELECT DISTINCT *
FROM NewCars n
where not exists (select 1 from CarList c where c.CarColour =n.CarColour
and c.CarName = n.CarName
and c.CarCompany = n.CarCompany)
There are at least 4 different way to do this
NOT IN (will not work for more than 1 column like you have)
NOT EXISTS
LEFT and RIGHT JOIN
OUTER APPLY (2005+)
EXCEPT (2005+)
Read Select all rows from one table that don't exist in another table
INSERT
INTO CarList ( CarColour, CarName, CarCompany)
SELECT CarColour, CarName, CarCompany
FROM NewCars nc
WHERE NOT EXISTS
(
SELECT 1
FROM CarList cl
WHERE cl.CarColor = nc.CarColor
AND cl.CarName = nc.CarName
AND cl.CarCompany = nc.CarCompany
)
I would probably use:
INSERT INTO CarList(CarColour, CarName, CarCompany)
SELECT
NC.CarColour,
NC.CarName,
NC.CarCompany
FROM
NewCars NC
LEFT OUTER JOIN CarList CL ON
CL.CarColour = NC.CarColour AND
CL.CarName = NC.CarName AND
CL.CarCompany = NC.CarCompany
WHERE
CL.MyID IS NULL
INSERT INTO CarList ( CarColour, CarName, CarCompany)
SELECT DISTINCT new.CarColor, new.CarName, new.CarCompany
FROM NewCar new
where not exists (select 0
from CarList old
WHERE new.CarColour = old.CarColour
AND new.CarName = old.CarName
AND new.CarCompany = old.CarCompany)
--This statement matches all that does exists in carlist
--and insert everything that does not exists in Carlist