Converting Oracle upsert to AzureSQL(T-SQL) prepared statement - tsql

I want to migrate upsert queries from Oracle DB to AzureSQL. Below shows an Oracle prepared statement that takes values from dual and does an upsert operation on the DUMMY table.
MERGE INTO DUMMY a
USING (SELECT ? ID,
? NAME,
? SIZE from dual) b
ON (a.ID = b.ID)
WHEN MATCHED THEN
UPDATE
SET a.ID = b.ID,
a.NAME = b.NAME,
a.SIZE = b.SIZE
WHEN NOT MATCHED THEN
INSERT(a.ID,
a.NAME,
a.SIZE)
VALUES ( b.ID,
b.NAME,
b.SIZE)
I also asked for a migration from Oracle to Postgres earlier. This is the PostgreSQL version that I asked. I am looking for a way to convert into AzureSQL now.

Upsert for T-SQL.
MERGE dbo.table_name AS [Target]
USING (SELECT 1 AS Id, 'name' as t_name, 1 as size) AS [Source]
ON [Target].Id = [Source].Id
WHEN MATCHED THEN
UPDATE SET [Target].name = [Source].t_name, [Target].size = [Source].size
WHEN NOT MATCHED THEN
INSERT (Id, name, size) VALUES ([Source].Id, [Source].t_name, [Source].size);
Use parameter value for preparing source table
DECLARE #id int = 1,
#name varchar(10) = 'ABC',
#size int = 5
MERGE dbo.table_name AS [Target]
USING (SELECT #id AS Id, #name as t_name, #size as size) AS [Source]
ON [Target].Id = [Source].Id
WHEN MATCHED THEN
UPDATE SET [Target].name = [Source].t_name, [Target].size = [Source].size
WHEN NOT MATCHED THEN
INSERT (Id, name, size) VALUES ([Source].Id, [Source].t_name, [Source].size);
Please check from url https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=13d32c099991dc3001fe4a8cd0b3fc77

Related

Joining two one-to-many tables duplicates records

I have 3 tables, Transaction, Transaction_Items and Transaction_History.
Where the Transaction is the parent table, while Transaction_Items and Transaction_History are the children tables, with one to many relationship.
When i try to join those tables together, if i have 2+ Transaction_History records, or 2+ Transaction_Items i get duplicated or triplicated record results.
This is the SQL query im currently using which works, but what worries me that in the future if i have to Join another one-to-many table, it will duplicate the results again.
I found a workaround for this, but i was just wondering if there is a better and cleaner way to do this ?
The results should be a PostgreSQL JSON array which will contain the Transaction_Items and Transaction_History
SELECT
TR.id AS transaction_id,
TR.transaction_number,
TR.status,
TR.status AS status,
to_json(TR_INV.list),
COUNT(TR_INV) item_cnt,
COUNT(THR) tr_cnt,
json_agg(THR)
FROM transaction_transaction AS TR
LEFT JOIN (
SELECT
array_agg(t) list, -- this is a workaround method
t.transaction_id
FROM (
SELECT
TR_INV.transaction_id transaction_id,
IT.id,
IT.stock_number,
CAT.key category_key,
ITP.description description,
ITP.serial_number serial_number,
ITP.color color,
ITP.manufacturer manufacturer,
ITP.inventory_model inventory_model,
ITP.average_cost average_cost,
ITP.location_in_store location_in_store,
ITP.firearm_caliber firearm_caliber,
ITP.federal_firearm_number federal_firearm_number,
ITP.sold_price sold_price
FROM transaction_transaction_item TR_INV
LEFT JOIN inventory_item IT ON IT.id = TR_INV.item_id
LEFT JOIN inventory_itemprofile ITP ON ITP.id = IT.current_profile_id
LEFT JOIN inventory_category CAT ON CAT.id = ITP.category_id
LEFT JOIN inventory_categorytype CAT_T ON CAT_T.id = CAT.category_type_id
) t
GROUP BY t.transaction_id
) TR_INV ON TR_INV.transaction_id = TR.id
LEFT JOIN transaction_transactionhistory THR ON THR.transaction_id = TR.id
AND (THR.audit_code_id = 44 OR THR.audit_code_id = 27 OR THR.audit_code_id = 28)
WHERE TR.store_id = 21
AND TR.transaction_type = 'Pawn_Loan' AND TR.date_made >= '2018-10-08'
GROUP BY TR.id, TR_INV.list
What you want to do can be achieved by not using joins, as shown below.
Because your actual tables have so many columns that I don't know and should not care. I just created the simplest forms of them for demonstration.
CREATE TABLE transactions (
tid serial PRIMARY KEY,
name varchar(40) NOT NULL
);
CREATE TABLE transaction_histories (
hid serial PRIMARY KEY ,
tid integer REFERENCES transactions(tid),
history varchar(40) NOT NULL
);
CREATE TABLE transaction_items (
iid serial PRIMARY KEY ,
tid integer REFERENCES transactions(tid),
item varchar(40) NOT NULL
);
INSERT INTO transactions(tid,name) Values(1, 'transaction');
INSERT INTO transaction_histories(tid, history) Values(1, 'history1');
INSERT INTO transaction_histories(tid, history) Values(1, 'history2');
INSERT INTO transaction_items(tid, item) Values(1, 'item1');
INSERT INTO transaction_items(tid, item) Values(1, 'item2');
select
t.*,
(select count(*) from transaction_histories h where h.tid= t.tid) h_count ,
(select json_agg(h) from transaction_histories h where h.tid= t.tid) h ,
(select count(*) from transaction_items i where i.tid= t.tid) i_count ,
(select json_agg(i) from transaction_items i where i.tid= t.tid) i
from transactions t;

TSQL: Update where count (select * from table) = 0

I just want to know if this is possible
To select or update data, but only if another query returns zero results
so something like this
Update A from tableA A
Set A.value = 'test'
where count(select * from tableB Where B.date = A.date) = 0
Yes, you should be able to write an UPDATE query with this logic. You may use an EXISTS clause here:
UPDATE tableA a
SET value = 'test'
WHERE NOT EXISTS (SELECT 1 FROM tableB b WHERE a.date = b.date);
Probably this:
Update tableA
Set tableA.value = 'test'
where (select count(*) from tableB B Where B.date = tableA.date) = 0

Updating a CTE table fail cause of derived or constant field

I'm using MS-SQL 2012
WITH C1
(
SELECT ID, 0 as Match, Field2, Count(*)
FROM TableX
GROUP BY ID, Fields2
)
UPDATE C1 SET Match = 1
WHERE ID = (SELECT MATCHING_ID FROM AnotherTable WHERE ID = C1.ID)
This TSQL statement gives me the following error:
Update or insert of view or function 'C1' failed because it contains a derived or constant field.
Ideally I would like to create a "fake field" named Match and set its default value to 0. Then with the update I would like to Update ONLY the records that have an existing entry on the "AnotherTable".
Any thoughts what am I doing wrong?
Thanks in advanced.
Try doing a Left Outer Join like
SELECT x.ID, ISNULL(a.Matching_ID, 0) as Match, x.Field2, Count(*)
FROM TableX x
LEFT OUTER JOIN AnotherTable a on x.ID = a.ID
GROUP BY x.ID, ISNULL(a.Matching_ID, 0), x.Fields2
without the need of a C1
If I am understanding correctly, the problem is that you are trying to update the CTE table. If you update the table directly you should be fine.
Does this modified version help?
SELECT t.ID
, CASE WHEN (EXISTS (SELECT MATCHING_ID FROM AnotherTable WHERE ID = t.ID)) THEN 1 ELSE 0 END
,t.Field2
,Count(*)
FROM TableX t
GROUP BY ID, Fields2

Subquery in update doesn't see already updated records

I have a few thousand records with a duplicate sortorder (which causes duplicate entries in other queries), so I'm trying to set a correct sort order for all those records.
First I set them all to -1 so the sortorder would start from 0, and then I execute this query:
UPDATE op.customeraddress SET sortorder = (SELECT MAX(ca.sortorder) + 1
FROM op.customeraddress AS ca
WHERE ca.customerid = customeraddress.customerid)
WHERE id IN (<subquery for IDs>)
The problem is that the MAX() in the subquery always seems to return the same value - it doesn't know about an earlier update.
The query works fine if I manually apply it record by record.
Any ideas on how to do this without having to resort to looping?
This should do it:
with new_order as
(
select ctid as rid,
row_number() over (partition by customerid order by sortorder) as rn
from customeraddress
)
update customeraddress ca
set sortorder = new_order.rn
where ca.ctid = new_order.rid;
and ca.id IN (<subquery for IDs>);
No need to reset the sortorder before running this, it will renumber all customeraddresses for a one customerid according to the old order.
You need PostgreSQL 9.1 for the above solution (writeable CTEs)
For previous version this should do it:
update customeraddress ca
set ca.sortorder = t.sortorder
from
(
select ctid as rid,
row_number() over (partition by customerid order by sortorder) as rn
from customeraddress
) t
where ca.ctid = t.rid
and ca.id IN (<subquery for IDs>);
You could use a sequence:
CREATE TEMPORARY SEQUENCE sort_seq;
UPDATE op.customeraddress SET sort_order = (
SELECT nextval('sort_seq')
FROM op.customeraddress AS ca
WHERE ca.customerid = customeraddress.customerid
) WHERE id IN ...

Bulk update a column in Oracle 11G

I have two tables say Table1 and Table2 that contains the following column with which I should join and perform an update a column of Table1 with the value of the same column present in Table2.
Columns for Join condition:
Table1.mem_ssn and Table2.ins_ssn
Table1.sys_id and Table2.sys_id
Table1.grp_id and Table2.grp_id
Column to update:
Table1.dtofhire=Table2.dtofhire
I need a way to bulk update (using single update query without looping) the above mentioned column in Oracle 11G.
Table1 does not contain any key constraint specified since it will be used as a staging table for Data upload.
Please help me out to update the same.
You can use the MERGE statement.
It should look something like this:
MERGE INTO table1 D
USING (SELECT * FROM table2 ) S
ON (D.mem_ssn = S.ins_ssn and D.sys_id = S.sys_id and D.grp_id=S.grp_id)
WHEN MATCHED THEN
UPDATE SET D.dtofhire=S.dtofhire;
UPDATE:
Since you have more than one row in table2 with the same (ins_ssn,sys_id,grp_id) and you want the max dtofhire, you should change the query in the using clause:
MERGE INTO table1 D
USING (SELECT ins_ssn, sys_id, grp_id, max(dtofhire) m_dtofhire
FROM table2
GROUP BY ins_ssn,sys_id,grp_id) S
ON (D.mem_ssn = S.ins_ssn and D.sys_id = S.sys_id and D.grp_id=S.grp_id)
WHEN MATCHED THEN
UPDATE SET D.dtofhire=S.m_dtofhire;
The query that I used to arrive the functionality is seen below
UPDATE table1 T2
SET dtofhire = (SELECT Max(dtofhire) AS dtofhire
FROM table2 T1
WHERE T2.mem_ssn = T1.ins_ssn
AND T2.sys_id = T1.sys_id
AND T2.grp_id = T1.grp_id
GROUP BY ins_ssn,
sys_id,
grp_id)
WHERE ( mem_ssn, sys_id, grp_id ) IN (SELECT ins_ssn,
sys_id,
grp_id
FROM table2 );