Why won't this merge statement work? - sql

I've spent the better part of the day trying to determine why a merge statement won't work and I'm starting to think the problem must be something a bit exotic.
My database has dozens of PL/SQL procedures that use merge statements but I absolutely cannot get one in particular to work. Although it's much larger than the example shown, I've stripped it down so that it only updates a couple of columns and it still will not compile.
The error is 'ORA-00904 "alias"."column_name" invalid identifier'. This typically means that a column name was mistyped or, in the case of a merge, you are attempting to update a field that's used in a join. This is definately NOT the case. I've quadrupled-checked and the column names are right, they all exist and the format of the statement is exactly the same as what I'm using in many other place.
/**
Result: ORA-00904 "P"."SFDC_CUST_CONTACT_PK": invalid identifier
I'm certain that the table and column names are all correct.
If I join on any of the dozen or so other columns instead, I
get the exact same error.
Note: I'm NOT attempting to update the column that I join
against.
**/
merge into customer_contact c
using (select p.fax_number,
p.email
from sfdc_cust_contact_temp p
) p
on (p.sfdc_cust_contact_pk = c.sfdc_cust_contact_pk)
when matched then
update set
c.fax_number = p.fax_number,
c.email = p.email;
/***
This works fine on the same machine
**/
merge into customer_contact_legacy c
using (select ct.contact_legacy_pk,
ct.fax_number,
ct.email
from customer_contact_temp ct
) ct
on (upper(trim(ct.contact_legacy_pk)) = upper(trim(c.contact_legacy_pk)))
when matched then
update set
c.fax_number = ct.fax_number,
c.email = ct.email;
Any ideas what else could be wrong here? Could there be some type of corruption with the table?
The version is 10g.

It looks like your using clause is missing the column you're trying to join on.
Your code:
merge into customer_contact c
using (select p.fax_number,
p.email
from sfdc_cust_contact_temp p
) p
on (p.sfdc_cust_contact_pk = c.sfdc_cust_contact_pk)
Potential fix:
merge into customer_contact c
using (select p.sfdc_cust_contact_pk,
p.fax_number,
p.email
from sfdc_cust_contact_temp p
) p
on (p.sfdc_cust_contact_pk = c.sfdc_cust_contact_pk)

Related

Arel UpdateManager with Join creates invalid SQL - how to rephrase?

Apparently there is an issue in Arel core, where Arel::UpdateManager, when performing a column update on a join, does not generate the table name for the update column. It results in invalid SQL.
I ran into this in a Rails 5.2 app, where I had an SQL literal UPDATE statement that I was trying to rephrase in Arel.
UPDATE observations o, names n
SET o.lifeform = n.lifeform
WHERE o.name_id = n.id
AND o.lifeform != n.lifeform
In Arel, i wrote this:
names = Name.arel_table
obs = Observation.arel_table
join_source = Arel::Nodes::JoinSource.new(
obs, [obs.create_join(names)]
)
Arel::UpdateManager.new.
table(join_source).
where(obs[:id].eq(names[:id]).
and(obs[:lifeform].not_eq(names[:lifeform]))).
set([[obs[:lifeform], names[:lifeform]]])
This returns:
Mysql2::Error: Column 'lifeform' in field list is ambiguous:
The problem is at the end. The SQL generated from this does not specify the table where the column is to be set.
UPDATE `observations`
INNER JOIN `names`
SET `lifeform` = `names`.`lifeform`
WHERE (`observations`.`id` = `names`.`id`)
AND (`observations`.`lifeform` != `names`.`lifeform`)
Elsewhere, Arel-generated SQL usually qualifies columns with table names to avoid ambiguity. But the source code for update_manager.rb definitely uses Nodes::UnqualifiedColumn.new(column). (I have added my description to the Arel issue on GitHub.)
For now I'd maybe like to rephrase my Arel some other way. Is there a way to force Arel to quote the table name, similar to connection.quote_table_name?
Or would using a CTE be appropriate?
I guess one way to do this is with ActiveRecord's connection.update_all.
names = Arel::Table.new(:names)
Observation.joins(:name).
 where(names[:correct_spelling_id].not_eq(nil)).
  update_all("`observations`.`name_id` = `names`.`correct_spelling_id`")
This generates the desired SQL:
UPDATE `observations`
INNER JOIN `names`
ON (`observations`.`name_id` = `names`.`correct_spelling_id`)
AND (`names`.`correct_spelling_id` IS NOT NULL)
SET `observations`.`name_id` = `names`.`correct_spelling_id`
I think this is the way to go.

UPDATE with EXISTS

I'm testing this UPDATE statement to update all 4%, 8%, and 9% parts in our database. I'm trying to get the QTY_MULTIPLE value to match the CASES per layer value.
So, the REGEXP_LIKE, will eventually read:
> Regexp_like ( sp.part_no, '^4|^8|^9' )
It doesn't right now because I'm testing three specific parts. I want to make sure the rest of the statement works the way that it's supposed to.
Here's what I'm testing with:
UPDATE SALES_PART_TAB sp
SET sp.qty_multiple = ( SELECT cases_per_layer
FROM HH_INV_PART_CHARS
WHERE sp.part_no = HH_INV_PART_CHARS.part_no AND
sp.contract = HH_INV_PART_CHARS.contract )
WHERE Regexp_like ( sp.part_no, '^410-0017|^816-0210|^921-0003' ) AND
EXISTS
( SELECT sp.contract,
sp.part_no,
sp.qty_multiple,
HH_INV_PART_CHARS.cases_per_layer
FROM SALES_PART sp
inner join HH_INV_PART_CHARS
ON sp.part_no = HH_INV_PART_CHARS.part_no AND
sp.contract = HH_INV_PART_CHARS.contract
WHERE sp.qty_multiple != HH_INV_PART_CHARS.cases_per_layer );
When I run this statement, it updates 16 rows.
However, I'm expecting it to update 15 rows. I reached this conclusion by running the following SELECT statement:
SELECT sp.contract,
sp.catalog_no,
sp.qty_multiple,
HH_INV_PART_CHARS.cases_per_layer
FROM SALES_PART sp
inner join HH_INV_PART_CHARS
ON sp.part_no = HH_INV_PART_CHARS.part_no AND
sp.contract = HH_INV_PART_CHARS.contract
WHERE sp.qty_multiple != HH_INV_PART_CHARS.cases_per_layer AND
Regexp_like ( sp.part_no, '^410-0017|^816-0210|^921-0003' )
I think the problem I'm having is the UPDATE statement is updating all rows where the part_no and contract from the sales_part table exist on HH_INV_PART_CHARS. It's not limiting the update to part where the qty_multiple isn't equal to the cases_per_layer (which is what I want).
I'm a little stumped right now. I've been trying to work on both the subqueries but I'm not having any luck identifying where the problem is.
The Regexp_like ( sp.part_no,...) in the update query refers to SALES_PART_TAB.spart_no, while in the second query it refers to SALES_PART.spart_no.
One of the causes of the fog is that you redefine the alias sp in the same query, and so the exists subquery does not relate in any way to the record that is being updated. This means that if you would throw away the exists condition, you would still update 16 records. It seems very unlikely that this is what you want.
Use a different alias, so you can distinguish which table you want to refer to.

Using a select statement as criteria for an update query

I'm trying to put together a query that updates a field within a table. I'm attempting to run a sub select query that gives me a number, and then use that number that resulted from the sub-query as part of the criteria for the update query.
USE EMMS
Update [2_import_VZW_tbl_SMTN]
set [2_import_VZW_tbl_SMTN].[Client_ID] =[tbl_Foundation_Account].[Client_ID]
where ([tbl_Foundation_Account].[Foundation_Account_ID] =
(Select TOP 1 tbl_Foundation_Account.Foundation_Account_ID
FROM tbl_Foundation_Account
INNER JOIN [2_Import_tbl_AWCDSU]
ON tbl_Foundation_Account.Foundation_Account_ID =
[2_Import_tbl_AWCDSU].[ECPD Profile ID]))
My issue is I keep receiving this error
The multi-part identifier
tbl_Foundation_Account.Foundation_Account_ID" could not be bound.
Am I using the sub-query incorrectly? When I've received this error before, it's been because of some ambiguity in the table or field names, but this time I've checked for all that and it should be fine. Can anyone explain what SQL sin I have committed?
On the error
The multi-part identifier
tbl_Foundation_Account.Foundation_Account_ID" could not be bound.
This is because the table column [tbl_Foundation_Account].[Client_ID] does not exists in the scope of outer UPDATEquery .
The only table the outer query has an inkling about is [2_import_VZW_tbl_SMTN] and it does not have a column like [tbl_Foundation_Account].[Client_ID].
It is akin to writing a column name with a typo or like you said
When I've received this error before, it's been because of some
ambiguity in the table or field names
Please try a query like below.
Note that I am using Inner query syntax and ensuring that a single value is returned by using
select top 1 [Client_ID]
in the inner query. rest of the query syntax is same.
USE EMMS
Update [2_import_VZW_tbl_SMTN]
set [2_import_VZW_tbl_SMTN].[Client_ID] =
(
select top 1 [Client_ID]
from [tbl_Foundation_Account]
where [Foundation_Account_ID] =
(
Select TOP 1 a.Foundation_Account_ID
FROM tbl_Foundation_Account a
INNER JOIN [2_Import_tbl_AWCDSU] b
ON a.Foundation_Account_ID = b.[ECPD Profile ID]
)
)
Another poster submitted this answer earlier, but then deleted it. I was able to try it before they deleted it and it works exactly how I needed it to work. I will use this as the right answer unless someone else can tell me why this is a bad Idea.
USE EMMS
Update [2_import_VZW_tbl_SMTN]
set [2_import_VZW_tbl_SMTN].[Client_ID] = [tbl_Foundation_Account].[Client_ID]
from [tbl_Foundation_Account]
where ([tbl_Foundation_Account].[Foundation_Account_ID] =
(Select TOP 1 tbl_Foundation_Account.Foundation_Account_ID
FROM tbl_Foundation_Account
INNER JOIN [2_Import_tbl_AWCDSU]
ON tbl_Foundation_Account.Foundation_Account_ID = [2_Import_tbl_AWCDSU].[ECPD Profile ID]))

how to use another table\query inside an update query

I tried to make an UPDATE query using data from another table
which in my case was a result of a query and it looks like this:
UPDATE CalculatedQueryINNER JOIN Orders
ON CalculatedQuery.orderid = Orders.OrderID
SET Orders.TotalPrice = [CalculatedQuery].[calculated];
But it's not working, it shows an error must use an updateable query
But i went and copied the results from the query to a table named temp and i did the same and it worked!!
UPDATE temp INNER JOIN Orders
ON temp.orderid = Orders.OrderID
SET Orders.TotalPrice = [temp].[calculated];
Can anyone please provide me with a solution on how to use the query as a table
please?
EDIT: I tried to treat a query as if it was a table by itself, that was my issue, joining tables is not an issue in update queries inaccess
I read a lot through different sites
The problem that I introduced is unsolvable in access.
There is an issue with the update queries since they are not standart in access.
The problem is That in access you can't use another sql query inside an update statement because as i read any queries that use group by or join or select within select etc.. Are marked as unupdateable queries and you can't use them.
As i read i found a few good solutions using a temporary table like i did
Or using dlookup() function.
Keep in mind that you can also perform DELETE and UPDATE statements with a 'FROM', which will help in writing the query in a syntax that is more familiar. Given that you are running two very different update statements above, I've re-written both of them here. Also, given that you're using an Inner Join on both statements, which performs an intersect of data, I've rearranged the tables in the order of update.
UPDATE O
SET [TotalPrice] = [T].[calculated]
FROM [Orders] AS O
INNER JOIN [temp] AS T
ON [T].[orderid] = [O].[OrderID];
UPDATE O
SET [TotalPrice] = [CQ].[calculated]
FROM [Orders] as O
INNER JOIN [CalculatedQuery] as CQ
ON [CQ].[orderid] = [O].[OrderID];
Setting up your Updates (and likewise Deletes) in this syntax is pretty slick, as it allows for another nice feature: running a select to see what data will be changing:
SELECT [O].[TotalPrice]
, [T].[calculated]
FROM [Orders] AS O
INNER JOIN [temp] AS T
ON [T].[orderid] = [O].[OrderID];
SELECT [O].[TotalPrice],
, [CQ].[calculated]
FROM [Orders] as O
INNER JOIN [CalculatedQuery] as CQ
ON [CQ].[orderid] = [O].[OrderID];

ambiguous column name, my WHERE statement is already taken up for other purposes

So i have this problem where i need an INNER JOIN to connect my imagetable to my product table, and i've already tried to specify each coloumn name with a connection to the tables as you can see below. When it comes to my WHERE statement, thats where it gets tricky. Since i need to save my ProductID as a variable called pid for other codebehind purposes.
SELECT * FROM ProductTBL p
INNER JOIN ImageTBL img on p.ProductID = img.ProductID
WHERE (ProductID = #pid)
When i run this code it will tell me ProductID is an ambiguous column name and when i try
to specify my Where statement even further like this:
WHERE (p.ProductID = #pid)
It will give me another error telling me that no data is bound to my certain rows from which i'm taken out data from. This error is only there when i don't specify the Where statement like the one above.
Any input at all will be greatly appreciated :)
PS: im running MSSQL if that has anything to say.
I know two ways of doing this. The 2nd way is probably the recommended way (it should run quicker).
Way 1)
Run it in a two step statement. I don't think you can use the * in the inner SELECT statement:
SELECT * FROM (
SELECT p.ProductID, [other needed fields]
FROM ProductTBL p
INNER JOIN ImageTBL img on p.ProductID = img.ProductID
) as x
WHERE (ProductID = #pid)
The inner SELECT p.ProductID says to explicitly return the ProductID from the ProductTBL. That ProductID field can then be used in your WHERE statement because it's in the outer SQL statement.
Way 2)
Put the "WHERE" criteria as part of the join
SELECT *
FROM ProductTBL p
INNER JOIN ImageTBL img on p.ProductID = img.ProductID AND p.ProductID = #pid
The "ambiguous column name" message is a SQL Server error, and you fixed it by specifying "WHERE p.ProductID = #pid". Yes, you need the p. in front of ProductID, because ImageTBL also has a column with the same name.
The "no data is bound" error is not coming from SQL. You now have a correct SQL statement, but it is apparently returning no records, at least for the value of #pid that you are running it with.
You are calling this from an application that is trying to do something with the data, right? That is where the "no data is bound" error is coming from.
You can separate the two parts of your problem by running your SQL statements in a SQL Server Management Studio query window, and plugging in your desired value for #pid. Then you can run your application in debug mode, and check whether it is receiving the same data you see in Management Studio. Here's one guess: your application is not passing #pid correctly, and the SQL statement is returning records where p.ProductID = ''. See if that's what's going on.
It can be an issue with your code. Try to run the query with hard core value of product ID and run it in the database console.
It doesn't appear to be a database issue
Ok, well its the where statment that has the problem. You need to prefix your productID with your table alias. Table alias' are good, I've no doubt you have realised that when you type SELECT * From MyTable T, and then go back to choose your columns, you get an intellisnce list of columns when you replace * by T. Aliasing tables is always good. Say you have two tables T1 and T2 and Columns C1, C2, C3 in T1 and C4, C5, C6 in T2. You script your stored procedure and it works. Sometime later somone comes along and adds c column named C2 to T2, then your procedure will probably break if you have not prfixed the table name on the columns. So even if there is no current ambiguity and you never change your stored proc, using prefixed is always more robust.
Its always a good idea to explicitly state you columns rather than using *, because once again adding columns can break your proc and also if you have a column name incorrect, SSMS will underline it with a little red squigle.