Update between 2 databases using dblink not working - sql

Basically I have 2 databases each containing 2 tables. I need to update what's in database 2 using a table in database 1.
Below is database 1 table 2 (db1 ta2)
pid | pname | pcompany | clocation | did | name
1 Pirates Argos London 1 Mary
Database 2 table 1 (db1 tb1)
pid | pname | pcompany |
1 Ba Ba Black Argos
Now I need to update 'Ba Ba Black' to Pirates meaning I need to update db2 tb1 from db1 ta2.
UPDATE tb1
SET name = ta1.name
WHERE ta1.name = (SELECT ta1.name FROM dblink('port=5432, dbname=db1 user=postgres password=12345',
'SELECT name FROM ta1'))
AS t1 (a integer, b character(20), c integer)
WHERE pid = 1;
But I have this error:
ERROR: syntax error at or near "AS"
LINE 5: AS t1 (a integer, b character(20), c integer)
Any idea what I might be messing up?

Add table / column aliases and (most probably) a WHERE condition like this:
UPDATE tb1 b
SET name = a.pname -- maybe you want to update *pname* instead?
FROM (
SELECT *
FROM dblink('port=5432 dbname=db1 user=postgres password=12345'
, 'SELECT pname FROM ta1 WHERE pid = 1')
AS t(pname text)
) a
WHERE b.pid = 1;
Assuming type text. Adapt to your actual type.
And no comma in the connection string.
I am not linking a to b since a returns a single row here.

I worked the following way:
DO$$
DECLARE
sql_statement TEXT;
BEGIN
sql_statement:='UPDATE public.ta2 SET name='''||NEW.name||''' WHERE pid=1;';
PERFORM public.dblink('port=5432 dbname=db1 user=postgres password=*',sql_statement);
RETURN NEW;
END;
$$
languaje plpgsql;
You can consult this link: https://www.postgresql.org/docs/9.2/static/plpgsql-trigger.html

Related

Update table according to matches found in second table

Having a heavy brain freeze here, all help is welcome.
Background info: working on a search function that should return the number of occurrences of one or multiple (user provided) keywords based on matches in another table.
The first table will actually be a temporary table inside a table function but for the sake of simplicity, let's make it a regular table.
CREATE TABLE search_query {
keyword VARCHAR (30),
occurrences INT
}
That "table" will contain the (sanitized) keywords the user provides. For example:
keyword occurrences
HOW 0
TO 0
CODE 0
THIS 0
"occurences" is 0 by default.
My second table contains a record for each significant keyword a product can have.
CREATE TABLE product_keywords {
productId INT,
keyword VARCHAR(30)
}
Sample data:
productId keyword
12 HOW
12 NOT
12 CODE
13 RANDOM
13 THIS
13 CODE
What I'm trying to do is build a query that will UPDATE the "occurrences" field in search_query based on keyword matches with product_keywords.
In this example, after the query runs, search_query should contain
HOW 1
TO 0
CODE 2
THIS 1
My efforts so far stranded because for the life of me, I can't figure out how to build the JOIN necessary for the update. I'm about here:
UPDATE destination_table
SET destination_table.occurrences = JN.matches
FROM search_query AS destination_table
INNER JOIN [where I freeze] AS JN
WHERE ....
Any help appreciated! Thanks, Chris
You need a subquery to return the matches and join it to the table:
UPDATE s
SET s.occurrences = g.counter
FROM search_query s INNER JOIN (
SELECT s.keyword, COUNT(*) counter
FROM search_query s INNER JOIN product_keywords p
ON p.keyword = s.keyword
GROUP BY s.keyword
) g ON g.keyword = s.keyword
See the demo.
Or for readability you could use a CTE:
WITH cte AS (
SELECT s.keyword, COUNT(*) counter
FROM search_query s INNER JOIN product_keywords p
ON p.keyword = s.keyword
GROUP BY s.keyword
)
UPDATE s
SET s.occurrences = c.counter
FROM search_query s INNER JOIN cte c
ON c.keyword = s.keyword
See the demo.
Results:
> keyword | occurrences
> :------ | ----------:
> HOW | 1
> TO | 0
> CODE | 2
> THIS | 1
A correlated subquery with UPDATE might be the simplest and fastest approach:
update search_query
set occurrences = (select count(*)
from product_keywords pk
where pk.keyword = search_query.keyword
);
For performance, you want an index on product_keywords(keywords).

Update inner join result Oracle

In my java code I have foreach loop which iterates though list
foreach(MyObject obj:list){
String status = obj.getStatus();
String is = obj.getId();
// DB call
1. To update Status in Table A
jdbcobj.updtastatus(status,id);
2. Get status from table B
String tableBStatu= jdbcobj.getstatufromtableB(status,id):
obj.setStatus(tableBStatus):
}
To avoid 2 dB calls in for loop I am using inner join and trying to achieve same output as above
I am using inner-join and get the new result set based on common field.I want to update the result set but I am unable to figure out how?
I have two tables "A" and "B".
Table "A" has columns id,name,statusA
Table "B" has columns id,city,statusB
As stated at start, I am using inner-join and my query looks like this.
Select A.id A.statusA,B.statusB FROM A INNER JOIN ON B where A.id=B.id
Which gives me result as "id", status from table "A" and status from table "B".
Now i want use the inner-join result, to update statusA column from table "A" and set value ="DONE"
And want to use the statusB column value in java object.
String statusfromColumnB = get statusB col value
and set in my java object like this
myObj.setStatus(statusfromColumnB)
Sample Data
Suggest a solution.
If I understand you correctly, an Oracle MERGE query could properly respond to your need :
Consider :
MERGE INTO A
USING B ON (A.id = B.id)
WHEN MATCHED THEN UPDATE SET A.statusA = B.statusB
This query will update the status in table A from that of the corresponding record in table B.
Oracle merge is a vendor-specific statement that is optimized for multi-rows upserts (inserts/updates).
Demo on DB Fiddle :
Select A.id, A.statusA, B.statusB FROM A INNER JOIN B ON A.id=B.id
ID | STATUSA | STATUSB
-: | :------ | :--------
1 | Pending | Initiated
2 | Pending | Completed
MERGE INTO A
USING B ON (A.id = B.id)
WHEN MATCHED THEN UPDATE SET A.statusA = B.statusB
2 rows affected
Select A.id, A.statusA, B.statusB FROM A INNER JOIN B ON A.id=B.id
ID | STATUSA | STATUSB
-: | :-------- | :--------
1 | Initiated | Initiated
2 | Completed | Completed
If you want to set statusA to a fixed value instead, then you could go :
MERGE INTO A
USING B ON (A.id = B.id)
WHEN MATCHED THEN UPDATE SET A.statusA = 'Finished'
Do you want something like this?
update a
set (status, somewhereelsecolumn) =
(select 'DONE', <whatever>
from b
where A.id = B.id
)
where exists (select 1 from b where a.id = b.id);

SQL query with two EXISTS statements behaving differently than expected

The following SQL query is intended to find label_item_lists which have label_items with given names.
SELECT lils.id FROM label_item_lists AS lils
INNER JOIN label_items AS items ON lils.id = items.label_item_list_id
WHERE EXISTS(SELECT * FROM label_item_lists WHERE items.name=?)
OR EXISTS(SELECT * FROM label_item_lists WHERE items.name=?)
It properly returns the ids of label_item_lists having an item with either name. However, the same query using the AND operator rather than OR returns no results.
SELECT lils.id FROM label_item_lists AS lils
INNER JOIN label_items AS items ON lils.id = items.label_item_list_id
WHERE EXISTS(SELECT * FROM label_item_lists WHERE items.name=?)
AND EXISTS(SELECT * FROM label_item_lists WHERE items.name=?)
There are definitely label_item_list entries that have label_items matching both names provided. In fact the OR SQL query returns the id twice for these entries, but the AND query returns no results. For this reason I think I might be missing an important piece of info on how these JOINed queries with EXISTS work. Thanks for any assistance!
----------------------------------------------------------------
| label_items | id | name | label_item_list_id |
----------------------------------------------------------------
| Row1 | 1 | foo | 1 |
----------------------------------------------------------------
| Row2 | 2 | bar | 1 |
----------------------------------------------------------------
| Row3 | 3 | bar | 2 |
----------------------------------------------------------------
--------------------------------
| label_item_lists | id |
--------------------------------
| Row1 | 1 |
--------------------------------
| Row2 | 2 |
--------------------------------
I want to return the first label_item_list but not the second, as it only has one of the two names I am searching for, 'foo' and 'bar'.
try changing the where condition from
WHERE EXISTS(SELECT * FROM label_item_lists WHERE items.name=?)
AND EXISTS(SELECT * FROM label_item_lists WHERE items.name=?)
to
WHERE EXISTS(SELECT * FROM label_item_lists lst WHERE lst.name=?)
AND EXISTS(SELECT * FROM label_item_lists lst WHERE lst.name=?)
In your query AND will not return anything because on same output row it will apply both filters which will never happen hence it is giving blank output.
And Or operator will never check condition after OR operator until first condition is false.
Try something like this, # is just a place holder to distinguish between two searches:
select * from label_items lil
where label_item_list_id
in (
select li.label_item_list_id from
label_items li
inner join label_items l1
on li.label_item_list_id = l1.label_item_list_id
and li.name <> l1.name
where concat(li.name,'#',l1.name) = 'foo#bar')
This is what I eventually came up with! I'm not 100% confident yet, but it has worked so far. I added a bit of functionality in Ruby and ActiveRecord to allow for as many necessary matches as desired and to return only those which match exactly (without any extra names not in the list).
items = ["foo", "bar"]
db = ActiveRecord::Base.connection
query = <<-EOS
SELECT lils.id FROM label_item_lists AS lils
JOIN label_items AS items ON items.label_item_list_id = lils.id
WHERE lils.id IN (
SELECT label_item_list_id FROM label_items AS items
WHERE items.name IN (#{(['?'] * items.length).join(',')})
) AND lils.id NOT IN (
SELECT label_item_list_id FROM label_items AS items
WHERE items.name NOT IN (#{(['?'] * items.length).join(',')})
)
GROUP BY lils.id
HAVING COUNT(DISTINCT items.name) = #{items.length}
EOS
query = ActiveRecord::Base.send(:sanitize_sql_array, [query, *(items*2)])
Basically it checks that a name is both IN the list of given names (items array) AND it also checks that the name IS NOT outside (or NOT IN) the list of given names. Any list of label_items that has a non-matching name is excluded by the latter IN query. This is helpful so that a label_item_list with both "foo" and "bar" but also "lorem" is not included.

How to convert this Access inner join query into SQL Server?

I want to write an update statement that compares the new code table to the old code by joining on the MASTER_ID. Then If the NEW_CODE.CODE = 1234 AND OLD_CODE.CODE_ID is not in the CODE_MAPPING table then SET NEW_CODE.CODE_ID = OLD_CODE.CODE_ID AND NEW_CODE.SEQ_NO = OLD_CODE.SEQ_NO. This is what I have so far but it does not seem to correctly update if I get a null value in the CODE_ID column. I think I need to do a self join but nothing I do seems to work any help would be greatly appreciated.
I have three tables with the following data:
**NEW_CODE**
MASTER_ID SEQ_NO CODE_ID CODE
100 0 XX 1234
200 0 5555
300 0 XX 1234
300 0 XX 1234
**OLD_CODE**
MASTER_ID SEQ_NO CODE_ID
100 1 D1
200 1 A1
300 1
300 2 Z1
**CODE_MAPPING**
CODE_ID
A1
B1
C1
D1
UPDATE STATEMENT:
UPDATE NEW
SET SEQ_NO = OLD.SEQ_NO,
CODE_ID = OLD.CODE_ID
FROM NEW_CODE NEW
INNER JOIN OLD_CODE OLD
ON NEW.MASTER_ID = OLD.MASTER_ID
LEFT JOIN CODE_MAPPING CM
ON OLD.CODE_ID = CM.CODE_ID
WHERE NEW.CODE = 1234
AND CM.CODE_ID IS NULL
The updated table should look like this:
**UPDATED NEW_CODE**
MASTER_ID SEQ_NO CODE_ID CODE
100 0 XX 1234
200 0 5555
300 1 1234
300 2 Z1 1234
So the only thing that should update is the two rows with a MASTER_ID of 300. But for some reason I am getting the following result instead:
**CURRENT RESULT NEW_CODE**
MASTER_ID SEQ_NO CODE_ID CODE
100 0 XX 1234
200 0 5555
300 2 Z1 1234
300 2 Z1 1234
As you have two rows with same identifier (MASTER_ID), tsql is needed instead of a simple statement.
Precondition for rows to update:
Every row in NEW could be matched to one row in OLD and reverse.
We need a criteria to identify updated rows in NEW. I use SEQ_NO=0 here.
a unique key for each row in OLD and NEW is needed. Your example data don't contain a unique key. So I add one to OLD and one to NEW. It can be dropped in the end.
alter table OLD add IDX int IDENTITY(1,1)
alter table NEW add IDX int IDENTITY(1,1)
declare a cursor on OLD (only rows to be updated)
DECLARE #IdxOld int
DECLARE #IdxNew int
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT idx
FROM OLD
WHERE NOT EXISTS (SELECT * FROM CODE_MAPPING CM
WHERE OLD.CODE_ID = CM.CODE_ID)
do foreach on OLD and update NEW
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #IdxId
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #IdxNew = Min(Idx) -- min to get only one row
FROM NEW
INNER JOIN OLD_CODE OLD ON NEW.MASTER_ID = OLD.MASTER_ID
WHERE NEW.CODE = 1234
AND OLD.IDX = #IdxOld -- current row from old
AND SEQ_NO=0 -- row not updated yet
UPDATE NEW
SET SEQ_NO = OLD.SEQ_NO,
CODE_ID = OLD.CODE_ID
FROM NEW_CODE NEW
INNER JOIN OLD_CODE OLD ON NEW.MASTER_ID = OLD.MASTER_ID
WHERE NEW.IDX = #IdxNew
AND OLD.IDX = #IdxOld
FETCH NEXT FROM MY_CURSOR INTO #IdxId
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
Drop IDX columns
alter table OLD drop COLUMN IDX
alter table NEW drop COLUMN IDX
The aliases do not match in the update . . . that is new is not new_code. Try this:
UPDATE NC
SET SEQ_NO = OC.SEQ_NO,
CODE_ID = OC.CODE_ID
FROM NEW_CODE NC INNER JOIN
OLD_CODE OC
ON NC.MASTER_ID = OC.MASTER_ID LEFT JOIN
CODE_MAPPING CM
ON OC.CODE_ID = CM.CODE_ID
WHERE NC.CODE = 1234 AND CM.CODE_ID IS NULL;

update each row in a procedure

I have the following data in a table TABLE1
DOCUMENT ------ FIELD1
12345
23456
34567
45678
98765
i have the following data in a view VIEW1
DOCUMENT ---- BUS
12345 ------------ 5
23456 ------------ 6
34567 ------------ 8
45678 ------------ 12
98765 ------------ 14
What i would like to do is update each row
if (table1.document = view1.document)
then
table1.field1 = view1.bus
Any insight will help.
Thank you.
That can be done using plain SQL, no procedures required:
UPDATE table1 SET field1 = (SELECT bus FROM view1 WHERE table1.document = view1.document)
Or, if your database allows it:
UPDATE (select table1.field1, view1.bus FROM table1 JOIN view1 ON table1.document = view1.document) SET table1.field1 = view1.bus
As Dan said, but in MS SQL Server I find this styling easier to read:
UPDATE U
SET U.field1 = V.bus
FROM table1 AS U
JOIN view1 AS V
ON V.document = U.document
Note that if VIEW1 could have multiple rows for a given TABLE1 row [DOCUMENT] value then the [BUS] value choosen to update TABLE1 will be random, within the matching set. (If this is the case the query could be modified to choose MAX / MIN / etc.)
I would refine this query to NOT update any rows that already matched the BUS value, which will make it faster if it is rerun and thus some values already exist in TABLE1
UPDATE U
SET U.field1 = V.bus
FROM table1 AS U
JOIN view1 AS V
ON V.document = U.document
WHERE U.field1 = V.bus
OR (U.field1 IS NOT NULL AND V.bus IS NULL)
OR (U.field1 IS NULL AND V.bus IS NOT NULL)
you can leave out the NULL / NOT NULL tests if the field is defined as not allowing NULLs.