Select and Insert rows that have changed - sql

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 ;
/*****************************************************************/

Related

UPDATE from another table with multiple WHERE criteria

In Postgres 9.5, I want to connect to another DB using Postgres' dblink, get data and then use them to update another table.
-- connect to another DB, get data from table, put it in a WITH
WITH temp_table AS
(
SELECT r_id, descr, p_id
FROM
dblink('myconnection',
'SELECT
r_id, descr, p_id
FROM table
WHERE table.p_id
IN (10,20);'
)
AS tempTable(r_id integer, descr text, p_id integer)
)
-- now use temp_table to update
UPDATE anothertable
SET
descr =temp_table.descr
FROM anothertable AS x
INNER JOIN temp_table
ON
x.r_id = temp_table.r_id
AND
x.p_id = temp_table.p_id
AND
x.p_id IN (2) ;
dblink works fine and if I do select * from temp_table before the UPDATE, it has data.
The issue is the UPDATE itself. It runs with no errors, but it never actually updates the table.
I tried changing the UPDATE to:
UPDATE anothertable
SET
descr =temp_table.descr
FROM anothertable AS x , temp_table
WHERE x.r_id = temp_table.r_id
AND
x.p_id = temp_table.p_id
AND
x.p_id IN (2) ;
Same as above: runs with no errors, but it never actually updates the table.
I also tried to change the UPDATE to:
UPDATE anothertable
INNER JOIN temp_table
ON x.r_id = temp_table.r_id
AND
x.p_id = temp_table.p_id
AND
x.p_id IN (2)
SET descr =temp_table.descr
But I get:
ERROR: syntax error at or near "INNER" SQL state: 42601
Character: 1894
How can I fix this to actually update?
Don't repeat the target table in the FROM clause of the UPDATE:
WITH temp_table AS ( ... )
UPDATE anothertable x
SET descr = t.descr
FROM temp_table t
WHERE x.r_id = t.r_id
AND x.p_id = t.p_id
AND x.p_id IN (2);
Or simplified:
...
AND x.p_id = 2
AND t.p_id = 2
The manual:
Do not repeat the target table as a from_item unless you intend a self-join (in which case it must appear with an alias in the from_item).
Related:
UPDATE statement with multiple joins in PostgreSQL
SQL update query with substring WHERE clause

Stored procedure that takes in a dynamic number of joins

I am trying to build a stored procedure that takes in a dynamic number of tables and joins them.
SELECT a.Account
FROM Database.Accounts a
JOIN (SELECT Table_Name FROM Database.TableStrings WHERE Row = 1) b ON a.Account = b.Account
JOIN (SELECT Table_Name FROM Database.TableStrings WHERE Row = 2) c ON a.Account = c.Account
JOIN (SELECT Table_Name FROM Database.TableStrings WHERE Row = 3) d ON a.Account = d.Account
...
/*Where the number of joins is equal to COUNT(Table_Name) FROM Database.TableStrings*/
or
first = SELECT a.Account
FROM Database.Accounts a
JOIN (SELECT Table_Name FROM Database.TableStrings WHERE Row = 1) b ON a.Account = b.Account
second = SELECT a.Account
FROM first a
JOIN (SELECT Table_Name FROM Database.TableStrings WHERE Row = 2) b ON a.Account = b.Account
...
Below is an example of the tables and the expected output:
---Database.Accounts---
Account (Integer)
------------------------
111
222
333
444
555
---Database.TableStrings---
Table_Name (String):
------------------------
'Database.HasPhoneNumber'
'Database.HasEmail'
'Database.HasBankAccount'
---Database.HasPhoneNumber---
Account (Integer)
------------------------
111
444
---Database.HasEmail---
Account (Integer)
------------------------
111
222
---Database.HasBankAccount---
Account (Integer)
------------------------
111
222
555
With the example tables, the expected output would be Account 111.
I am having a hard time visualizing how to do this in a stored procedure, if it is even possible.
you may consider using recursive queries (cte) to handle the string concatenation. and run sp_executesql to execute generated sql string query
lets generate also your table aliases using char()
declare #strSQL nvarchar(max), #strSQLjoin nvarchar(max)
set #strSQL = 'SELECT a.Account
FROM Database.Accounts a ';
set #strSQLjoin = '';
with cte as (
select 'Database.PhoneNumbers' as TableStrings
union all select 'Database.Emails'
union all select 'Database.BankAccounts'
), cte2 as (
select TableStrings, (char(row_number() over (order by TableStrings) + 97)) as tbl
from cte)
select #strSQLjoin = coalesce(#strSQLjoin
+ ' JOIN (SELECT Table_Name FROM '+TableStrings+') '+tbl+' ON a.Account = '+tbl+'.Account '
,' JOIN (SELECT Table_Name FROM '+TableStrings+') '+tbl+' ON a.Account = '+tbl+'.Account')
from cte2;
set #strSQL=#strSQL + #strSQLjoin;
print #strSQL
exec sp_executesql #strSQL
I understand that this is not exactly what you asked for. But since adding new rows to the TableStrings table already implies schema changes (creating new tables), at this point you might as well update your stored procedure accordingly instead of using dynamic SQL
If that's an option then for example your query may look like
SELECT a.Account
FROM Database.Accounts a
where exists (select 1 from Database.HasPhoneNumber e where e.Account = a.Account)
and exists (select 1 from Database.HasEmaile e where e.Account = a.Account)
and exists (select 1 from Database.HasBankAccount e where e.Account = a.Account)
...
based on your sample data and your sample query, you can achieve what you want without using dynamic query
-- sample table
declare #Accounts table
(
Account int
)
declare #TableStrings table
(
Table_Name varchar(100),
Account int
)
-- sample data
insert into #Accounts
values (111), (222), (333), (444), (555)
insert into #TableStrings
values ('Database.HasPhoneNumber', 111), ('Database.HasPhoneNumber', 444),
('Database.HasEmail', 111), ('Database.HasEmail', 222),
('Database.HasBankAccount', 111), ('Database.HasBankAccount', 222),
('Database.HasBankAccount', 555)
select a.Account
from #Accounts a
inner join #TableStrings t on a.Account = t.Account
group by a.Account
having count(*) = (select count(distinct Table_Name) from #TableStrings)

Using INSERT and/or UPDATE together from a single CTE

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/

How to insert record into table result of other Select Query?

My requirement is to insert record from selected query result in to new table.
My query is
SELECT
a.Section_name, b.sectionname, a.SubQno, b.Qno, a.ans, b.Answ,
a.Exame_id, b.Exame_id AS Expr1, b.User_id, b.Start_time, b.End_time
FROM Question AS a
INNER JOIN Solve_Student_question AS b ON a.SubQno = b.Qno AND a.Section_name = b.sectionname
WHERE (b.User_id = 'gopal ram51765078')
Now this query result stored in to Temp table. How can I do this?
You can use the Insert Into ... Select idiom like so:
INSERT INTO TempTable
(...) --Columns
SELECT
a.Section_name, b.sectionname, a.SubQno, b.Qno, a.ans, b.Answ, a.Exame_id, b.Exame_id AS Expr1, b.User_id, b.Start_time, b.End_time
FROM Question AS a INNER JOIN
Solve_Student_question AS b ON a.SubQno = b.Qno AND a.Section_name = b.sectionname
WHERE (b.User_id = 'gopal ram51765078')
You can use Cursor also for this case
declare
cursor C is
select a.Section_name, b.sectionname, a.SubQno, b.Qno, a.ans,
b.Answ, a.Exame_id, b.Exame_id AS Expr1, b.User_id, b.Start_time, b.End_time
FROM Question AS a INNER JOIN
Solve_Student_question AS b ON a.SubQno = b.Qno AND a.Section_name = b.sectionname
WHERE (b.User_id = 'gopal ram51765078');
begin
for i in C
insert into tablename(Column1,Column2,Column3)
values(i.column1,i.Column2,i.Column3);
end loop;
Exit when last.record = 'TRUE';
end;
`
`
insert into [temptablename] (comma-separated list of column names)
(your Select Query Here)

JOIN Issue : Correct the SQL Statement to solve : ORA-01799: a column may not be outer-joined to a subquery

As you see below; how can I implement fx.ftf_validitystartdate= ... this lines value since oracle does not allow me to do it like this below
.
select * from acc_accounts acc
join kp_paramcore p on
acc.account_no = p.accountnum
acc.suffix = p.suffixc
LEFT JOIN ftf_rates fx
ON p.maturestart = fx.ftf_vadealtsinir
AND p.maturefinish = fx.ftf_vadeustsinir
AND fx.statusrec = 'A'
AND fx.currencycode = acc.currencsw_kod
AND fx.status= 'A'
and fx.ftf_validitystartdate= (SELECT MAX(ff.ftf_validitystartdate)
FROM ftf_rates ff
WHERE ff.status = 'A'
AND ff.statusrec = 'A'
AND v_CurrentDate BETWEEN ff.systemstartdate AND ff.systemfinishdate AND ff.currencycode = acc.currencsw_kod
)
It should work if you switch this to a where clause:
select *
from acc_accounts acc join
kp_paramcore p
on acc.account_no = p.accountnum and
acc.suffix = p.suffixc LEFT JOIN
ftf_rates fx
ON p.maturestart = fx.ftf_vadealtsinir and
p.maturefinish = fx.ftf_vadeustsinir and
fx.statusrec = 'A' and
fx.currencycode = acc.currencsw_kod and
fx.status= 'A'
where fx.ftf_validitystartdate= (SELECT MAX(ff.ftf_validitystartdate)
FROM ftf_rates ff
WHERE ff.status = 'A' and
ff.statusrec = 'A'
p.v_CurrentDate BETWEEN ff.systemstartdate AND ff.systemfinishdate AND ff.currencycode = acc.currencsw_kod
)
However, you lose the 'left outer join' characteristics, so you would also want to add: or fx.ftf_validitystartdate is null. I guess that v_CurrentDate comes from "p". It is always a good idea to use table aliases before column names.
However, I question whether the subquery is really needed. It is only needed when there is more than one record that meets the conditions inside the subquery. Otherwise, I think you can just change the on clause to be:
ON p.maturestart = fx.ftf_vadealtsinir and
p.maturefinish = fx.ftf_vadeustsinir and
fx.statusrec = 'A' and
fx.currencycode = acc.currencsw_kod and
fx.status= 'A'and
p.v_CurrentDate BETWEEN fx.systemstartdate AND fx.systemfinishdate
I publish the workaround with CTE and tested only in Oracle 11g.
To make test I create this schema:
create table t_a ( a int );
create table t_b ( a int);
create table t_c ( a int);
insert into t_a values (1);
insert into t_a values (2);
insert into t_a values (3);
insert into t_b values (1);
insert into t_b values (2);
insert into t_b values (3);
insert into t_c values (1);
insert into t_c values (2);
insert into t_c values (3);
At this time I force error with this query:
select *
from t_a
left outer join t_b
on t_a.a = t_b.a and
t_b.a = ( select max( a )
from t_c);
And now I rewrite query with CTE:
with cte (a ) as (
select a
from t_b
where t_b.a = ( select min( a )
from t_c)
)
select *
from t_a
left outer join cte
on t_a.a = cte.a;
This second query returns right results.
I rewrite your query with CTE:
with CTE as (
select * from ftf_rates
where ftf_validitystartdate= (SELECT MAX(ff.ftf_validitystartdate)
FROM ftf_rates ff
WHERE ff.status = 'A'
AND ff.statusrec = 'A'
AND v_CurrentDate BETWEEN ff.systemstartdate
AND ff.systemfinishdate
AND ff.currencycode = acc.currencsw_kod )
)
select * from acc_accounts acc
join kp_paramcore p on
acc.account_no = p.accountnum
acc.suffix = p.suffixc
LEFT JOIN CTE fx
ON p.maturestart = fx.ftf_vadealtsinir
AND p.maturefinish = fx.ftf_vadeustsinir
AND fx.statusrec = 'A'
AND fx.currencycode = acc.currencsw_kod
AND fx.status= 'A'
Notice, only tested in Oracle 11g. See #a_horse_with_no_name coment:
#danihp: CTEs were available long before Oracle 11g (I think they were
introducted in 9.1 maybe even earlier - but they are definitely
available in 10.x). 11.2 introduced recursive CTEs which is not needed
in this case. –