Merge in SQL Server 2017 - sql

This is my query, I want to merge data from the source into the destination (I'm a beginner in SQL Server
DECLARE #T TABLE(NoContratAdhesion char(8) );
DECLARE #rqt as nvarchar (800000)
SET = 'SELECT *
FROM infocentre.[dbo].[TCtrCollRG] as RG
INNER JOIN SSIS_Temp.dbo.TLID_ADH_RG1_HUM AS RG1 ON RG.NoContratAdhesion = RG1.NoContratAdhesion'
USING (SELECT * FROM SSIS_Temp.dbo.Tmp_CollRG_sans_cle_HUM
WHERE Flag_doublons <> '1') AS SOURCE
ON (TARGET.Nocontratadhesion = SOURCE.NoContratAdhesion
AND TARGET.NoAvenant = SOURCE.NoAvenant
AND TARGET.CodeRG=SOURCE.CodeRG)
WHEN NOT MATCHED BY TARGET
THEN
INSERT ([NoContratAdhesion], [NoAvenant], [NoAdherent],
[CodeProduitCible], [CodeRG], [Optionnel], [Retenu],
[Retarde], [DateDebutValiditeRG], [DateFinValiditeRG],
[DateMajRG], [DateInsertPl], [DateMajPl], Date_CHARG_SIAD, FLAG_DELTA)
VALUES (SOURCE.[NoContratAdhesion], SOURCE.[NoAvenant], NULL,
SOURCE.[CodeProduitCible], SOURCE.[CodeRG], SOURCE.[Optionnel], SOURCE.[Retenu],
SOURCE.[Retarde], SOURCE.[DateDebutValiditeRG], SOURCE.[DateFinValiditeRG],
SOURCE.[DateMajRG], SOURCE.[DateInsertPl], SOURCE.[DateMajPl], GETDATE(), 'I')
WHEN MATCHED
THEN UPDATE SET
TARGET.[NoContratAdhesion]=SOURCE.[NoContratAdhesion]
,TARGET.[NoAvenant]=SOURCE.[NoAvenant]
,TARGET.[NoAdherent]=NULL
,TARGET.[CodeProduitCible]=SOURCE.[CodeProduitCible]
,TARGET.[CodeRG]=SOURCE.[CodeRG]
,TARGET.[Optionnel]=SOURCE.[Optionnel]
,TARGET.[Retenu]=SOURCE.[Retenu]
,TARGET.[Retarde]=SOURCE.[Retarde]
,TARGET.[DateDebutValiditeRG]=SOURCE.[DateDebutValiditeRG]
,TARGET.[DateFinValiditeRG]=SOURCE.[DateFinValiditeRG]
,TARGET.[DateMajRG]=SOURCE.[DateMajRG]
,TARGET.[DateInsertPl]=SOURCE.[DateInsertPl]
,TARGET.[DateMajPl]=SOURCE.[DateMajPl]
, TARGET.Date_CHARG_SIAD =getdate()
,TARGET.FLAG_DELTA='M'
WHEN NOT MATCHED BY SOURCE
THEN DELETE
OUTPUT Source.NoContratAdhesion
INTO #T;
DELETE infocentre.[dbo].[TCtrCollRG]
WHERE NoContratAdhesion in (SELECT NoContratAdhesion
FROM #T);
select count(*) from infocentre.[dbo].[TCtrCollRG]
I want to delete data if there are not in destination or update if exists, i tried this query but have errors.
Can you help me please?

nvarchar does not permit a length of 800000. Your set command does not describe what variable is being set. You have no merge clause to indicate what your target table is.
At minimum, you need to change this:
DECLARE #T TABLE(NoContratAdhesion char(8) );
DECLARE #rqt as nvarchar (800000)
set= 'SELECT * from infocentre.[dbo].[TCtrCollRG] as RG
INNER JOIN SSIS_Temp.dbo.TLID_ADH_RG1_HUM AS RG1 on RG.NoContratAdhesion=RG1.NoContratAdhesion'
USING (SELECT * from SSIS_Temp.dbo.Tmp_CollRG_sans_cle_HUM where Flag_doublons <> '1') AS SOURCE
ON (TARGET.Nocontratadhesion = SOURCE.NoContratAdhesion
and TARGET.NoAvenant = SOURCE.NoAvenant and TARGET.CodeRG=SOURCE.CodeRG)
into this:
DECLARE #T TABLE (NoContratAdhesion char(8));
DECLARE #rqt nvarchar(4000);
set #rqt = '
SELECT * from infocentre.[dbo].[TCtrCollRG] as RG
INNER JOIN SSIS_Temp.dbo.TLID_ADH_RG1_HUM AS RG1 on RG.NoContratAdhesion=RG1.NoContratAdhesion
';
merge #T target
USING (SELECT * from SSIS_Temp.dbo.Tmp_CollRG_sans_cle_HUM where Flag_doublons <> '1') AS SOURCE
ON TARGET.Nocontratadhesion = SOURCE.NoContratAdhesion
and TARGET.NoAvenant = SOURCE.NoAvenant and TARGET.CodeRG=SOURCE.CodeRG
After a cursory scan, I don't believe I see a major issue with the rest of your logic.
This is assuming you're in fact trying to merge from the real table into #T and not the other way around. Otherwise switch the merge and the using statements.

Related

SET more than 1 row output into 1 variable

I am wanting to be able to save more than just 1 output from a SELECT query in a single variable.
Currently I am gathering my needed data like so:
DECLARE #something1 VARCHAR(MAX)
DECLARE #something2 VARCHAR(MAX)
DECLARE #something3 VARCHAR(MAX)
SET #something1 = (
SELECT
Custom AS 'XXL Format'
FROM
tblData
WHERE
ID = 1);
SET #something2 = (
SELECT
Custom.value('(/Individual/text())[1]', 'varchar(MAX)') AS 'Non XML Format'
FROM
tblData
WHERE
ID = 1)
SET #something3 = (
SELECT
tbl1.paper,
tbl2.type
FROM
tblData AS tbl1
JOIN tblData2 tbl2
ON tbl1.ID = tbl2.ID
WHERE
ID = 1);
I have the following demo that shows what I am wanting to do
DECLARE #tester VARCHAR(MAX)
SET #tester = (
SELECT
tbl1.Custom AS 'XXL Format',
tbl1.Custom.value('(/Individual/text())[1]', 'varchar(MAX)') AS 'Non XML Format'
tbl1.paper,
tbl2.type
FROM
tblData AS tbl1
JOIN tblData2 tbl2
ON tbl1.ID = tbl2.ID
WHERE
ID = 1);
I get the error of:
Only one expression can be specified in the select list when
the subquery is not introduced with EXISTS
Both demos can be found here
I have tried to set the variable to "table" and store the data like that but that also does not seem to work correctly [I'm sure I am doing something wrong - that may be the answer to this question I'm asking]
How can I just use 1 variable for all that the above query outputs?
Your error is that the subquery returned more than one value. You can use top if you don't care which value gets assigned:
SET #tester = (SELECT TOP (1) description FROM ForgeRock);
You have to return only one value you can try top or limit like SET #tester = (SELECT TOP 1 description FROM ForgeRock); or SET #tester = (SELECT description FROM ForgeRock LIMIT 1);
So I guess I was correct with the set the variable to a temp "table". I finally just now got it to work for my needs!
DECLARE #tmpTbl table (_xml, _parsedXML, _paper, _type)
INSERT INTO #tmpTbl
SELECT
tbl1.Custom AS 'XML Format',
tbl1.Custom.value('(/Individual/text())[1]', 'varchar(MAX)') AS 'Non XML Format',
tbl1.paper,
tbl2.type
FROM
tblData AS tbl1
JOIN tblData2 AS tbl2
ON tbl1.ID = tbl2.ID
WHERE
ID = 1;
DECLARE #something1 VARCHAR(MAX) = (SELECT _xml FROM #tmpTbl);
DECLARE #something2 VARCHAR(MAX) = (SELECT _parsedXML FROM #tmpTbl);
DECLARE #something3 VARCHAR(MAX) = (SELECT _paper FROM #tmpTbl);
DECLARE #something4 VARCHAR(MAX) = (SELECT _type FROM #tmpTbl);
DELETE FROM #tmpTbl --Not really needed but nice to be offical :)
This above stores the values into one place. Although its not inside 1 variable I guess having to create a temp table isn't all that bad for the database/performance...

Can we create a view after a script from a variable?

I would like to create a view at the end of the following request.
I know that 'create view' must be the first statement in a query batch. My problem is that for this query i must use a variable (#listOfIDRUB).
This variable is only fill correctly at the end of my little script.
I also have tried to create the view before my first declaration but it created a problem with "DECLARE".
So is it possible to create a view easily from the result of my script or i have to do something else ?
DECLARE #CounterResId int;
DECLARE #lePath varchar(255);
DECLARE #listOfIDRUB TABLE (EXTERNALREFERENCE uniqueidentifier, ID varchar(255), DOCID varchar(255) );
DECLARE #Max int;
SET #lePath = '';
SET #CounterResId = 1;
SET #Max = (SELECT COUNT(*) FROM SYNTHETIC..EXTRANET_PURGE WHERE TYPE_SUPPR = 'ResId')
WHILE (#CounterResId <= #Max )
BEGIN;
set #lePath =
(select tmp.lePath from
(
select row_number() over(order by path)as NumLigne, CONCAT(path, '%' ) as lePath from RUBRIQUE
WHERE MODELE = 'CAEEE64D-2B00-44EF-AA11-6B72ABD9FE38'
and CODE in (SELECT ID FROM SYNTHETIC..EXTRANET_PURGE where TYPE_SUPPR='ResId')
) tmp
WHERE tmp.NumLigne = #CounterResId)
INSERT INTO #listOfIDRUB(EXTERNALREFERENCE, ID, DOCID)
SELECT SEC.EXTERNALREFERENCE , SEC.ID, SEC.DOCUMENTID
FROM WEBACCESS_FRONT..SECTIONS sec
inner join rubrique rub ON rub.ID_RUBRIQUE = sec.EXTERNALREFERENCE
inner join template_tree_item tti ON tti.id_template_tree_item = rub.modele
inner join template t ON t.id_template = tti.template
WHERE t.CODE IN (SELECT TEMPLATE_CODE from SYNTHETIC..EasyFlowEngineListTemplateCode)
and rub.path like #lePath
print #CounterResId;
print #lePath;
set #CounterResId = #CounterResId + 1;
END;
select * from #listOfIDRUB;
Instead of select * from #listOfIDRUB
i wanted create view test as select * from listOfIDRUB
I have also tried create view test as (all my request)
Whenever you ask something about SQL please state your RDBMS (product and version). The answers are highly depending on this...
From your code I assume this is SQL Server.
So to your question: No, a VIEW must be "inlineable" (single-statement or "ad-hoc") statement.
You might think about a multi-statement UDF, but this is in almost all cases a bad thing (bad performance). Only go this way, if your result table will consist of rather few rows!
Without knowing your tables this is rather blind walking, but you might try this (add parameters, if you can transfer external operations (e.g. filtering) into the function):
CREATE FUNCTION dbo.MyFunction()
RETURNS #listOfIDRUB TABLE (EXTERNALREFERENCE uniqueidentifier, ID varchar(255), DOCID varchar(255) )
AS
BEGIN
DECLARE #CounterResId int;
DECLARE #lePath varchar(255);
DECLARE #Max int;
SET #lePath = '';
SET #CounterResId = 1;
SET #Max = (SELECT COUNT(*) FROM SYNTHETIC..EXTRANET_PURGE WHERE TYPE_SUPPR = 'ResId')
WHILE (#CounterResId <= #Max )
BEGIN;
set #lePath =
(select tmp.lePath from
(
select row_number() over(order by path)as NumLigne, CONCAT(path, '%' ) as lePath from RUBRIQUE
WHERE MODELE = 'CAEEE64D-2B00-44EF-AA11-6B72ABD9FE38'
and CODE in (SELECT ID FROM SYNTHETIC..EXTRANET_PURGE where TYPE_SUPPR='ResId')
) tmp
WHERE tmp.NumLigne = #CounterResId)
INSERT INTO #listOfIDRUB(EXTERNALREFERENCE, ID, DOCID)
SELECT SEC.EXTERNALREFERENCE , SEC.ID, SEC.DOCUMENTID
FROM WEBACCESS_FRONT..SECTIONS sec
inner join rubrique rub ON rub.ID_RUBRIQUE = sec.EXTERNALREFERENCE
inner join template_tree_item tti ON tti.id_template_tree_item = rub.modele
inner join template t ON t.id_template = tti.template
WHERE t.CODE IN (SELECT TEMPLATE_CODE from SYNTHETIC..EasyFlowEngineListTemplateCode)
and rub.path like #lePath
--print #CounterResId;
--print #lePath;
set #CounterResId = #CounterResId + 1;
END;
RETURN;
END
You can call it like this (very similar to a VIEW)
SELECT * FROM dbo.MyFunction();
And you might even use it in joins...
And last but not least I'm quite sure, that one could solve this without declares and a loop too...

I need to optimize my first T-SQL update trigger

How do I rewrite this update trigger without using a lot of variables?
I wrote my first SQL Server trigger and it works fine, but I think, that there must be an easier solution.
If minimum one of 5 columns is changed I write two new rows in another table.
row 1 = old Fahrer (=Driver) and old dispodate and update-time
row 2 = new Fahrer and new dispodate and updatedatetime
My solution is just a copy of the foxpro-trigger, but there must be a easier solutions in T-SQL to check whether one colum is changed.
ALTER TRIGGER [dbo].[MyTrigger]
ON [dbo].[tbldisposaetze]
AFTER UPDATE
AS
SET NOCOUNT ON;
/*SET XACT_ABORT ON
SET ARITHABORT ON
*/
DECLARE #oldfahrer varchar(10)
DECLARE #oldbus varchar(10)
DECLARE #olddispodat date
DECLARE #oldvzeit decimal(4,0)
DECLARE #oldbzeit decimal(4,0)
DECLARE #oldbeschreibk varchar(255)
DECLARE #newfahrer varchar(10)
DECLARE #newbus varchar(10)
DECLARE #newdispodat date
DECLARE #newvzeit decimal(4,0)
DECLARE #newbzeit decimal(4,0)
DECLARE #newbeschreibk varchar(255)
SELECT #oldfahrer = fahrer,#oldbeschreibk=beschreibk,#oldbus=bus,#oldbzeit=bzeit,#olddispodat=dispodat,#oldvzeit=vzeit
FROM DELETED D
SELECT #newfahrer = fahrer,#newbeschreibk=beschreibk,#newbus=bus,#newbzeit=bzeit,#newdispodat=dispodat,#newvzeit=vzeit
FROM inserted I
if #oldbeschreibk <> #newbeschreibk or #oldbus <> #newbus or #oldbzeit <> #newbzeit or #oldfahrer <> #newfahrer or #oldvzeit <> #newvzeit
begin
IF (SELECT COUNT(*) FROM tbldispofahrer where fahrer=#oldfahrer and dispodat=#olddispodat) > 0
update tbldispofahrer set laenderung = GETDATE() where fahrer=#oldfahrer and dispodat=#olddispodat
else
INSERT into tbldispofahrer (fahrer,dispodat,laenderung) VALUES (#oldfahrer,#olddispodat,getdate())
IF (SELECT COUNT(*) FROM tbldispofahrer where fahrer=#newfahrer and dispodat=#newdispodat) > 0
update tbldispofahrer set laenderung = GETDATE() where fahrer=#newfahrer and dispodat=#newdispodat
else
INSERT into tbldispofahrer (fahrer,dispodat,laenderung) VALUES (#newfahrer,#newdispodat,getdate())
end
I'll assume you have SQL Server 2008 or greater. You can do this all in one statement without any variables.
Instead of doing all the work to first get the variables and see if they don't match, you can easily do that in as part of where clause. As folks have said in the comments, you can have multiple rows as part of inserted and deleted. In order to make sure you're working with the same updated row, you need to match by the primary key.
In order to insert or update the row, I'm using a MERGE statement. The source of the merge is a union with the where clause above, the top table in the union has the older fahrer, and the bottom has the new farher. Just like your inner IFs, existing rows are matched on farher and dispodat, and inserted or updated appropriately.
One thing I noticed, is that in your example newfahrer and oldfahrer could be exactly the same, so that only one insert or update should occur (i.e. if only bzeit was different). The union should prevent duplicate data from trying to get inserted. I do believe merge will error if there was.
MERGE tbldispofahrer AS tgt
USING (
SELECT d.farher, d.dispodat, GETDATE() [laenderung]
INNER JOIN inserted i ON i.PrimaryKey = d.PrimaryKey
AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreik ... )
UNION
SELECT i.farher, i.dispodat, GETDATE() [laenderung]
INNER JOIN inserted i ON i.PrimaryKey = d.PrimaryKey
AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreik ... )
) AS src (farher, dispodat, laenderung)
ON tgt.farher = src.farher AND tgt.dispodat = src.dispodat
WHEN MATCHED THEN UPDATE SET
laenderung = GETDATE()
WHEN NOT MATCHED THEN
INSERT (fahrer,dispodat,laenderung)
VALUES (src.fahrer, src.dispodat, src.laenderung)
There were a few little syntax errors in the answer from Daniel.
The following code is running fine:
MERGE tbldispofahrer AS tgt
USING (
SELECT d.fahrer, d.dispodat, GETDATE() [laenderung] from deleted d
INNER JOIN inserted i ON i.satznr = d.satznr
AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreibk or i.bus <> d.bus or i.bzeit <> d.bzeit or i.vzeit <> d.vzeit)
UNION
SELECT i.fahrer, i.dispodat, GETDATE() [laenderung] from inserted i
INNER JOIN deleted d ON i.satznr = d.satznr
AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreibk or i.bus <> d.bus or i.bzeit <> d.bzeit or i.vzeit <> d.vzeit)
) AS src (fahrer, dispodat, laenderung)
ON tgt.fahrer = src.fahrer AND tgt.dispodat = src.dispodat
WHEN MATCHED THEN UPDATE SET
laenderung = GETDATE()
WHEN NOT MATCHED THEN
INSERT (fahrer,dispodat,laenderung)
VALUES (src.fahrer, src.dispodat, src.laenderung);

optimizing the code

I have written this code for small database but know the database size has increased,it is showing timeout error.plz help in optimizing it
Below is the code:-
IF OBJECT_ID('Temp_expo') is not null
begin
drop table Temp_expo
end
set #query3 = 'SELECT SPCT_ID_REL_LOW,SPCT_ID_REL_HIGH,ROW_NUMBER() over (order by PDBC_PFX) as TempId
INTO Temp_expo
FROM ['+ #FCTServer +'].['+#FCTDBName+'].dbo.CMC_SPCT_SUPP_CONV
where SPCT_ID_REL_LOW <> '''' and SPCT_ID_REL_HIGH <> '''''
exec (#query3)
Select #minCount= min(TempId) from Temp_expo
Select #maxCount= max(TempId) from Temp_expo
create table #ICD9SPCT
(
ICD9Code varchar(200)
}
while #minCount<=#maxCount
begin
select #low=SPCT_ID_REL_LOW,#high=SPCT_ID_REL_HIGH
from Temp_expo
where TempId=#minCount
group by SPCT_ID_REL_LOW,SPCT_ID_REL_HIGH
set #loworder = (select ISNULL(OrderId,0) from FCT_ICD9_Diag_ORDER where ICD9=#low)
set #highorder = (select ISNULL(OrderId,0) from FCT_ICD9_Diag_ORDER where ICD9=#high)
insert into #ICD9SPCT
select ICD9 from FCT_ICD9_Diag_ORDER ordert
left join #ICD9SPCT icdorder on ordert.ICD9 = icdorder.ICD9Code
where OrderId between #loworder and #highorder and icdorder.ICD9Code is null
set #minCount = #minCount+1;
end
If this is for SQL SERVER, there are some basic tips you can try:
USE: WITH (NOLOCK) after every select you use.
i.e.
select ICD9 from FCT_ICD9_Diag_ORDER ordert WITH (NOLOCK)
left join #ICD9SPCT icdorder on ordert.ICD9 = icdorder.ICD9Code
where OrderId between #loworder and #highorder and icdorder.ICD9Code is null
You can also try to change your temp tables to variable tables, by just changing the # for an # like :
create table #ICD9SPCT
(
ICD9Code varchar(200)
}
Still, the WHILE loop you are using may be the primary cause of your problem.

Quicker way to update all rows in a SQL Server table

Is there a more efficient way to write this code? Or with less code?
SELECT *
INTO #Temp
FROM testtemplate
Declare #id INT
Declare #name VARCHAR(127)
WHILE (SELECT Count(*) FROM #Temp) > 0
BEGIN
SELECT TOP 1 #id = testtemplateid FROM #Temp
SELECT TOP 1 #name = name FROM #Temp
UPDATE testtemplate
SET testtemplate.vendortestcode = (SELECT test_code FROM test_code_lookup WHERE test_name = #name)
WHERE testtemplateid = #id
--finish processing
DELETE #Temp Where testtemplateid = #id
END
DROP TABLE #Temp
You can do this in a single UPDATE with no need to loop.
UPDATE tt
SET vendortestcode = tcl.test_code
FROM testtemplate tt
INNER JOIN test_code_lookup tcl
ON tt.name = tcl.test_name
You could try a single update like this:
UPDATE A
SET A.vendortestcode = B.test_code
FROM testtemplate A
INNER JOIN test_code_lookup B
ON A.name = B.test_name
Also, the way you are doing it now is wrong, since you are taking a TOP 1 Id and a TOP 1 name in two separate querys, without an ORDER BY, so its not sure that you are taking the right name for your ID.
You could write a function to update vendortestcode. Then your code reduces to one SQL statement:
update testtemplate set vendortestcode = dbo.get_test_code_from_name(name)