SQL Update using value from join table - sql

I tried using this sql to update a new column in a table from a value in table that already exists.
update "PROMOTION" t1
set "OFFER_CHAIN_ID" = poc."OFFER_CHAIN_ID"
from "PROMOTION_OFFER_CHAIN" poc
inner join "PROMOTION" on "PROMOTION"."ID" = poc."PROMOTION_ID"
What happened is that the first value of the join got replicated in all the subsequent entries. about a both tables. The original table has unique values all the values in the updated column are the same.
Eventually I used this SQL instead.
update "PROMOTION" t1
set "OFFER_CHAIN_ID" = poc."OFFER_CHAIN_ID"
from "PROMOTION_OFFER_CHAIN" poc
where
t1."ID" = poc."PROMOTION_ID"
This update works and duplicates all the data, 1000 unique elements in the original table, 1000 unique elements in the updated table.
Is this a bug, or is this the expected result?

SQL is behaving correctly. Your original query is:
update "PROMOTION" t1
--------^
set "OFFER_CHAIN_ID" = poc."OFFER_CHAIN_ID"
from "PROMOTION_OFFER_CHAIN" poc inner join
"PROMOTION"
-----------^
on "PROMOTION"."ID" = poc."PROMOTION_ID"
Note that the table PROMOTION is mentioned twice. Not good. So, the join takes place, producing lots of rows. Then there is no correlation to the t1 version of the table.
You don't mention the database you are using. In SQL Server, you would just do:
update p
set "OFFER_CHAIN_ID" = poc."OFFER_CHAIN_ID"
from "PROMOTION_OFFER_CHAIN" poc inner join
"PROMOTION" p
on p."ID" = poc."PROMOTION_ID";
Note the alias is used after the update (or table name with if there is no alias). Now the table is mentioned only once, so the update should behave as desired.

Related

Access Update on Query Table

I'm currently in the process of converting an access database to SQL. While going through it, I found this.
Update EmployeeCustomerOrderDetail set Valid = -1;
This is strange because EmployeeCustomerOrderDetail is a query, defined below.
Select *
From Employee
inner join Order on Employee.EmployeeID = Order.EmployeeID
inner join Customer on Order.CustomerID = Customer.CustomerID
inner join OrderDetail on Order.OrderID = OrderDetail.OrderID
I thought that this achieved nothing since an Update on a Select wouldn't work, but I tested it with
Update (Select * From Employees) as Emp set Emp.WorkPhone as 'Random Value'
And it worked.
My question then, is how is this processed. Does the update traverse backwards through all query tables until it finds a dataTable? Will it change the value of a reference column, or the actual data in the associated table?
This only works with updateable recordsets. It's the same as just changing a value directly in the Access query.
In an updateable recordsets, each field in the dataset (query) is directly bound to a field in a table. This means updates directly affect the underlying tables of a query. You can review this answer for rules about which recordsets are and aren't updateable.
Another really contra-intuitive thing about this is that recordsets with outer joins can be contain updateable unmatched records. In this case, a record is created in the table that doesn't contain a matched record, then a new one gets created.

SQL Server - UPDATE data based on SELECT

I have written the following which returns a list of Buildings that have only one room, but the area of that room (fma0.area) is not equal to the area of the building (fmb0.nia)
select
rtrim(fma0.bldgcode) As bldgcode
from fma0
left join fmb0 on fma0.bldgcode = fmb0.bldgcode
where fma0.bldgcode in (
select fma0.bldgcode
from fma0
left join fmb0 on fma0.bldgcode = fmb0.bldgcode
where fmb0.bldgstatus = ''
group by fma0.bldgcode
having count(fma0.auto_key) = 1
)
and round(fma0.area,0) <> fmb0.nia
and fmb0.nia > 0
order by 1
I need to use this list of buildings to UPDATE several fields in the FMA0 table (FMA0.GROSS, FMA0.AREA, FMA0.RENTABLE) for each BLDGCODE with the value from FMB0.NIA for the same BLDGCODE
How do I convert this into an UPDATE statement that looks up the FMB0.NIA value for each BLDGCODE and updates the value in each field for the same BLDGCODE in the FMA0 table
Thanks
This seems like a much simpler way to get the buildings that you want:
select b.bldcode
from fmbo b join
(select r.bldgcode, max(r.area) as room_area
from fma0 r
group by r.bldgcode
having count(*) = 1
) r
on r.bldgcode = b.bldgcode and r.room_area <> b.nia;
The first subquery gets the area of rooms in buildings that have only one room. The join then simply combines them according to your rules.
This is readily turned into an update:
update b
set . . .
from fmbo b join
(select r.bldgcode, max(r.area) as room_area
from fma0 r
group by r.bldgcode
having count(*) = 1
) r
on r.bldgcode = b.bldgcode and r.room_area <> b.nia;
Under most circumstances, SQL updates are performed using direct references to a particular table (UPDATE books SET books.title = 'The Hobbit' WHERE books.id = 1). Yet, on occasion, it may prove beneficial to alter the contents of a table indirectly, by using a subset of data obtained from secondary query statement.
Performing an UPDATE using a secondary SELECT statement can be accomplished in one of two ways, primarily depending upon which version of SQL Server you are using. We’ll briefly explore both options so you can find what works best for you.
Using INNER JOINS
For all SQL Server installations, the most basic method of performing this action is to use an INNER JOIN, whereby values in the columns of two different tables are compared to one another.
UPDATE
books
SET
books.primary_author = authors.name
FROM
books
INNER JOIN
authors
ON
books.author_id = authors.id
WHERE
books.title = 'The Hobbit'
In the above example, we’re UPDATING the books.primary_author field to match the authors.name for ‘The Hobbit’ by JOINING both tables in the query to their respective, matching values of authors.id and books.author_id.
Using MERGE to UPDATE and INSERT Simultaneously
For SQL Server 2008 and newer, Microsoft introduced the exceptionally useful MERGE operation which is similar to the above INNER JOIN method, but MERGE attempts to perform both an UPDATE and an INSERT command together. This effectively synchronizes the two tables based on the query performed, updating and inserting records as necessary for the two to match.
MERGE INTO
books
USING
authors
ON
books.author_id = authors.id
WHEN MATCHED THEN
UPDATE SET
books.primary_author = authors.name
WHEN NOT MATCHED THEN
INSERT
(books.author_id, books.primary_author)
VALUES
(authors.id, authors.name)
The full query when using MERGE is certainly a bit more complex then that of a basic INNER JOIN, but once you grasp how the operation functions, you’ll quickly understand how powerful this capability can truly be.
The first few lines are rather self-explanatory:
MERGE INTO
books
USING
authors
ON
books.author_id = authors.id
We want to MERGE INTO (UPDATE/INSERT) the books table by using the secondary authors table, and we’re matching the two based on the same books.author_id = authors.id comparison.
Where the MERGE command differs is in the branching logic that follows.
WHEN MATCHED THEN
UPDATE SET
books.primary_author = authors.name
Here we’re asking SQL to perform an action only when records MATCHED – when an existing record is found. In that case, we perform a standard UPDATE just as we did before, setting the books.primary_author field to equal the authors.name field.
Finally, if the query discovers a matching comparative record that doesn’t exist, we instead perform an INSERT.
WHEN NOT MATCHED THEN
INSERT
(books.author_id, books.primary_author)
VALUES
(authors.id, authors.name)
Here we’re simply asking SQL to INSERT a new record into the books table and passing along the values for the author_id and primary_author fields, grabbed from the associated authors table record.
The end result of our MERGE statement is that for every author in the authors table, we verify whether a corresponding book exists in books. If a record is found, we ensure books.primary_author is set using UPDATE, and where no match is found, we add a new record to books.
With that, you should have a solid understanding of two different methods that can be used to UPDATE records in SQL by using secondary, comparative SELECT statements.

How to copy lookup_id from Table1 to Table2 with INSERT INTO SELECT

I am a student learning SQL Server and using the management studio to normalize a db which started as a single table.
I now have Table1 with 80,000 rows containing ID, CategoryDescription, etc... with many repeated CateogoryDescriptions.
Table2 has a list of all of the CategoryDescriptions and a DescriptionID column which was created using SELECT DISTINCT. It has about 100 rows.
I want to copy the DescriptionID values from Table2 into Table 1 so that I can delete the large CategoryDescription column and replace it with a link to the lookup table.
The following generates the expected data (a single column of 80,000 ids):
SELECT TEST.dbo.LU_ConNames.Con_ID
FROM TEST.dbo.LU_ConNames
JOIN TEST.dbo.MainTable
ON TEST.dbo.MainTable.CONCESSION = TEST.dbo.LU_ConNames.Con_Name
However, when I add the INSERT INTO...
INSERT INTO TEST.dbo.MainTable
SELECT TEST.dbo.LU_ConNames.Con_ID
FROM TEST.dbo.LU_ConNames
JOIN TEST.dbo.MainTable
ON TEST.dbo.MainTable.CONCESSION = TEST.dbo.LU_ConNames.Con_Name
I get "Column name or number of supplied values does not match table definition." To clarify, there is no column in MainTable called Con_ID. I thought that perhaps that was the problem, but when I added one (and verified the same data type) I get the same error.
You should not be inserting new records as you want to update existing ones.
You can do:
UPDATE TEST.dbo.MainTable
SET TEST.dbo.MainTable.Con_ID = C.Con_ID
FROM TEST.dbo.MainTable T
INNER JOIN TEST.dbo.LU_ConNames C
ON T.CONCESSION = C.Con_Name
Some reading on that topicon MSDN - UPDATE syntax

Oracle SQL - How do I update from an Outer Joined Table?

The Problem
I need to write an Update query where my SET references an outer joined table.
I can do this fairly easily with SQL Server but I'm having a heck of a time figuring out the syntax in Oracle as I'm only allow a single table in an update query.
I have written the following Oracle query:
UPDATE SalesExt_tmp tmp
SET slsrep = (SELECT three_dig_rep
FROM dw_sls_rep_conv sls
WHERE sls.aims_rep = tmp.slsrep)
WHERE EXISTS (SELECT three_dig_rep
FROM dw_sls_rep_conv sls
WHERE sls.aims_rep = tmp.slsrep)
AND tmp.sysind = 'AIM';
This takes care of the intersection but I need to deal with values in SalesExt_tmp that do not have equivalent matches in dw_sls_rep_conv (I plan to add a case statement to set null values to a default value). To do this I need to set up dw_sls_rep_conv as an outer joined table. But this is where I get stuck.
SQL Server Example
In SQL Server the solution is a piece of cake as you can have multiple tables in an Update Query:
UPDATE SalesExt_tmp tmp
LEFT JOIN dw_sls_rep_conv sls ON sls.aims_rep = tmp.slsrep
SET tmp.slsrep = sls.three_dig_rep
WHERE tmp.sysind = 'AIM';
But I can't for the life of me figure out how to do this in Oracle. I understand that this query will allow my slsrep field to be set to NULL in some occasions which causes me to fear that this operation may not be allowed.
Questions
1) Firstly is this possible in Oracle? (It's got to be, right?)
2) How do I need to restructure my query to pull this off? I'm guessing my WHERE EXISTS clause needs to go... but I'm still stuck as to where to place my JOIN.
If I understood you correctly, you want to set the value to NULL if there is no match in the dw_sls_rep_conv table? If so, just drop your EXISTS condition and all the rows will be updated:
UPDATE SalesExt_tmp tmp
SET slsrep = (SELECT three_dig_rep
FROM dw_sls_rep_conv sls
WHERE sls.aims_rep = tmp.slsrep)
WHERE tmp.sysind = 'AIM';
If there is a match in the dw_sls_rep_conv table, then the slsrep column will be updated with selected value, otherwise, with NULL.
Have you considered using a subquery in your where clause that performs the outer join as you stated like this:
UPDATE SalesExt_tmp tmp
SET slsrep = (SELECT three_dig_rep
FROM dw_sls_rep_conv sls WHERE sls.aims_rep = tmp.slsrep)
WHERE
tmp.rowid in
(SELECT tmp1.rowid
FROM SalesExt_tmp tmp1,
dw_sls_rep_conv sls
WHERE
tmp1.slsrep = sls.aims_rep (+)
AND tmp1.sysind = 'AIM' );

Adding new fields to an existing table, inserting data into proper position, then joining

Scenario One
I have two new fields that I want to add to a table called existingTable. After I add these fields, I can update SOME but NOT ALL records with data for those fields. There will be blank entries, and I am fine with this.
Problem One
I want to make sure that the CORRECT records are updated. The primary key for the existing table and the incoming data table is Email.
Proposed Solution One
An UPDATE query looking like this is the solution.
UPDATE existingTable
SET existingTable.newField1 = incomingDataTable.newField1, existingTable.newField2 = incomingDataTable.newField2
WHERE existingTable.Email = incomingDataTable.Email
What do you think?
Scenario Two
After the table is updated with the new fields & data in the proper records, I want to join this table with two other ones. I want ALL entries, even if some fields are blank, to be in this join. I don't want ANY records excluded.
By the way, each record in these tables has a 1-to-1 relationship with its partner in the other tables. There SHOULD NOT BE ANY duplicate records. In the past, I've seen Access use an INNER JOIN, which excludes records that do not have values for newField1 and newField2. This is not what I want.
Problem
I'm inexperienced at joining tables. The different joins are a bit confusing to me.
Proposed Solution
Does the join I use necessarily matter since the three to-be-joined tables should have a one-to-one relationship?
SELECT * FROM existingTable
FULL JOIN tableToJoinWith1, tableToJoinWith2
On existingTable.Email = tableToJoinWith1.Email, tableToJoinWith1.Email = tableToJoiNWith2.Email
Clarifying your Scenario 2. I'm assuming you mean you want all the rows from existingTable even if there is no match on the Email field with either of the other tables. In this case, a LEFT JOIN is what you want:
SELECT * FROM existingTable
LEFT JOIN tableToJoinWith1 ON existingTable.email = tableToJoinWith1.email
LEFT JOIN tableToJoinWith2 ON existingTable.email = tableToJoinWith2.email
For scenario 1, the problem is that you haven't given it any sort of SELECT for incomingDataTable. In standard SQL, to my knowledge, there's no nice way to do this that supports multiple columns. So it depends what database you're using. Some will let you do this:
UPDATE existingTable
SET newField1 = incomingDataTable.newField1, newField2 = incomingDataTable.newField2
FROM incomingDataTable
WHERE existingTable.Email = incomingDataTable.Email
But some won't. Others will allow this:
UPDATE (Select * FROM existingTable JOIN incomingDataTable
ON existingTable.Email = incomingDataTable.Email)
SET existingTable.newField1 = incomingDataTable.newField1,
existingTable.newField2 = incomingDataTable.newField2
If it were only a single column, you could do this which is totally standard:
UPDATE existingTable SET newField1 = (SELECT newField1 FROM incomingDataTable
WHERE existingTable.Email = incomingDataTable.Email)