Using Select From table in Insert statement of a merge query - sql-server

The below code is getting error in SQL
Incorrect syntax near the keyword 'Select'.
This is my code for merge where InstalledSoftwareList is a used defined table.
MERGE [dbo].[TableName] AS TargetTable
USING UDTableName AS SourceTable
ON (TargetTable.[EId] = SourceTable.[EId]
AND TargetTable.[MId] = SourceTable.[MId]
AND TargetTable.PackageId = (SELECT Id FROM [PackagesDummyTable] SP WHERE SP.[Version] = SourceTable.[Version] AND SP.[Name] = SourceTable.[Name])
)
WHEN NOT MATCHED BY TARGET -- If the records in the Customer table is not matched?-- then INSERT the record
THEN INSERT ([Guid], [PackageId], [CName], [UUID], [MAC], [Date], [isUninstalled], [LastUpdatedDateTime], [DataCapturedTime], [SGuid], [UniqueId], [MId], [EId])
Select SourceTable.Guid,SP.PackageId,SourceTable.CName,SourceTable.UUID,SourceTable.MAC,SourceTable.Date,SourceTable.isUninstalled,GETUTCDATE(),SourceTable.DataCapturedTime,SourceTable.SGuid, SourceTable.UniqueId, SourceTable.MId, SourceTable.EId
FROM [PackagesDummyTable] SP WHERE SP.[Version] = SourceTable.[Version] AND SP.[Name] = SourceTable.[Name];
I was referring this https://msdn.microsoft.com/en-us/library/bb510625.aspx. And my syntax seems to be right.
Can anyone help me on this. I am using SQL Azure.

As #gotqn said, If you only need to prcess the new data, you can just you insert into statement.
If it's required that you must you MERG INTo, you can change your script to below
MERGE [dbo].[TableName] AS TargetTable
USING (
SELECT UN.[EId],UN.[MId],SP.ID ,UN.Guid,SP.PackageId,UN.CName,UN.UUID,UN.MAC,UN.Date,UN.isUninstalled
,UN.DataCapturedTime,UN.SGuid, UN.UniqueId
FROM UDTableName AS UN AS
INNER JOIN [PackagesDummyTable] SP ON SP.[Version] = UN.[Version] AND SP.[Name] = UN.[Name]
) SourceTable
ON TargetTable.[EId] = SourceTable.[EId]
AND TargetTable.[MId] = SourceTable.[MId]
AND TargetTable.PackageId = SourceTable.Id
WHEN NOT MATCHED BY TARGET -- If the records in the Customer table is not matched?-- then INSERT the record
THEN INSERT ([Guid], [PackageId], [CName], [UUID], [MAC], [Date], [isUninstalled], [LastUpdatedDateTime], [DataCapturedTime], [SGuid], [UniqueId], [MId], [EId])
VALUES( SourceTable.Guid,SourceTable.PackageId,SourceTable.CName,SourceTable.UUID,SourceTable.MAC,SourceTable.Date,SourceTable.isUninstalled,GETUTCDATE(),SourceTable.DataCapturedTime,SourceTable.SGuid, SourceTable.UniqueId, SourceTable.MId, SourceTable.EId)
;

MERGE is nice if you want to do more than one CRUD operations. In this case, we only need to insert new records. Could you try something like this:
INSERT INTO ([Guid], [PackageId], [CName], [UUID], [MAC], [Date], [isUninstalled], [LastUpdatedDateTime], [DataCapturedTime], [SGuid], [UniqueId], [MId], [EId])
SELECT SourceTable.Guid,SP.PackageId,SourceTable.CName,SourceTable.UUID,SourceTable.MAC,SourceTable.Date,SourceTable.isUninstalled,GETUTCDATE(),SourceTable.DataCapturedTime,SourceTable.SGuid, SourceTable.UniqueId, SourceTable.MId, SourceTable.EId
-- we need these two tables in order to import data
FROM UDTableName AS SourceTable
INNER JOIN [PackagesDummyTable] SP
ON SP.[Version] = SourceTable.[Version]
AND SP.[Name] = SourceTable.[Name]
-- we are joing this table in order to check if there is new data for import
LEFT JOIN [dbo].[TableName] AS TargetTable
ON TargetTable.[EId] = SourceTable.[EId]
AND TargetTable.[MId] = SourceTable.[MId]
-- we are importing only the data that is new
WHERE TargetTable.PackageId IS NULL;

Related

how to insert a value to both tables using join codeigniter 4 sql server

I'm trying to insert values to both tables at the same time. I'm using a form in my application where I use the inserted values from the form to inert them into the db. But now I'm inserting values to one table (Users).
public function registerUser($formdata){
helper('global');// a heper for randomString().
//Asign value to columns
$db_data['Emailaddress'] = $formdata['emailaddress'];
$db_data['Password'] = password_hash($formdata['password'], PASSWORD_DEFAULT);
$db_data['Status'] = 'Free';
$db_data['Token'] = randomString(32);
$db_data['FirstLogin'] = 0;
$db_data['Users.UsersKey'] = $db_data['UsersSettings.UsersKey'];
//insert to db
$this->db->table('Users', 'UsersSettings')->join('UsersSettings','Users.UsersKey = UsersSettings.UsersKey', 'inner')->insert($db_data);
}
public function updateUserSetting_proccess(){
$formdata = $this->request->getPostGet();
return $this->SettingsModel->update_user_settings($formdata);
}
The content of the Users table is:
SELECT TOP (1000) [UsersKey]
,[UniqueID]
,[Token]
,[ResetToken]
,[Emailaddress]
,[Password]
,[Status]
,[DateTimeAdded]
,[DateTimeLastUpdated]
,[FirstLogin]
FROM [dbo].[Users]
The UsersKey is inserted automaticly because of the auto increment.
The second table I want to use is UsersSettings with content:
SELECT TOP (1000) [UsersSettingsKey]
,[UsersKey]
,[FirstName]
,[LastName]
,[Logo]
,[Organization]
,[Address]
,[Number]
,[Addition]
,[Postcode]
,[City]
,[Country]
,[Language]
,[Theme]
,[CalcPercentage]
,[CalcAdminFee]
,[ColorPrimary]
,[ColorSecondary]
,[DateTimeLastUpdated]
FROM [dbo].[UsersSettings]
I want the UsersKey from UsersSettings have the same value in Users UsersKey.
I tried this:
join('UsersSettings','Users.UsersKey = UsersSettings.UsersKey', 'inner')
but it didn't help. Can someong give me some suggestions?
You'll need to perform the insert into table Users first in order to get the generated UsersKey.
Explanation of why inserting into table Users first is required, may be shown with the SQL equivalent:
declare #lv_UsersKey int
-- insert into table Users (only essential parts shown)
insert into Users(....) values (...)
-- capture UsersKey for inserted record
select #lv_UsersKey = cast(SCOPE_IDENTITY() as int)
-- then insert into UsersSettings (only essential parts shown)
insert into UsersSettings (UsersKey, ....) values (#lv_UsersKey, ...)
Transferring the above SQL to codeigniter will look like this:
$this->db->table('Users')->insert($db_data);
$inserted_users_key = $this->db->insert_id();
$db_data2['UsersKey'] = $inserted_users_key;
// some more init of $db_data2 here
$this->db->table('UsersSettings')->insert($db_data2);

SQL Trigger - DML statement Output Insert Error [duplicate]

I have a table that I want to add / update when a new row is added to a view but I am struggling to make this work.
My target table is Course Learner Progress and my view is Quiz_Results_For_Course_Learner_Progress.
I have created the view from a table that records quiz scores and is populated by a Zapier zap so I can't add a trigger to that table - which is why I have created the view of that table.
My trigger is as follows:
create trigger Update_Course_Progress_Quiz_Scores
on Quiz_Results_For_Course_Learner_Progress
instead of insert
as
declare #CompanyID int = (select CompanyID
from LEARNERS.dbo.ILR
where LEARNERID = LearnerID)
Merge Course_Learner_Progress as t
using inserted as s on t.CourseID = s.CourseID
and t.ModuleID = s.ModuleID
and t.LearnerID = s.LearnerID
and t.ContentID = s.ContentID
when not matched by Target then
insert (CompanyID, LearnerID, CourseID, ModuleID, ContentID, ContentType, Passmark, Score, [Status])
values (#CompanyID, s.LearnerID, s.CourseID, s.ModuleID,
s.ContentID, 4, s.Passmark, s.Quiz_Score, s.Status)
when matched then
update
set t.Score = s.Quiz_Score,
t.Status = s.[Status]
;
I can create the trigger successfully, but it doesn't insert / update a row in the Course_Learner_Progress table.
I would really welcome some help on this

Update row with values from select on condition, else insert new row

I'm need to run a calculation for month every day. If the month period, exists already, I need to update it, else I need to create a new row for the new month.
Currently, I've written
declare #period varchar(4) = '0218'
DECLARE #Timestamp date = GetDate()
IF EXISTS(select * from #output where period=#period)
/* UPDATE #output SET --- same calculation as below ---*/
ELSE
SELECT
#period AS period,
SUM(timecard.tworkdol) AS dol_local,
SUM(timecard.tworkdol/currates.cdrate) AS dol_USD,
SUM(timecard.tworkhrs) AS hrs,
#Timestamp AS timestamp
FROM dbo.timecard AS timecard
INNER JOIN dbo.timekeep ON timecard.ttk = timekeep.tkinit
INNER JOIN dbo.matter with (nolock) on timecard.tmatter = matter.mmatter
LEFT JOIN dbo.currates with (nolock) on matter.mcurrency = currates.curcode
AND currates.trtype = 'A'
AND timecard.tworkdt BETWEEN currates.cddate1
AND currates.cddate2
WHERE timekeep.tkloc IN('06','07') AND
timecard.twoper = #period
SELECT * FROM #output;
How can simply update my row with the new data from my select.
Not sure what RDBMS are you using, but in SQL Server something like this would update the #output table with the results of the SELECT that you placed in the ELSE part:
UPDATE o
SET o.dol_local = SUM(timecard.tworkdol),
SET o.dol_USD = SUM(timecard.tworkdol/currates.cdrate),
SET o.hrs = SUM(timecard.tworkhrs),
set o.timestamp = #Timestamp
FROM #output o
INNER JOIN dbo.timecard AS timecard ON o.period = timecard.twoper
INNER JOIN dbo.timekeep ON timecard.ttk = timekeep.tkinit
INNER JOIN dbo.matter with (nolock) on timecard.tmatter = matter.mmatter
LEFT JOIN dbo.currates with (nolock) on matter.mcurrency = currates.curcode
AND currates.trtype = 'A'
AND timecard.tworkdt BETWEEN currates.cddate1
AND currates.cddate2
WHERE timekeep.tkloc IN('06','07') AND
timecard.twoper = #period
Also, I think you want to do an INSERT in the ELSE part, but you are doing just a SELECT, so I guess you should fix that too
The answer to this will vary by SQL dialect, but the two main approaches are:
1. Upsert (if your DBMS supports it), for example using a MERGE statement in SQL Server.
2. Base your SQL on an IF:
IF NOT EXISTS (criteria for dupes)
INSERT INTO (logic to insert)
ELSE
UPDATE (logic to update)

Sql Merge- How to Insert row from source table after deleting original from Target when matched?

I currently have a stored procedure that compares my target table (Ticket_Report) to my data source table (New_Tickets).
I am using a MERGE INTO statement to compare these two. When it finds a match between the two tables, it updates the current row in the target table with the corresponding info from the source table. If it dosent find a match, it inserts that data from the source table into the target table.
MERGE INTO Ticket_REPORT T1
USING #New_Tickets T2
ON T1.TICKET_NO=T2.TICKET_NO
WHEN MATCHED THEN
UPDATE SET
T1.TICKET_NO = T2.TICKET_NO,
T1.ASSIGNED_GROUP = T2.ASSIGNED_GROUP,
T1.ASSIGNEE = T2.ASSIGNEE,
T1.FNAME = T2.FNAME,
T1.LNAME = T2.LNAME
WHEN NOT MATCHED THEN
INSERT VALUES(
T2.TICKET_NO,
T2.ASSIGNED_GROUP,
T2.ASSIGNEE,
T2.FNAME,
T2.LNAME
);
I need to change this, so that when match is found on the Ticket Number, instead up just updating it, I need to A.)replace the current row in the Target table by deleting it, then B.)inserting the corresponding Row from the source table.
I currently have
MERGE INTO Ticket_REPORT T1
USING #New_Tickets T2
ON T1.Ticket_NO=T2.Ticket_NO
WHEN MATCHED THEN DELETE
//Now I need to replace what I deleted with the row from the source table
Which will delete the row from the Target Table. Now I want to Insert the corresponding Row from the Source Table. I am having trouble trying to do multiple things inside the WHEN MATCHED clause. Does anyone know how I can accomplish this?
*Side note: When matched, I could Insert the row from the Source, but then I how would I delete the original?
Strictly solution For your boss
First put the matched records into one temp table
Next use Merge query to delete the matched records from target table and Insert the unmatched records
Finally insert the records from temp table to target table
Try something like this
select * into #temp
from #New_Tickets T2
where exists(select 1
from Ticket_REPORT T1
where T1.Ticket_NO=T2.Ticket_NO)
MERGE INTO Ticket_REPORT T1
USING #New_Tickets T2
ON T1.TICKET_NO=T2.TICKET_NO
WHEN MATCHED THEN DELETE
WHEN NOT MATCHED THEN
INSERT VALUES(
T2.TICKET_NO,
T2.ASSIGNED_GROUP,
T2.ASSIGNEE,
T2.FNAME,
T2.LNAME
);
insert into Ticket_REPORT (col1,col2,..)
select col1,col2,..
from #temp
Note :
What has to be done is delete the matched records from target table and insert all then records from source table to target table
One approach is to subquery your MERGE statement. You can filter the output based on the action (INSERT, UPDATE or DELETE). The filtered records can then be INSERTED, UPDATED or DELETED.
This is a common technique for loading slowly changing dimensions into a data warehouse.
My example uses the follow temp tables:
SETUP
/* Create and populate sample tables.
*/
CREATE TABLE #NewTicket
(
Id INT
)
;
CREATE TABLE #TicketReport
(
Id INT
)
;
INSERT INTO #NewTicket
(
Id
)
VALUES
(1),
(2),
(3)
;
INSERT INTO #TicketReport
(
Id
)
VALUES
(3),
(4),
(5)
;
In the example 1, 2 and 3 appear in new ticket. 3, 4 and 5 appear in ticket report. 3 is deleted by the MERGE statement and INSERTED by the outer query.
EXAMPLE
/* Filter the results from the sub query
* for deleted records.
* These are then appended in the main
* outer query.
*/
INSERT INTO #TicketReport
(
Id
)
SELECT
Id
FROM
(
/* MERGE statments can be used as a sub query.
* You'll need the OUTPUT clause for this to work.
* The column $action describes what happened to each record.
*/
MERGE
#TicketReport AS t
USING #NewTicket AS s ON s.Id = t.Id
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
Id
)
VALUES
(
Id
)
OUTPUT
$action,
s.*
) AS r
WHERE
[$Action] = 'DELETE'
;
/* View the final result.
*/
SELECT
*
FROM
#TicketReport
ORDER BY
Id
;

Looping table valued parameter values passed as parameter to stored procedure for checking a value

I have a stored procedure that receives a TVP as input. Now, I need to check the received data for a particular ID in a primary key column. If it exists, then I just need to update the table using those new values for other column (sent via TVP). Else, do an insert.
How to do it?
CREATE PROCEDURE ABC
#tvp MyTable READONLY
AS
IF EXISTS (SELECT 1 FROM MYTAB WHERE ID= #tvp.ID)
DO update
ELSE
Create
Just wondering the if exists loop I did is correct. I reckon its wrong as it will only check for first value and then update. What about other values? How should I loop through this?
Looping/CURSOR is the weapon of last resort, always search for solution that is SET based, not ROW based.
You should use MERGE which is designed for this type of operation:
MERGE table_name AS TGT
USING (SELECT * FROM #tvp) AS SRC
ON TGT.id = SRC.ID
WHEN MATCHED THEN
UPDATE SET col = SRC.col
WHEN NOT MATCHED THEN
INSERT (col_name, col_name2, ...)
VALUES (SRC.col_name1, SRC.col_name2, ...)
If you don't like MERGE use INSERT/UPDATE:
UPDATE table_name
SET col = tv.col,
...
FROM table_name AS tab
JOIN #tvp AS tv
ON tab.id = tv.id
INSERT INTO table_name(col1, col2, ...)
SELECT tv.col1, tv.col2, ...
FROM table_name AS tab
RIGHT JOIN #tvp AS tv
ON tab.id = tv.id
WHERE tab.id IS NULL

Resources