Using INSERT and/or UPDATE together from a single CTE - sql

I'm trying to query a cte using a cte called s4 and based on the results either insert or update to another table eventslog. I was unable to do this in one single query and needed to insert the data first in a temp table. I would like to get rid of insertion to a temp table part and just insert or update it directly.
I think I was having issues with calling that cte more than once. Is there a work around? How can I either insert or update into a table by querying a single cte? Any help is most appreciated.
SQL;
,ss4
AS (SELECT DISTINCT h.groupid,
h.eventid,
Sum(h.vcheck) AS tot ,
max(h.eventtime) as eventtime
FROM ss3 h
GROUP BY h.groupid,
h.eventid
)
INSERT INTO #glo
(eventtime,
eventid,
groupid,
vcheck)
SELECT DISTINCT i.eventtime,
i.eventid,
i.groupid,
i.tot
FROM ss4 i
INSERT INTO eventslog
(eventtime,
eventid,
groupid)
SELECT DISTINCT j.eventtime,
j.eventid,
j.groupid
FROM #glo j
WHERE
j.vcheck = 0
AND NOT EXISTS(SELECT eventid
FROM eventslog
WHERE eventid = j.eventid
AND groupid = j.groupid
AND clearedtime IS NULL)
UPDATE k
SET k.clearedtime = l.eventtime
FROM eventslog k
RIGHT JOIN #glo l
ON k.groupid = l.groupid
AND k.eventid = l.eventid
WHERE l.vcheck > 0
AND k.groupid = l.groupid

I was working on something similar myself recently. I used a merge statement to handle doing an insert or update using a CTE.
Example below:
;WITH cte AS
(
SELECT id,
name
FROM [TableA]
)
MERGE INTO [TableA] AS A
USING cte
ON cte.ID = A.id
WHEN MATCHED
THEN UPDATE
SET A.name = cte.name
WHEN NOT MATCHED
THEN INSERT
VALUES(cte.name);

You can use Merge command which is available in SQL 2008,
A very good tutorial is available here
http://www.made2mentor.com/2013/05/writing-t-sql-merge-statements-the-right-way/

Related

Increase number into insert from select in SQL

In the code there is a lot of rows will be inserted and the code gives me this error
[23000][2601] Cannot insert duplicate key row in object 'dbo.Estimates' with unique index 'IX_Estimates_EstimateNumber'. The duplicate key value is (10005)
Code:
INSERT INTO dbo.Estimates (EstimateNumber, Date, Comments, CustomerId)
SELECT
(SELECT MAX(Number) + 1 FROM EstimateNumber),
po.DateReceived,
po.Notes,
(SELECT Id FROM Customers WHERE Name = po.Customer)
FROM
staging.PricingTable po
LEFT JOIN
dbo.Estimates Es ON Es.Date = po.DateReceived
WHERE
Es.Date IS NULL;
The issue is happening because select MAX(Number)+1 FROM EstimateNumber in the select clause will not work as you expect and always return the same value for each row. Since there is an unique index, it will block the data insert. You can execute the select statement to verify this.
You can use a ROW_NUMBER() to fix this.
A sample sql code is follows :
declare #maxval integer ;
select #maxval = max(Number) from EstimateNumber ;
insert into dbo.Estimates ( EstimateNumber, Date,Comments, CustomerId )
select #maxval + ROW_NUMBER() OVER (ORDER BY c.Id), po.DateReceived, po.Notes, c.Id
from staging.PricingTable po
join Customers c on c.Name = po.Customer
left join dbo.Estimates Es on Es.Date = po.DateReceived
where Es.Date is null;
here I have used a local variable to hold max(Number) and incremenetd it using row_number. also moved the Cutomers from nested-select to a join

Rewrite query without using temp table

I have a query that is using a temp table to insert some data then another select from to extract distinct results. That query by it self was fine but now with entity-framework it is causing all kinds of unexpected errors at the wrong time.
Is there any way I can rewrite the query not to use a temp table? When this is converted into a stored procedure and in entity framework the result set is of type int which throws an error:
Could not find an implementation of the query pattern Select not found.
Here is the query
Drop Table IF EXISTS #Temp
SELECT
a.ReceiverID,
a.AntennaID,
a.AntennaName into #Temp
FROM RFIDReceiverAntenna a
full join Station b ON (a.ReceiverID = b.ReceiverID) and (a.AntennaID = b.AntennaID)
where (a.ReceiverID is NULL or b.ReceiverID is NULL)
and (a.AntennaID IS NULL or b.antennaID is NULL)
select distinct r.ReceiverID, r.ReceiverName, r.receiverdescription
from RFIDReceiver r
inner join #Temp t on r.ReceiverID = t.ReceiverID;
No need for anything fancy, you can just replace the reference to #temp with an inner sub-query containing the query that generates #temp e.g.
select distinct r.ReceiverID, r.ReceiverName, r.receiverdescription
from RFIDReceiver r
inner join (
select
a.ReceiverID,
a.AntennaID,
a.AntennaName
from RFIDReceiverAntenna a
full join Station b ON (a.ReceiverID = b.ReceiverID) and (a.AntennaID = b.AntennaID)
where (a.ReceiverID is NULL or b.ReceiverID is NULL)
and (a.AntennaID IS NULL or b.antennaID is NULL)
) t on r.ReceiverID = t.ReceiverID;
PS: I haven't made any effort to improve the query overall like Gordon has but do consider his suggestions.
First, a full join makes no sense in the first query. You are selecting only columns from the first table, so you need that.
Second, you can use a CTE.
Third, you should be able to get rid of the SELECT DISTINCT by using an EXISTS condition.
I would suggest:
WITH ra AS (
SELECT ra.*
FROM RFIDReceiverAntenna ra
Station s
ON s.ReceiverID = ra.ReceiverID AND
s.AntennaID = ra.AntennaID)
WHERE s.ReceiverID is NULL
)
SELECT r.ReceiverID, r.ReceiverName, r.receiverdescription
FROM RFIDReceiver r
WHERE EXISTS (SELECT 1
FROM ra
WHERE r.ReceiverID = ra.ReceiverID
);
You can use CTE instead of the temp table:
WITH
CTE
AS
(
SELECT
a.ReceiverID,
a.AntennaID,
a.AntennaName
FROM
RFIDReceiverAntenna a
full join Station b
ON (a.ReceiverID = b.ReceiverID)
and (a.AntennaID = b.AntennaID)
where
(a.ReceiverID is NULL or b.ReceiverID is NULL)
and (a.AntennaID IS NULL or b.antennaID is NULL)
)
select distinct
r.ReceiverID, r.ReceiverName, r.receiverdescription
from
RFIDReceiver r
inner join CTE t on r.ReceiverID = t.ReceiverID
;
This query will return the same results as your original query with the temp table, but its performance may be quite different; not necessarily slower, it can be faster. Just something that you should be aware about.

Problems shortening a SQL query

I am trying to make a query that works with a temp table, work without that temp table
I tried doing a join in the subquery without the temp table but I don't get the same results as the query with the temp table.
This is the query with the temp table that works as I want:
create table #results(
RowId id_t,
LastUpdatedAt date_T
)
insert into #results
select H.RowId, H.LastUpdatedAt from MemberCarrierMap M Join MemberCarrierMapHistory H on M.RowId = H.RowId
update MemberCarrierMap
set CreatedAt = (select MIN(LastUpdatedAt) from #results r where r.rowId = MemberCarrierMap.rowId)
Where CreatedAt is null;
and here is the query I tried without the temp table that doesn't work like the above:
update MemberCarrierMap
set CreatedAt = (select MIN(MH.LastUpdatedAt) from MemberCarrierMapHistory MH join MemberCarrierMap M on MH.RowId = M.RowId where MH.RowId = M.RowId )
Where CreatedAt is null;
I was expecting the 2nd query to work as the first but It is not. Any suggestions on how to achieve what the first query does without the temp table?
This should work:
update M
set M.CreatedAt = (select MIN(MH.LastUpdatedAt) from MemberCarrierMapHistory MH WHERE MH.RowId = M.RowId)
FROM MemberCarrierMap M
Where M.CreatedAt is null;
Your question is more or less a duplicate of this answer. There, you will find multiple solutions. But the ones that implement correlated subqueires are less performant than the one that simply uses an uncorrelated aggregation subquery inside a join.
Applying it to your situation, you will have this:
update m
set m.createdDate = hAgg.maxVal
from memberCarrierMap m
join (
select rowId, max(lastUpdatedAt) as maxVal
from memberCarrierMapHistory
group by rowId
) as hAgg
on m.rowId = hAgg.rowId
where m.createdAt is null;
Basically, it's more performant because it is more expensive to run aggregations and filterings on a row-by-row basis (which is what happens in a correlated subquery) than to just get the aggregations out of the way all at once (joins tend to happen early in processing) and perform the match afterwards.

Select and Insert rows that have changed

I have the following query:
select z.EncID, z.PatName, convert(datetime,z.ArriveDtTm) ArriveDtTm,
z.Status, convert(datetime,z.UpdateTime) UpdateTime
from
OPENQUERY(linked_db, '
select e.encntr_id as EncID, p.name_full_formatted as PatName,
e.arrive_dt_tm as ArriveDtTm , e.referring_comment as Status,
e.updt_dt_tm as UpdateTime
from
TRACKING_CHECKIN TC,
encounter e,
tracking_item ti,
person p
where
tc.checkin_dt_tm >= sysdate-1
and TC.TRACKING_GROUP_CD= 5328362.00
and ti.tracking_id = tc.tracking_id
and ti.encntr_id = e.encntr_id
and e.disch_dt_tm is null
and e.person_id = p.person_id
and e.loc_nurse_unit_cd = 664926.00
order by e.arrive_dt_tm, e.disch_dt_tm desc'
)z
I want to take the results and insert into another table if the rows is different in any way. What is the best way to accomplish?
The code will be in a job that runs every 5 minutes.
thanks
Use SQL server Merge for SQL 2008+, or fall back to manual insert and update for lower versions of SQL as shown in the query.
Note : It is just a quick query. It was not validated against data.
SELECT z.encid,
z.patname,
CONVERT(DATETIME, z.arrivedttm) ArriveDtTm,
z.status,
CONVERT(DATETIME, z.updatetime) UpdateTime
INTO #temp_table
FROM Openquery (linked_db, 'Link Query') AS z;
/****************** USING MERGE For SQL 2008+ ********************/
MERGE destinationtable A USING #temp_table B ON A.encid= b.encid WHEN matched THEN
UPDATE
SET A.patname = B.patname,
A.arrivedttm = B.arrivedttm,
A.status= B.status WHEN NOT matched THEN
INSERT (encid,
patname,
arrivedttm,
status)
VALUES (b.encid,
b.patname,
b.arrivedttm,
b.status) END
SELECT * FROM destinationtable ;
/***************** Without MERGE For SQL 2000 *******************/
-- Insert Part
INSERT INTO destinationtable (encid, patname, arrivedttm, status)
SELECT Col1,
Col2
FROM #temp_table A
LEFT JOIN destinationtable B ON A.encid = B.encid
WHERE B.encid IS NULL
-- Update Part
UPDATE A
SET A.patname = B.patname,
A.arrivedttm = B.arrivedttm,
A.status= B.status
FROM destinationtable A
INNER JOIN #temp_table B ON A.encid = B.encid
SELECT * FROM destinationtable ;
/*****************************************************************/

Update with Sub Query Derived Table Error

I have the following SQL statement to simply update the #temp temp table with the latest package version number in our Sybase 15 database.
UPDATE t
SET versionId = l.latestVersion
FROM #temp t INNER JOIN (SELECT gp.packageId
, MAX(gp.versionId) latestVersion
FROM Group_Packages gp
WHERE gp.groupId IN (SELECT groupId
FROM User_Group
WHERE userXpId = 'someUser')
GROUP BY gp.packageId) l
ON t.packageId = l.packageId
To me (mainly Oracle & SQL Server experience more than Sybase) there is little wrong with this statement. However, Sybase throws an exception:
You cannot use a derived table in the FROM clause of an UPDATE or DELETE statement.
Now, I don't get what the problem is here. I assume it is because of the aggregation / GROUP BY being used. Of course, I could put the sub query in a temp table and join on it but I really want to know what the 'correct' method should be and what the hell is wrong.
Any ideas or guidance would be much appreciated.
It seems that SYBASE doesn't support nested queries in UPDATE FROM class. Similar problem
Try to use this:
UPDATE #temp
SET versionId = (SELECT MAX(gp.versionId) latestVersion
FROM Group_Packages gp
WHERE gp.packageId=#temp.packageId
and
gp.groupId IN (SELECT groupId
FROM User_Group
WHERE userXpId = 'someUser')
)
And also what if l.latestVersion is NULL? Do you want update #temp with null ? if not then add WHERE:
WHERE (SELECT MAX(gp.versionId)
....
) is not null
I guess this is a limitation of Sybase (not allowing derived tables) in the FROM clause of the UPDATE. Perhaps you can rewrite like this:
UPDATE t
SET t.versionId = l.versionId
FROM #temp t
INNER JOIN
Group_Packages l
ON t.packageId = l.packageId
WHERE
l.groupId IN ( SELECT groupId
FROM User_Group
WHERE userXpId = 'someUser')
AND
l.versionId =
( SELECT MAX(gp.versionId)
FROM Group_Packages gp
WHERE gp.groupId IN ( SELECT groupId
FROM User_Group
WHERE userXpId = 'someUser')
AND gp.packageId = l.packageId
) ;
Your table alias for #temp is called "t" and your original table is called "t".
My guess is that this is the problem.
I think you want to start with:
update #temp
Does this syntax work in Sybase?
update dstTable T
set (T.field1, T.field2, T.field3) =
(select S.value1, S.value2, S.value3
from srcTable S
where S.key = T.Key);
I believe the correlated subquery can be as complicated as you like (including using CTE. etc). It just has to project the right number (and type) of values.